Created
December 7, 2021 20:03
-
-
Save havenwood/7010dc226bcf18e9831beaf3805b60ed to your computer and use it in GitHub Desktop.
Matrix Tic Tac Toe for fun
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# frozen_string_literal: true | |
require 'matrix' | |
class TicTacToe | |
BOARD_SIZE = 3 | |
MARKS = {x: -1, o: 1}.freeze | |
private_constant :MARKS | |
ROW_MOVES = %i[top middle bottom].freeze | |
private_constant :ROW_MOVES | |
COLUMN_MOVES = %i[left middle right].freeze | |
private_constant :COLUMN_MOVES | |
attr_reader :board | |
def initialize | |
@board = new_board | |
@turns = MARKS.keys.cycle | |
end | |
def new_board | |
Matrix.zero(BOARD_SIZE) | |
end | |
def move(mark, row, column) | |
validate_turn(mark) | |
validate_moves(row, column) | |
self[ROW_MOVES.index(row), COLUMN_MOVES.index(column)] = mark | |
{win?: win?(mark), board: @board} | |
end | |
def []=(column, row, mark) | |
validate_mark(mark) | |
@board[row, column] = MARKS.fetch(mark) | |
@turns.next | |
end | |
def win?(mark) | |
straight_win?(mark) || diagonal_win?(mark) | |
end | |
private | |
def validate_mark(mark) | |
unless MARKS.key?(mark) | |
expected = MARKS.keys.map { |key| "`#{key.inspect}'" }.join(', ') | |
raise ArgumentError, | |
"given `#{mark.inspect}', expected #{expected}" | |
end | |
end | |
def validate_turn(mark) | |
unless mark == @turns.peek | |
expected = | |
raise ArgumentError, | |
"`#{mark.inspect}' given, expected `#{@turns.peek.inspect}' due to turn" | |
end | |
end | |
def validate_moves(row, column) | |
[[ROW_MOVES, row], [COLUMN_MOVES, column]].each do |possible_moves, given_move| | |
next if possible_moves.include?(given_move) | |
expected = possible_moves.map { |move| "`#{move.inspect}'" }.join(', ') | |
raise ArgumentError, | |
"given `#{given_move.inspect}', expected #{expected}" | |
end | |
end | |
def straight_win?(mark) | |
%i[row_vectors column_vectors].any? do |direction| | |
@board.public_send(direction).any? do |vectors| | |
vectors.sum == MARKS.fetch(mark) * BOARD_SIZE | |
end | |
end | |
end | |
def diagonal_win?(mark) | |
[@board, rotated_board].any? do |board_orientation| | |
board_orientation.each(:diagonal).sum == MARKS.fetch(mark) * BOARD_SIZE | |
end | |
end | |
def rotated_board | |
Matrix[*@board.to_a.map(&:reverse).transpose] | |
end | |
end | |
game = TicTacToe.new | |
game.move(:x, :top, :right) | |
game.move(:o, :top, :middle) | |
game.move(:x, :middle, :middle) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment