TicTacToe 重赢条件
TicTacToe Heavy Win Condition
我在 Ruby 做了一个井字棋游戏。我是 Ruby OOP 的新手。我检查行、列和对角线的获胜条件非常繁重,它们占用太多代码行,看起来非常糟糕。我的代码也总是重复。
所以我的问题是如何在我的代码中实现以下获胜条件?
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
]
这是我的获胜条件:
module CheckWinner
def check_horizontal(player1, player2)
player1_win = false
player2_win = false
@board.each do |row|
player1_win = row.all?(player1)
player2_win = row.all?(player2)
break if player1_win || player2_win
end
puts "#{player1} won!" if player1_win
puts "#{player2} won!" if player2_win
player1_win || player2_win
end
def check_vertical(player1, player2)
player1_win = false
player2_win = false
@board.transpose.each do |row|
player1_win = row.all?(player1)
player2_win = row.all?(player2)
break if player1_win || player2_win
end
puts "#{player1} won!" if player1_win
puts "#{player2} won!" if player2_win
player1_win || player2_win
end
def check_diagonal(player1, player2)
if @board[0][0] == player1 && board[1][1] == player1 && board[2][2] == player1 ||
@board[0][2] == player1 && board[1][1] == player1 && board[2][0] == player1
puts "#{@player1} won!"
true
elsif @board[0][0] == player2 && board[1][1] == player2 && board[2][2] == player2 ||
@board[0][2] == player2 && board[1][1] == player2 && board[2][0] == player2
puts "#{@player2} won!"
true
end
end
end
我从这行代码调用这些条件:
def game
choosing_player
loop do
player1(@player1)
break if check_vertical(@player1,@player2) == true || check_diagonal(@player1,@player2) == true || check_horizontal(@player1,@player2) == true || full?
player2(@player2)
break if check_vertical(@player1,@player2) == true || check_diagonal(@player1,@player2) == true || check_horizontal(@player1,@player2) == true || full?
end
end
end
这是我的完整项目,它可以正常工作。
module CheckWinner
def check_horizontal(player1, player2)
player1_win = false
player2_win = false
@board.each do |row|
player1_win = row.all?(player1)
player2_win = row.all?(player2)
break if player1_win || player2_win
end
puts "#{player1} won!" if player1_win
puts "#{player2} won!" if player2_win
player1_win || player2_win
end
def check_vertical(player1, player2)
player1_win = false
player2_win = false
@board.transpose.each do |row|
player1_win = row.all?(player1)
player2_win = row.all?(player2)
break if player1_win || player2_win
end
puts "#{player1} won!" if player1_win
puts "#{player2} won!" if player2_win
player1_win || player2_win
end
def check_diagonal(player1, player2)
if @board[0][0] == player1 && board[1][1] == player1 && board[2][2] == player1 ||
@board[0][2] == player1 && board[1][1] == player1 && board[2][0] == player1
puts "#{@player1} won!"
true
elsif @board[0][0] == player2 && board[1][1] == player2 && board[2][2] == player2 ||
@board[0][2] == player2 && board[1][1] == player2 && board[2][0] == player2
puts "#{@player2} won!"
true
end
end
end
# TicTacToe Board
class Board
include CheckWinner
attr_accessor :board
def initialize
@board = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
end
def print_board
puts '-------------'
@board.each do |row|
print '|'
row.each do |col|
print " #{col}"
print ' | '
end
puts
end
puts '-------------'
end
def twodimentional_board
@board = @board.each_slice(3).map { |el| el }
end
def occupied_error(value)
puts 'There is a value or wrong place! Try Again!'
twodimentional_board
print_board
value == @player1 ? player2(@player1) : player1(@player2) # Stay same player
end
def move_if_possible(place, value)
@board.flatten!
if @board[place - 1] == 'X' || @board[place - 1] == 'O' || !place.between?(1, 9)
occupied_error(value)
else
@board[place - 1] = value
twodimentional_board
@board
end
end
def full?
if @board.flatten.all?(String)
puts 'Draw!'
true
end
end
def choosing_player
puts 'Choose for Player1(X or O)'
loop do
@player1 = gets.chomp!
break if @player1 == 'X' || @player1 == 'O'
puts 'Try Again!(X or O)'
end
puts "Player 1 is: #{@player1}"
@player1 == 'X' ? @player2 = 'O' : @player2 = 'X'
puts "Player 2 is: #{@player2}"
print_board
end
def player1(player1)
puts "Choice #{player1} Place on a board(1 to 10)"
@place = gets.chomp!.to_i
move_if_possible(@place, player1)
print_board
end
def player2(player2)
puts "Choice #{player2} Place on a board(1 to 10)"
@place = gets.chomp!.to_i
move_if_possible(@place, player2)
print_board
end
def game
choosing_player
loop do
player1(@player1)
break if check_vertical(@player1,@player2) == true || check_diagonal(@player1,@player2) == true || check_horizontal(@player1,@player2) == true || full?
player2(@player2)
break if check_vertical(@player1,@player2) == true || check_diagonal(@player1,@player2) == true || check_horizontal(@player1,@player2) == true || full?
end
end
end
board = Board.new
board.game
如果要实时预览这里是repllink
我不确定这是否正是您想要的,但这里有一些东西。
- 您的
@board
是一个 3 行的数组,但您可能获胜的只是一个点数数组。 flatten
使用@board 意味着您可以按原样使用索引。
- 你已经有了一组获胜条件,然后忽略这个数组并手动完成所有选项
定义
# example winning board for 'O'
@board = [['O','X','O'], ['X','O','X'], ['X','O','O']]
player1 = 'X'
player2 = 'O'
首先,遍历 WIN_COMBINATIONS
中的可能组合,对于每个组合,从 @board
中获取这些单元格作为单独的数组,以供稍后检查。将数组数组存储为另一个数组(使用 #map
)。 *
字符是 ruby splat 运算符,它将数组转换为被调用过程的参数。
shorthand:
winmap = WIN_COMBINATIONS.map {|w| @board.flatten.values_at(*w)}
手写:
flat_board = @board.flatten
winmap = WIN_COMBINATIONS.map do |win|
flat_board.values_at(*win)
end
设置如下数组:
winmap = [
["O", "X", "O"], # top_row
["X", "O", "X"], # middle_row
["X", "O", "O"], # bottom_row
["O", "X", "X"], # left_column
["X", "O", "O"], # center_column
["O", "X", "O"], # right_column
["O", "O", "O"], # left_diagonal
["X", "O", "O"] # right_diagonal
]
一旦你有了棋盘上所有可能获胜的单元格的列表,你就可以使用你现有的 row
搜索代码,除了循环遍历每个数组,我们使用数组操作 #any?
识别匹配项
player1_win = winmap.any?{|m| m.all?(player1)}
player2_win = winmap.any?{|m| m.all?(player2)}
这应该消除所有单独的子程序,并给你一个 true/false 答案,看看是否有人赢了。
这里有一种不同的方法来确定棋盘是否表明获胜,如果是,谁获胜。
设board
为三个元素的数组,每个元素为三个元素的数组,每个元素为'X'
、'O'
或'_'
,最后一个表示单元格尚未标记。
让我们创建一些方法。
def row_win?(board)
board.each { |row| return row.first if row_uniq.size == 1 }
nil
end
这个方法returns 'X'
如果'X'连胜,'O'
如果'O'连胜,nil
如果没有玩家连续获胜。
def column_win?(board)
row_win?(board.transpose)
end
此方法类似returns'X'
、'O'
或nil
,表示某列是否有赢,如果有,谁赢
def diagonal_win?(board)
row_win? [[board[0][0], board[1][1], board[2][2]],
[board[0][2], board[1][1], board[2][0]]]
end
此方法对两条对角线执行相同的操作。
最后,
def win?(board)
row_win?(board) || column_win?(board) || diagonal_win?(board)
end
nil
无胜负返回
我们来试试吧。
win? [['X', '_', 'X'],
['_', 'X', 'O'],
['X', 'O', 'O']]
#=> 'X'
win? [['X', 'O', 'X'],
['_', 'O', 'X'],
['X', 'O', 'O']]
#=> 'O'
win? [['X', 'O', 'X'],
['O', 'O', 'X'],
['X', 'X', 'O']]
#=> nil
为什么不跟踪状态来帮助您在标记新动作时确定获胜条件?这样你就不必重新计算你已经知道的所有内容。
例如,如果我告诉你我的棋盘尺寸是 3
,第一行已经有 2
X,现在 X 在导致 3
Xs 的那一行下了一个新的棋子,你知道 X 马上就赢了吗?您甚至不需要了解有关董事会状态的任何其他信息。
您可以在此处应用相同的逻辑,只需跟踪每行的 X & O 计数,cols & diags 作为标记记录下来,您甚至不必循环查看谁赢了。
above-described 方法易于实施,并且需要 O(1)
时间复杂度来检查获胜条件。您用一点内存 (O(n)
) 来换取计算速度。
我在 Ruby 做了一个井字棋游戏。我是 Ruby OOP 的新手。我检查行、列和对角线的获胜条件非常繁重,它们占用太多代码行,看起来非常糟糕。我的代码也总是重复。
所以我的问题是如何在我的代码中实现以下获胜条件?
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
]
这是我的获胜条件:
module CheckWinner
def check_horizontal(player1, player2)
player1_win = false
player2_win = false
@board.each do |row|
player1_win = row.all?(player1)
player2_win = row.all?(player2)
break if player1_win || player2_win
end
puts "#{player1} won!" if player1_win
puts "#{player2} won!" if player2_win
player1_win || player2_win
end
def check_vertical(player1, player2)
player1_win = false
player2_win = false
@board.transpose.each do |row|
player1_win = row.all?(player1)
player2_win = row.all?(player2)
break if player1_win || player2_win
end
puts "#{player1} won!" if player1_win
puts "#{player2} won!" if player2_win
player1_win || player2_win
end
def check_diagonal(player1, player2)
if @board[0][0] == player1 && board[1][1] == player1 && board[2][2] == player1 ||
@board[0][2] == player1 && board[1][1] == player1 && board[2][0] == player1
puts "#{@player1} won!"
true
elsif @board[0][0] == player2 && board[1][1] == player2 && board[2][2] == player2 ||
@board[0][2] == player2 && board[1][1] == player2 && board[2][0] == player2
puts "#{@player2} won!"
true
end
end
end
我从这行代码调用这些条件:
def game
choosing_player
loop do
player1(@player1)
break if check_vertical(@player1,@player2) == true || check_diagonal(@player1,@player2) == true || check_horizontal(@player1,@player2) == true || full?
player2(@player2)
break if check_vertical(@player1,@player2) == true || check_diagonal(@player1,@player2) == true || check_horizontal(@player1,@player2) == true || full?
end
end
end
这是我的完整项目,它可以正常工作。
module CheckWinner
def check_horizontal(player1, player2)
player1_win = false
player2_win = false
@board.each do |row|
player1_win = row.all?(player1)
player2_win = row.all?(player2)
break if player1_win || player2_win
end
puts "#{player1} won!" if player1_win
puts "#{player2} won!" if player2_win
player1_win || player2_win
end
def check_vertical(player1, player2)
player1_win = false
player2_win = false
@board.transpose.each do |row|
player1_win = row.all?(player1)
player2_win = row.all?(player2)
break if player1_win || player2_win
end
puts "#{player1} won!" if player1_win
puts "#{player2} won!" if player2_win
player1_win || player2_win
end
def check_diagonal(player1, player2)
if @board[0][0] == player1 && board[1][1] == player1 && board[2][2] == player1 ||
@board[0][2] == player1 && board[1][1] == player1 && board[2][0] == player1
puts "#{@player1} won!"
true
elsif @board[0][0] == player2 && board[1][1] == player2 && board[2][2] == player2 ||
@board[0][2] == player2 && board[1][1] == player2 && board[2][0] == player2
puts "#{@player2} won!"
true
end
end
end
# TicTacToe Board
class Board
include CheckWinner
attr_accessor :board
def initialize
@board = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
end
def print_board
puts '-------------'
@board.each do |row|
print '|'
row.each do |col|
print " #{col}"
print ' | '
end
puts
end
puts '-------------'
end
def twodimentional_board
@board = @board.each_slice(3).map { |el| el }
end
def occupied_error(value)
puts 'There is a value or wrong place! Try Again!'
twodimentional_board
print_board
value == @player1 ? player2(@player1) : player1(@player2) # Stay same player
end
def move_if_possible(place, value)
@board.flatten!
if @board[place - 1] == 'X' || @board[place - 1] == 'O' || !place.between?(1, 9)
occupied_error(value)
else
@board[place - 1] = value
twodimentional_board
@board
end
end
def full?
if @board.flatten.all?(String)
puts 'Draw!'
true
end
end
def choosing_player
puts 'Choose for Player1(X or O)'
loop do
@player1 = gets.chomp!
break if @player1 == 'X' || @player1 == 'O'
puts 'Try Again!(X or O)'
end
puts "Player 1 is: #{@player1}"
@player1 == 'X' ? @player2 = 'O' : @player2 = 'X'
puts "Player 2 is: #{@player2}"
print_board
end
def player1(player1)
puts "Choice #{player1} Place on a board(1 to 10)"
@place = gets.chomp!.to_i
move_if_possible(@place, player1)
print_board
end
def player2(player2)
puts "Choice #{player2} Place on a board(1 to 10)"
@place = gets.chomp!.to_i
move_if_possible(@place, player2)
print_board
end
def game
choosing_player
loop do
player1(@player1)
break if check_vertical(@player1,@player2) == true || check_diagonal(@player1,@player2) == true || check_horizontal(@player1,@player2) == true || full?
player2(@player2)
break if check_vertical(@player1,@player2) == true || check_diagonal(@player1,@player2) == true || check_horizontal(@player1,@player2) == true || full?
end
end
end
board = Board.new
board.game
如果要实时预览这里是repllink
我不确定这是否正是您想要的,但这里有一些东西。
- 您的
@board
是一个 3 行的数组,但您可能获胜的只是一个点数数组。flatten
使用@board 意味着您可以按原样使用索引。 - 你已经有了一组获胜条件,然后忽略这个数组并手动完成所有选项
定义
# example winning board for 'O'
@board = [['O','X','O'], ['X','O','X'], ['X','O','O']]
player1 = 'X'
player2 = 'O'
首先,遍历 WIN_COMBINATIONS
中的可能组合,对于每个组合,从 @board
中获取这些单元格作为单独的数组,以供稍后检查。将数组数组存储为另一个数组(使用 #map
)。 *
字符是 ruby splat 运算符,它将数组转换为被调用过程的参数。
shorthand:
winmap = WIN_COMBINATIONS.map {|w| @board.flatten.values_at(*w)}
手写:
flat_board = @board.flatten
winmap = WIN_COMBINATIONS.map do |win|
flat_board.values_at(*win)
end
设置如下数组:
winmap = [
["O", "X", "O"], # top_row
["X", "O", "X"], # middle_row
["X", "O", "O"], # bottom_row
["O", "X", "X"], # left_column
["X", "O", "O"], # center_column
["O", "X", "O"], # right_column
["O", "O", "O"], # left_diagonal
["X", "O", "O"] # right_diagonal
]
一旦你有了棋盘上所有可能获胜的单元格的列表,你就可以使用你现有的 row
搜索代码,除了循环遍历每个数组,我们使用数组操作 #any?
识别匹配项
player1_win = winmap.any?{|m| m.all?(player1)}
player2_win = winmap.any?{|m| m.all?(player2)}
这应该消除所有单独的子程序,并给你一个 true/false 答案,看看是否有人赢了。
这里有一种不同的方法来确定棋盘是否表明获胜,如果是,谁获胜。
设board
为三个元素的数组,每个元素为三个元素的数组,每个元素为'X'
、'O'
或'_'
,最后一个表示单元格尚未标记。
让我们创建一些方法。
def row_win?(board)
board.each { |row| return row.first if row_uniq.size == 1 }
nil
end
这个方法returns 'X'
如果'X'连胜,'O'
如果'O'连胜,nil
如果没有玩家连续获胜。
def column_win?(board)
row_win?(board.transpose)
end
此方法类似returns'X'
、'O'
或nil
,表示某列是否有赢,如果有,谁赢
def diagonal_win?(board)
row_win? [[board[0][0], board[1][1], board[2][2]],
[board[0][2], board[1][1], board[2][0]]]
end
此方法对两条对角线执行相同的操作。
最后,
def win?(board)
row_win?(board) || column_win?(board) || diagonal_win?(board)
end
nil
无胜负返回
我们来试试吧。
win? [['X', '_', 'X'],
['_', 'X', 'O'],
['X', 'O', 'O']]
#=> 'X'
win? [['X', 'O', 'X'],
['_', 'O', 'X'],
['X', 'O', 'O']]
#=> 'O'
win? [['X', 'O', 'X'],
['O', 'O', 'X'],
['X', 'X', 'O']]
#=> nil
为什么不跟踪状态来帮助您在标记新动作时确定获胜条件?这样你就不必重新计算你已经知道的所有内容。
例如,如果我告诉你我的棋盘尺寸是 3
,第一行已经有 2
X,现在 X 在导致 3
Xs 的那一行下了一个新的棋子,你知道 X 马上就赢了吗?您甚至不需要了解有关董事会状态的任何其他信息。
您可以在此处应用相同的逻辑,只需跟踪每行的 X & O 计数,cols & diags 作为标记记录下来,您甚至不必循环查看谁赢了。
above-described 方法易于实施,并且需要 O(1)
时间复杂度来检查获胜条件。您用一点内存 (O(n)
) 来换取计算速度。