Как найти первую выигрышную комбинацию на доске Ti c -Ta c -Toe? - PullRequest
1 голос
/ 02 февраля 2020

Новое на Ruby, так что извините за плохой код. Я хотел бы пройтись по многомерному массиву WIN_COMBINATIONS и проверить, имеет ли хотя бы один массив все его элементы, равные 'X' или все равные 'O'. Если это так, вернуть соответствующий массив. Это делается с помощью функции won?, но, похоже, она возвращает только весь многомерный массив. Любая помощь будет оценена.

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

Ответы [ 2 ]

1 голос
/ 02 февраля 2020

[...] кажется, что возвращается только весь многомерный массив.

Существует несколько проблем с вашим попытанным решением:

  1. WIN_COMBINATIONS - это массив индексов. Эти индексы являются цифрами c, поэтому они никогда не будут 'X' или 'O'. Вы должны проверить, являются ли их соответствующие значения 'X' или 'O'.

  2. or * оператор потока управления предназначен для do_this or fail сценария ios. логическое "или" оператор равен ||. Использование or вместо || может работать, но может привести к неожиданным результатам из-за его более низкого приоритета . Вы почти всегда хотите ||.

  3. Выражение array.all? { |element| element == 'X' || element == 'O' } проверяет, являются ли все элементы 'X' или 'O'. Это будет true для ['X','O','O'] и false для ['X',' ','O']. Это потому, что вы поместили условное в блок. Вам нужно проверить, все ли элементы 'X' или все 'O':

    array.all?('X') || array.all?('O')
    
  4. Возвращаемое значение вашего метода является результатом WIN_COMBINATIONS.each { ... } и Array#each всегда возвращает сам массив (т.е. WIN_COMBINATIONS) независимо от результата блоков. Чтобы получить первый элемент, соответствующий условию, используйте 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 возвращает значения для соответствующих индексов (* преобразует массив indices в список аргументов, поэтому values_at(*[0,1,2]) становится values_at(0,1,2)). Затем вторая строка блока проверяет, все ли значения 'X' или все 'O'. Как только это оценивается как true, l oop разрывается и find возвращает соответствующий элемент. (или nil, если совпадений не было)

0 голосов
/ 02 февраля 2020

Вот как я бы подошел к проблеме:

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

В методе initialize мы создаем массив 3 * 3, который представляет все позиции на доске. Это делает его намного проще, поскольку он уже сгруппирован по строкам. Вместо пустой строки используйте 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 вернет :o.

...