如何在井字棋盘中找到第一个获胜组合?
How to find first winning combination in Tic-Tac-Toe board?
Ruby 的新功能,请原谅糟糕的代码。我想遍历多维数组 WIN_COMBINATIONS
并检查是否至少有一个数组的所有元素都等于 'X'
或全部等于 'O'
。如果是这样,return 匹配的数组。这是通过 won?
函数完成的,但它似乎只是 return 整个多维数组。如有任何帮助,我们将不胜感激。
class TicTacToe
WIN_COMBINATIONS = [
[0,1,2], # top_row
[3,4,5], # middle_row
[6,7,8], # bottom_row
[0,3,6], # left_column
[1,4,7], # center_column
[2,5,8], # right_column
[0,4,8], # left_diagonal
[6,4,2] # right_diagonal
]
def initialize
@board = Array.new(9, " ")
end
def display_board
puts " #{@board[0]} | #{@board[1]} | #{@board[2]} "
puts "-----------"
puts " #{@board[3]} | #{@board[4]} | #{@board[5]} "
puts "-----------"
puts " #{@board[6]} | #{@board[7]} | #{@board[8]} "
end
def input_to_index(board_position)
user_input = board_position.to_i
user_input - 1
end
def move(board_index, player_token = 'X')
@board[board_index] = player_token
end
def position_taken?(board_position)
if @board[board_position] == ' '
false
else
true
end
end
def valid_move?(board_position)
if board_position >= 0 and board_position <= 8
if @board[board_position] == ' '
true
end
else
false
end
end
def current_player
turn_count % 2 == 0 ? "X" : "O"
end
def turn_count
@board.count{|token| token == "X" || token == "O"}
end
def turn
puts "Select your move (1-9)\n"
move = gets.chomp
move_index = input_to_index(move)
if valid_move?(move_index)
token = current_player
move(move_index, token)
display_board
else
puts "Select your move (1-9)\n"
move = gets.chomp
end
end
def won?
WIN_COMBINATIONS.each do |combinations|
if combinations.all? {|combination| combination == 'X' or combination == 'O'}
combinations
else
false
end
end
end
def draw?
if full? and !won?
true
elsif won?
false
else
false
end
end
def over?
end
def winner
end
def play
end
end
以下是我解决问题的方法:
class TicTacToe
class OccupiedError < StandardError; end
attr_reader :rows
def initialize
@rows = 3.times.map{ Array(3, nil) }
end
def place!(player, x:, y:)
raise ArgumentError, "player must be :x or :o" unless [:x, :o].include?(player)
raise OccupiedError, "slot is already occupied" unless @rows[y][x].nil?
@rows[y][x] = player
end
# gets an array of columns instead of rows.
def columns
(0..2).map { |n| @rows.map {|row| row[n] } }
end
def diagonals
[
[@rows[0][0], @rows[1][1], @rows[2][2]], # lrt
[@rows[0][2], @rows[1][1], @rows[2][0]] # rtl
]
end
def all_combos
rows + columns + diagonals
end
# checks all the horizontal, vertical and diagonal combinations
def check_for_winner
# checks all combos for three in a row
(all_combos.find{ |a| a.all?(:x) || a.all?(:o) })&.first
end
end
在初始化方法中,我们创建了一个 3*3 的数组,代表棋盘上的所有位置。这使得它更容易,因为它已经分组在行中。 Intead 一个空字符串使用 nil 来表示一个空方块,因为 nil 是假的。
当我们想要检查获胜者时,我们将行、列和两条对角线收集到一个数组中:
[1] pry(main)> game.rows
=> [[:o, :o, :o], [nil, :x, :x], [:x, nil, nil]]
[2] pry(main)> game.all_combos
=> [[:o, :o, :o],
[nil, :x, :x],
[:x, nil, nil],
[:o, nil, :x],
[:o, :x, nil],
[:o, :x, nil],
[:o, :x, nil],
[:o, :x, :x]]
从那里我们只需要检查它们中的任何一个是否都是 :x
或 :o
。我们实际上不必列出获胜组合。在这种情况下 game.check_for_winner
将 return :o
.
[...] it seems to only be returning the entire multidimensional array.
您尝试的解决方案存在几个问题:
WIN_COMBINATIONS
是一个索引数组。这些索引是数字,因此它们永远不会是 'X'
或 'O'
。你要检查他们对应的值是'X'
还是'O'
.
or
是用于 do_this or fail
场景的 控制流运算符 。 布尔值 "or" 运算符 是 ||
。使用 or
而不是 ||
可能 有效,但由于 precedence 较低,可能会产生意想不到的结果。你几乎总是想要 ||
.
表达式 array.all? { |element| element == 'X' || element == 'O' }
检查是否所有元素都是 'X'
或 'O'
。 ['X','O','O']
为 true
,['X',' ','O']
为 false
。那是因为你把条件放在了块中。你想要的是检查元素是否都是 'X'
,或者都是 'O'
:
array.all?('X') || array.all?('O')
您的方法的 return 值是 WIN_COMBINATIONS.each { ... }
和 Array#each
always returns the array itself (i.e. WIN_COMBINATIONS
) regardless of the blocks' result. To get the first element matching a condition, use find
的结果。
让我们将所有这些应用到您的代码中。鉴于此板:
@board = %w[
X - O
O X -
- - X
]
您可以通过以下方式获得第一个匹配的组合:
WIN_COMBINATIONS.find do |indices|
values = @board.values_at(*indices)
values.all?('X') || values.all?('O')
end
#=> [0, 4, 8]
values_at
returns 相应索引的值(*
将 indices
数组转换为参数列表,因此 values_at(*[0,1,2])
变为 values_at(0,1,2)
).该块的第 2 行然后检查这些值是全部 'X'
还是全部 'O'
。一旦计算结果为 true
,循环就会中断并且 find
return 匹配元素。 (或 nil
如果没有匹配项)
Ruby 的新功能,请原谅糟糕的代码。我想遍历多维数组 WIN_COMBINATIONS
并检查是否至少有一个数组的所有元素都等于 'X'
或全部等于 'O'
。如果是这样,return 匹配的数组。这是通过 won?
函数完成的,但它似乎只是 return 整个多维数组。如有任何帮助,我们将不胜感激。
class TicTacToe
WIN_COMBINATIONS = [
[0,1,2], # top_row
[3,4,5], # middle_row
[6,7,8], # bottom_row
[0,3,6], # left_column
[1,4,7], # center_column
[2,5,8], # right_column
[0,4,8], # left_diagonal
[6,4,2] # right_diagonal
]
def initialize
@board = Array.new(9, " ")
end
def display_board
puts " #{@board[0]} | #{@board[1]} | #{@board[2]} "
puts "-----------"
puts " #{@board[3]} | #{@board[4]} | #{@board[5]} "
puts "-----------"
puts " #{@board[6]} | #{@board[7]} | #{@board[8]} "
end
def input_to_index(board_position)
user_input = board_position.to_i
user_input - 1
end
def move(board_index, player_token = 'X')
@board[board_index] = player_token
end
def position_taken?(board_position)
if @board[board_position] == ' '
false
else
true
end
end
def valid_move?(board_position)
if board_position >= 0 and board_position <= 8
if @board[board_position] == ' '
true
end
else
false
end
end
def current_player
turn_count % 2 == 0 ? "X" : "O"
end
def turn_count
@board.count{|token| token == "X" || token == "O"}
end
def turn
puts "Select your move (1-9)\n"
move = gets.chomp
move_index = input_to_index(move)
if valid_move?(move_index)
token = current_player
move(move_index, token)
display_board
else
puts "Select your move (1-9)\n"
move = gets.chomp
end
end
def won?
WIN_COMBINATIONS.each do |combinations|
if combinations.all? {|combination| combination == 'X' or combination == 'O'}
combinations
else
false
end
end
end
def draw?
if full? and !won?
true
elsif won?
false
else
false
end
end
def over?
end
def winner
end
def play
end
end
以下是我解决问题的方法:
class TicTacToe
class OccupiedError < StandardError; end
attr_reader :rows
def initialize
@rows = 3.times.map{ Array(3, nil) }
end
def place!(player, x:, y:)
raise ArgumentError, "player must be :x or :o" unless [:x, :o].include?(player)
raise OccupiedError, "slot is already occupied" unless @rows[y][x].nil?
@rows[y][x] = player
end
# gets an array of columns instead of rows.
def columns
(0..2).map { |n| @rows.map {|row| row[n] } }
end
def diagonals
[
[@rows[0][0], @rows[1][1], @rows[2][2]], # lrt
[@rows[0][2], @rows[1][1], @rows[2][0]] # rtl
]
end
def all_combos
rows + columns + diagonals
end
# checks all the horizontal, vertical and diagonal combinations
def check_for_winner
# checks all combos for three in a row
(all_combos.find{ |a| a.all?(:x) || a.all?(:o) })&.first
end
end
在初始化方法中,我们创建了一个 3*3 的数组,代表棋盘上的所有位置。这使得它更容易,因为它已经分组在行中。 Intead 一个空字符串使用 nil 来表示一个空方块,因为 nil 是假的。
当我们想要检查获胜者时,我们将行、列和两条对角线收集到一个数组中:
[1] pry(main)> game.rows
=> [[:o, :o, :o], [nil, :x, :x], [:x, nil, nil]]
[2] pry(main)> game.all_combos
=> [[:o, :o, :o],
[nil, :x, :x],
[:x, nil, nil],
[:o, nil, :x],
[:o, :x, nil],
[:o, :x, nil],
[:o, :x, nil],
[:o, :x, :x]]
从那里我们只需要检查它们中的任何一个是否都是 :x
或 :o
。我们实际上不必列出获胜组合。在这种情况下 game.check_for_winner
将 return :o
.
[...] it seems to only be returning the entire multidimensional array.
您尝试的解决方案存在几个问题:
WIN_COMBINATIONS
是一个索引数组。这些索引是数字,因此它们永远不会是'X'
或'O'
。你要检查他们对应的值是'X'
还是'O'
.or
是用于do_this or fail
场景的 控制流运算符 。 布尔值 "or" 运算符 是||
。使用or
而不是||
可能 有效,但由于 precedence 较低,可能会产生意想不到的结果。你几乎总是想要||
.表达式
array.all? { |element| element == 'X' || element == 'O' }
检查是否所有元素都是'X'
或'O'
。['X','O','O']
为true
,['X',' ','O']
为false
。那是因为你把条件放在了块中。你想要的是检查元素是否都是'X'
,或者都是'O'
:array.all?('X') || array.all?('O')
您的方法的 return 值是
WIN_COMBINATIONS.each { ... }
和Array#each
always returns the array itself (i.e.WIN_COMBINATIONS
) regardless of the blocks' result. To get the first element matching a condition, usefind
的结果。
让我们将所有这些应用到您的代码中。鉴于此板:
@board = %w[
X - O
O X -
- - X
]
您可以通过以下方式获得第一个匹配的组合:
WIN_COMBINATIONS.find do |indices|
values = @board.values_at(*indices)
values.all?('X') || values.all?('O')
end
#=> [0, 4, 8]
values_at
returns 相应索引的值(*
将 indices
数组转换为参数列表,因此 values_at(*[0,1,2])
变为 values_at(0,1,2)
).该块的第 2 行然后检查这些值是全部 'X'
还是全部 'O'
。一旦计算结果为 true
,循环就会中断并且 find
return 匹配元素。 (或 nil
如果没有匹配项)