Connect 4 Diagonal Win Check - PullRequest
       3

Connect 4 Diagonal Win Check

0 голосов
/ 09 декабря 2018

Я тестирую диагональный выигрыш в игре Ruby Connect 4.Я использовал жестко заданный 2D-массив для тестирования:

grid_array = [
["B", ".", ".", ".", ".", ".", ".", "."],
[".", "B", ".", ".", ".", ".", ".", "."],
[".", ".", "B", ".", ".", ".", ".", "."],
[".", ".", ".", "B", ".", ".", ".", "."],
[".", ".", ".", "X", "M", ".", ".", "."],
[".", ".", ".", ".", "X", "M", ".", "."],
[".", ".", ".", ".", ".", "X", "M", "."],
[".", ".", ".", ".", ".", ".", "X", "M"]
]

Этот внутренний цикл этого метода работает правильно (правильно определяет, что 'M' или 'B' по отдельности являются победителями, но я застрял, когдапытаясь переместить диагональную проверку по столбцам или вверх по строкам с помощью внешнего цикла, например, выбрать 'X' в качестве выигрышного значения.

def nw_diagonal_win (playing_board)
  row = 7
  while row < playing_board.size && row >= 0

    row = 7
    column = 7
    piece_count = 0

    while (row < playing_board.size && column < playing_board[row].size && column >= 0)

      if playing_board[row][column] == 'M'
        piece_count += 1

        if piece_count == 4
          puts "Player is the winner in a diagonal!"
        end

        puts piece_count.inspect

      else
        piece_count = 0
        puts "No winner."
      end

      row += 1
      column += 1
    end

    row -= 1
  end
end

Отредактировано для добавления: «Победитель» в Connect4 набора по 4 смежных фигуры (по горизонтали, вертикали или диагонали). В моей игре это обозначается 'X' и '0'. Куски "сбрасываются" с вершины столбца в сетке и падают на дно.Наиболее доступное пространство в этом столбце. Части могут быть сложены в столбце, но не могут «плавать» в середине доски. Диагонали могут проходить сверху вниз справа налево или сверху справа внизу слева. Выигрыш происходит только если фигурынепрерывно в сетке (без обтекания).

Подумайте над увеличенной версией крестики-нолики, где сначала нужно сделать ходы в нижнем ряду, а затем можно переходитьРавным образом быть сделаны в строках выше, укладывая как коробки.Четыре подряд (по горизонтали, вертикали или диагонали \ /) побеждают.

В ответ на предложение Стива, ниже:

def top_left_diagonal (playing_board, player_piece)
  row = 0
  while row < playing_board.size - 3
    piece_count = 0
    column = 0
    while column < playing_board[row].size - 3 && playing_board[row][column] == player_piece
      if (playing_board[row][column] == playing_board[row + piece_count][column + piece_count])
        piece_count += 1
      else
        piece_count = 0
      end
      column += 1
    end
    if piece_count == 4
      puts "Diagonal winner!"
    end
    row += 1
  end
end

Ответы [ 4 ]

0 голосов
/ 10 декабря 2018

Я нашел решение, которое, похоже, работает в тестировании.Он идет снизу справа вверху слева, начиная с строки 7, столбца 7 в моей сетке 8x8.Я также создал antediagonal, который перемещается снизу слева вверху справа.

Я проверял в течение последнего времени и не нашел ошибку, но хотел бы знать, если кто-то еще пробивает в ней дыры.

Я очень ценю всех вас - ваши решения и советы привели меня к этому!

def nw_diagonal_win (playing_board, player_piece)
    row = 7
    column = 7
    piece_count = 0
    while row < playing_board.size && row >= 0 && column < playing_board[row].size && column >= 0
      if playing_board[row][column] == player_piece
        piece_count += 1
        column -= 1
        row -= 1
      else
        piece_count = 0
        column -= 1
        if column < 3
          row -= 1
          column = 7
        end
      end
        if piece_count == 4
          puts "Player #{player_piece} is the winner in a diagonal!"
        end
    end
end
0 голосов
/ 09 декабря 2018

Если есть выигрышная диагональ, начальная позиция должна быть в диапазоне строк [0-3] и col [0-3].Любая диагональ, начинающаяся за пределами этого верхнего левого прямоугольника, не будет иметь достаточного положения справа и вниз, чтобы сделать ее четырьмя подряд.

Так что на самом деле вам нужны вложенные циклы while row < 4 и while col < 4.

Для каждой комбинации столбцов строк, предполагая, что значение не равно "."Затем вы можете установить значение piece_count на 1, а затем выполнить счетчик 1 на 3 и проверить, что playing_board[row + counter][col + counter] равно значению playing_board[row][col], и если оно увеличивается, то ваш piece_count.

За пределами счетчика от 1 до 3цикл, если piece_count равен 4, у вас есть победитель.

0 голосов
/ 10 декабря 2018

Предположим, у нас есть

grid = [
  %w| . . . . . . |,
  %w| . . . w w . |,
  %w| . . . w b . |,
  %w| b . w . b . |,
  %w| w w . w b b |,
  %w| b w b b w b |
]
  #=> [[".", ".", ".", ".", ".", "."], 
  #    [".", ".", ".", "w", "w", "."],
  #    [".", ".", ".", "w", "b", "."],
  #    ["b", ".", "w", ".", "b", "."],
  #    ["w", "w", ".", "w", "b", "b"],
  #    ["b", "w", "b", "b", "w", "b"]]

Правда, это всего лишь 6x6, но решение ничем не отличается.

Во-первых, так как массив маленький, нам не о чем беспокоитьсяо вычислительной эффективности, поэтому мы можем сосредоточиться на эффективности кода.

Давайте сначала проверим, есть ли четыре в строке в каждой строке.

Проверка строк

def four_in_a_row_by_row(arr)
  arr.each do |row|
    a = row.each_cons(4).find { |a| a.uniq.size == 1 && a.first != '.' }
    return a.first unless a.nil?        
  end
  nil
end

Этот метод возвращает w, если в строке четыре w, b, если их четыреb в строке, иначе nil.

Обратите внимание, что этот метод не требует, чтобы arr.size == grid.size или чтобы все элементы arr были одинакового размера.Он просто проверяет, имеет ли какой-либо элемент четыре 'w' или четыре 'b' подряд.Это будет иметь значение позже.

Последний элемент arr, переданный, например, в блок, следующий:

row =  ["b", "w", "b", "b", "w", "b"]

Затем мы вычисляем

enum0 = row.each_cons(4)
  #=> #<Enumerator: ["b", "w", "b", "b", "w", "b"]:each_cons(4)>

и

enum1 = enum0.find
  #=> #<Enumerator: #<Enumerator: ["b", "w", "b", "b", "w", "b"]:each_cons(4)>:find>

enum1 можно рассматривать как составной перечислитель, хотя Ruby не определяет его так.

Мы можем преобразовать этот перечислитель вмассив для просмотра элементов, которые будут переданы в блок.

enum1.to_a
  #=> [["b", "w", "b", "b"], ["w", "b", "b", "w"], ["b", "b", "w", "b"]]

См. Enumerable # each_cons .

Первый элемент передается в блок иследующие вычисления сделаны.

    a = enum1.next
    u = a.uniq
    u.size == 1

Поэтому нам не нужно вычислять a.first != '.'.Оставшиеся два элемента enum1 передаются в блок, и для каждого вычисляется nil, что указывает на то, что в последнем ряду нет ни четырех 'w', ни 'b'.

Мы почти закончили!

"Подождите", говорите, мы только проверили строки!Есть еще колонны и все диагонали!Оставайтесь с нами ...

Проверьте столбцы

Это очень просто.

four_in_a_row_by_row(grid.transpose)
  #=> nil

Проверьте диагонали (верхслева направо)

Здесь все, что нам нужно сделать, - это создать массив arr, содержащий диагонали, а затем применить four_in_a_row(arr).Сначала определите диагонали, которые включают элементы в первом столбце, которые имеют длину 4 или больше:

(0..grid.size-4).map { |i| (0..grid.size-1-i).map { |j| grid[i+j][j] } }
  #=> [[".", ".", ".", ".", "b", "b"], [".", ".", "w", "w", "w"], [".", ".", ".", "b"]]

Аналогично, определите диагонали, которые включают элементы в первом ряду, отличные от [0, 0], которые имеют длину 4 или больше:

(1..grid.first.size-4).map { |j| (0..grid.size-j-1).map { |i| grid[i][j+i] } }
  #=> [[".", ".", "w", "b", "b"], [".", "w", "b", "."]]

def diagonals(grid)
  (0..grid.size-4).map { |i| (0..grid.size-1-i).map { |j| grid[i+j][j] } }.
    concat((1..grid.first.size-4).map { |j| (0..grid.size-j-1).map { |i| grid[i][j+i] } })
end

arr = diagonals(grid)
  #=> [[".", ".", ".", ".", "b", "b"], [".", ".", "w", "w", "w"], [".", ".", ".", "b"],
  #    [".", ".", "w", "b", "b"], [".", "w", "b", "."]]

four_in_a_row_by_row(arr)
  #=> nil

Проверьте андиагоналы (с левого на верхний правый угол)

Мы могли бы пройти через те же рассуждениякак и при вычислении диагоналей, но поскольку эффективность вычислений здесь не важна, есть более простой способ: вычислить диагонали массива, полученные «вращением» grid 90 градусов.

def rotate90(grid)
  ncols = grid.first.size
  grid.each_index.with_object([]) { |i,a| a << ncols.times.map { |j| grid[j][ncols-1-i] } }
end

arr = rotate90(grid)
  #=> [[".", ".", ".", ".", "b", "b"],
  #    [".", "w", "b", "b", "b", "w"],
  #    [".", "w", "w", ".", "w", "b"],
  #    [".", ".", ".", "w", ".", "b"],
  #    [".", ".", ".", ".", "w", "w"],
  #    [".", ".", ".", "b", "w", "b"]]
arr1 = diagonals(arr)
  #=> [[".", "w", "w", "w", "w", "b"], [".", "w", ".", ".", "w"],
  #    [".", ".", ".", "b"], [".", "b", ".", ".", "w"], [".", "b", "w", "b"]]
four_in_a_row_by_row(arr1)
  #=> "w"

Собираем все вместе

def four_in_a_row(grid)
  four_in_a_row_by_row(grid) ||
  four_in_a_row_by_row(grid.transpose) ||
  four_in_a_row_by_row(diagonals(grid)) ||
  four_in_a_row_by_row(diagonals(rotate90(grid)))
end

four_in_a_row_by_row(grid) 
  #=> "w"
0 голосов
/ 09 декабря 2018

Я играл с похожей сеткой, за кроссворды и вышел со способом получить диагонали.Я немного изменил, чтобы соответствовать соединению 4. Это не полный ответ, но я надеюсь, что это может помочь.

Прежде всего я хочу отобразить сетку, чтобы иметь также координаты:

grid_map = grid_array.map.with_index{ |line, y| line.map.map.with_index { |e, x| [e, x, y] } }

Затем пара методов для восстановления всех диагоналей матрицы (в любом случае, должно быть лучшее решение).

В первом случае используется только половина диагоналей в одном направлении:

def half_diagonals(matrix)
  max_y = matrix[0].size
  max_x = matrix.size
  diagonals = []
  (0...max_x).each do |x_start|
    x = x_start
    y = 0
    diagonal = [matrix[x][y]]
    while  x < max_x - 1 and y < max_y - 1 do
      x += 1
      y += 1
      diagonal << matrix[x][y]
    end
    diagonals << diagonal
  end
  # only in diagonals with at least four spots
  return diagonals.select{ |d| d.size >= 4 }
end

Это вызывает half_diagonals (матрица) для преобразования матрицы, чтобы получить все диагонали:

def diagonals(matrix)
  half_diagonals(matrix) +
  half_diagonals(matrix.transpose)[1..-1] +
  half_diagonals(matrix.reverse) +
  half_diagonals(matrix.reverse.transpose)[1..-1]
end

# diagonals(grid_map).each { |e| p e } # to print the diagonals

Теперь необходимо проверить каждую диагональ на наличие победителя, поэтому определите метод, чтобы сделать это:

def check_diagonal(diagonal, empty_spot = ".")
  check = diagonal.chunk_while { |s1, s2| s1[0] == s2[0] }.map { |e| [e.count, e[0][0], e.map{ |ee| ee[1..-1]}] }
  return detected = check.detect { |e| e[0] == 4 and e[1] != empty_spot }
  # it returns the first four detected
end

Теперь вызовите методы на grid_map, чтобы получить количество, цвет и координаты подключенных четырех:

diagonals(grid_map).map { |diagonal| check_diagonal(diagonal) }.compact
#=> [[4, "B", [[0, 0], [1, 1], [2, 2], [3, 3]]], [4, "X", [[3, 4], [4, 5], [5, 6], [6, 7]]]]

Четыре "M" не возвращаютсяиз-за check.detect.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...