Scala连接四行之间的移动

Scala连接四行之间的移动,scala,Scala,我正在尝试在scala制作一个连接四的游戏。目前我有它,所以它每次都会打印棋盘,并在玩家之间切换,要求他们输入他们想要的列。问题是我不知道如何更改行。所有64个动作都保留在第7行(第1行)。我想做一个检查,检查用户想玩的地方是否已经有一个X或O,然后把这一行撞上去。我会用if-else吗?所以,如果有x或o,向上移动一行,否则就移动 // Initialize the grid val table = Array.fill(9,8)('.') var i = 0; while(i <

我正在尝试在scala制作一个连接四的游戏。目前我有它,所以它每次都会打印棋盘,并在玩家之间切换,要求他们输入他们想要的列。问题是我不知道如何更改行。所有64个动作都保留在第7行(第1行)。我想做一个检查,检查用户想玩的地方是否已经有一个X或O,然后把这一行撞上去。我会用if-else吗?所以,如果有x或o,向上移动一行,否则就移动

// Initialize the grid 
val table = Array.fill(9,8)('.') 
var i = 0; 
while(i < 8){ 
table(8)(i) = (i+'0').toChar 
i = i+1;
}

/* printGrid: Print out the grid provided */
def printGrid(table: Array[Array[Char]]) { 
table.foreach( x => println(x.mkString(" "))) 
}

var allowedMoves = 64 
var spotsLeft = 8

//Player One
def player1(){
printGrid(table) 
println("Player 1 it is your turn. Choose a column 0-7")
val move = readInt
table(7)(move) = ('X')
}

//Player Two
def player2(){
printGrid(table) 
println("Player 1 it is your turn. Choose a column 0-7")
val move = readInt
table(7)(move) = ('O')

for (turn <- 1 to 32) { 
  player 1
  player 2
}
//初始化网格
val table=Array.fill(9,8)(“.”)
var i=0;
而(i<8){
表(8)(i)=(i+'0')。toChar
i=i+1;
}
/*printGrid:打印提供的网格*/
def打印网格(表:数组[Array[Char]]){
表.foreach(x=>println(x.mkString(“”))
}
var allowedMoves=64
var spotsLeft=8
//一号玩家
def播放器1(){
打印网格(表格)
println(“玩家1轮到你了。选择一列0-7”)
val move=readInt
表(7)(move)=('X')
}
//球员二
def播放器2(){
打印网格(表格)
println(“玩家1轮到你了。选择一列0-7”)
val move=readInt
表(7)(移动)=(O)

对于(turn我不确定你是否仍然对我的评论感到困惑,但让我在这里给你一个更深入的解释

有关守则是:

//Player One
def player1(){
  printGrid(table) 
  println("Player 1 it is your turn. Choose a column 0-7")
  val move = readInt
  table(7)(move) = ('X')
}

//Player Two
def player2(){
  printGrid(table) 
  println("Player 1 it is your turn. Choose a column 0-7")
  val move = readInt
  table(7)(move) = ('O')
}

for (turn <- 1 to 32) { 
  player1
  player2
}
并将这两个功能合而为一:

def player(player: Player): Unit ={
  printGrid(table)
  println(s"${player.name} it is your turn. Choose a column 0-7")
  val move = readInt
  table(7)(move) = player.symbol
}

for (turn <- 1 to 32) {
  player(Player("Player 1", 'X'))
  player(Player("Player 2", 'O'))
}
让我们运行代码,然后…是的,它是有效的!。但这是一个糟糕的代码。有大量的重复,我们无法轻松地使表变大

好的,我们真正想解决的问题是什么?我们想找到在
移动
处有可用空间的行的最高索引,即包含
移动

我们如何找到这个索引呢?存在一个函数
indexOf
,它接受一个参数
x
,并返回数组中第一次出现的
x
的索引。但是我们的数组
是一个二维数组,我们只关心每个内部数组中
move
处的值。幸运的是,每个Scala中的集合提供了一个映射每个元素的函数
map
,因此我们可以做:
表映射(u(move))

假设我们收到以下数组:
.oxo2
,因此上次出现的
的索引是1。但是
indexOf
将返回第一个索引,因此
indexOf('.')
将返回0。我们可以反转数组,因为在反转数组中查找第一个索引相当于在数组中查找最后一个索引,但这有点棘手,因为我们还需要反转索引,因为反转数组中的索引通常与原始数组中的索引不同

让我们应用一个小技巧:我们不必查找
的最后一个索引,而是查找第一个元素的索引,该元素不是
,然后减去一个。但是
indexOf
函数不允许我们传递
而不是x
。但是,我们可以通过稍微修改map函数来解决这个问题:而不是
表映射((move))
,让我们映射到
表映射((move)='.')
。现在,我们需要找到第一个
false
值的索引并减去一个

整个解决方案如下所示:

def player(player: Player): Unit = {
  printGrid(table)
  println(s"${player.name} it is your turn. Choose a column 0-7")
  val move = readInt
  if (table(7)(move) != '.') {
    if (table(6)(move) != '.') {
      if (table(5)(move) != '.') {
        if (table(4)(move) != '.') {
          if (table(3)(move) != '.') {
            if (table(2)(move) != '.') {
              if (table(1)(move) != '.') {
                if (table(0)(move) != '.') {
                  throw new IllegalArgumentException(s"Column $move is already full")
                } else table(0)(move) = player.symbol
              } else table(1)(move) = player.symbol
            } else table(2)(move) = player.symbol
          } else table(3)(move) = player.symbol
        } else table(4)(move) = player.symbol
      } else table(5)(move) = player.symbol
    } else table(6)(move) = player.symbol
  } else table(7)(move) = player.symbol
}
def player(player: Player): Unit = {
  printGrid(table)
  println(s"${player.name} it is your turn. Choose a column 0-7")
  val move = readInt
  val freeRows = table map (_(move) == '.')
  val indexOfLastFreeRow = (freeRows indexOf false) - 1
  if (indexOfLastFreeRow == -1) throw new IllegalArgumentException(s"Column $move is already full")
  else table(indexOfLastFreeRow)(move) = player.symbol
}

for (turn <- 1 to 32) {
  player(Player("Player 1", 'X'))
  player(Player("Player 2", 'O'))
}

case class Player(name: String, symbol: Char)

您可以使用
table.map(u.apply(move)='.).indexOf(false)-1
来查找上一次出现的点的索引,假设该点是您的“空条目”符号。(不过可能还有更可读的替代方法)我建议在代码中添加更多的抽象:为什么不做一个类<代码>表<代码>,持有一系列的<代码>行< /代码>?此外,我会考虑使实现不可变,或者至少仔细考虑是否可变性使得代码更容易理解。第一层def player1(){printGrid(table)println(“玩家1”轮到你了。选择一列0-7“)val move=readInt if(table(7)(move)!=”。{table(6)(move)='X'}否则table(7)(move)='X'}//Player Two def player2(){printGrid(table)println(“玩家1”轮到你了。选择一列0-7“)val move=readInt if(table(7)(move)!”。)表(6)(move)='O'else表(7)(move)='O'}但是现在我不知道如何进一步推进它。我认为条件句在这里不是正确的选择。下一步是检查
if(表(6)!='.
,然后
if(表(5)!='.
,等等。您已经看到,这会导致大量重复,如果表可以任意大,也不会导致结果。您必须找到列中第一个空项的
索引,正如我在第一条注释中提到的。
def player(player: Player): Unit = {
  printGrid(table)
  println(s"${player.name} it is your turn. Choose a column 0-7")
  val move = readInt
  val freeRows = table map (_(move) == '.')
  val indexOfLastFreeRow = (freeRows indexOf false) - 1
  if (indexOfLastFreeRow == -1) throw new IllegalArgumentException(s"Column $move is already full")
  else table(indexOfLastFreeRow)(move) = player.symbol
}

for (turn <- 1 to 32) {
  player(Player("Player 1", 'X'))
  player(Player("Player 2", 'O'))
}

case class Player(name: String, symbol: Char)
case class Table(columns: List[Column]) {
  override def toString = (for (i <- 0 until columns.head.values.length) yield {
    columns map (_.values(i).toString) reduceLeft (_ + " " + _)
  }) reduceLeft (_ + System.lineSeparator + _)

  def add(entry: Char, columnIndex: Int): Table = {
    val (prefix, column :: suffix) = columns.splitAt(columnIndex)
    Table(prefix ++ (column.add(entry) :: suffix))
  }
}
object Table {
  val EmptyEntry = '.'

  def empty(numberOfColumns: Int, numberOfRows: Int): Table =
    Table(List.fill(numberOfColumns)(Column.empty(numberOfRows)))
}

case class Column(values: List[Char]) {

  def isFull: Boolean = !values.contains(Table.EmptyEntry)

  def add(entry: Char): Column = {
    if (isFull) this
    else {
      val (empty, filled) = values.partition(_ == Table.EmptyEntry)
      Column(empty.dropRight(1) ++ (entry :: filled))
    }
  }
}
object Column {
  def empty(numberOfRows: Int): Column =
    Column(List.fill(numberOfRows)(Table.EmptyEntry))
}