如何在井字棋盘中找到第一个获胜组合?

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.

您尝试的解决方案存在几个问题:

  1. WIN_COMBINATIONS 是一个索引数组。这些索引是数字,因此它们永远不会是 'X''O'。你要检查他们对应的值'X'还是'O'.

  2. or 是用于 do_this or fail 场景的 控制流运算符 布尔值 "or" 运算符 ||。使用 or 而不是 || 可能 有效,但由于 precedence 较低,可能会产生意想不到的结果。你几乎总是想要 ||.

  3. 表达式 array.all? { |element| element == 'X' || element == 'O' } 检查是否所有元素都是 'X''O'['X','O','O']true['X',' ','O']false。那是因为你把条件放在了块中。你想要的是检查元素是否都是 'X',或者都是 'O':

    array.all?('X') || array.all?('O')
    
  4. 您的方法的 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 如果没有匹配项)