我正在尝试用scala创建一个连接四个游戏。目前我拥有它所以它每次打印板,并在玩家之间切换,要求他们输入他们想要的列。问题是我不知道如何更改行。所有64次移动都停留在第7行(第1行)。我正在考虑以某种方式进行检查,检查用户想要播放的地点是否已经存在X或O,并且只是抬起该行。我会用这个吗?因此,如果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
}
答案 0 :(得分:1)
我不确定你是否仍对我的评论感到困惑,但让我试着在这里给你一个更深入的解释。
有问题的代码是:
//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
}
玩家轮流使用。但在直接回答之前,让我们通过删除我们在这里的重复来重构这段代码。 player1
和player2
几乎是相同的实现。因此,让我们将不同的部分作为参数传递。不同的部分是播放器名称以及表示此播放器的符号。因此,让我们定义一个类Player
:
case class Player(name: String, symbol: Char)
并将两个函数合并为一个:
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'))
}
现在,我们不需要做两次,但问题仍然是一样的。
好的,我们假设我们将使用条件:如果table(7)(move)
被占用,那么我们选择table(6)(move)
。但是,如果这也被占用,我们选择table(5)(move)
。这种情况一直持续到我们发现列完全填满为止,在这种情况下我们可能会想要抛出异常。在代码中,这将如下所示:
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
}
让我们运行代码并且...... 是的,它有效!。但它是可怕的代码。有很多重复,我们不能轻易地使表更大。
好的,我们真的想解决什么问题?我们希望在move
找到具有可用空间的行的最高索引,即在'.'
处包含move
。
我们怎么能找到这个指数?存在一个函数indexOf
,它接受参数x
并返回数组中第一次出现x
的索引。但是我们的数组table
是一个二维数组,我们只关心每个内部数组中move
的值。幸运的是,Scala中的每个集合都提供了一个函数map
来映射每个元素,因此我们可以这样做:table map (_(move))
。
假设我们收到以下数组:. . O X O O 2
,因此最后一次出现.
的索引为1.但indexOf
将返回第一个索引,因此{{1}我们可以反转数组,因为查找反向数组中的第一个索引等同于查找数组中的最后一个索引,但这有点棘手,因为我们还需要反转索引,因为反转数组中的索引通常与原始数组中的索引不同。
让我们应用一个小技巧:让我们找到第一个不是indexOf('.')
的元素的索引,而不是找到.
的最后一个索引。但是函数.
不允许我们传递indexOf
。但是,我们可以通过稍微修改我们的map函数来解决这个问题:让我们映射到not x
,而不是table map (_(move))
。现在,我们需要找到第一个table map (_(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
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
,让它们提供添加元素的功能,并打印一个适当的Column
桌子。
toString