Last active
November 23, 2023 15:15
-
-
Save justinledwards/40db10f6e1d5e8ed5a8e581502822a20 to your computer and use it in GitHub Desktop.
ruby 2d tic tac toe
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 'ruby2d' | |
# This class represents the game board. | |
class Board | |
attr_accessor :cells | |
def initialize | |
@cells = Array.new(3) { Array.new(3, '') } | |
end | |
def check_win | |
check_rows || check_columns || check_diagonals || check_draw | |
end | |
def check_rows | |
3.times do |i| | |
return @cells[i][0] if line_winner?(@cells[i]) | |
end | |
nil | |
end | |
def check_columns | |
3.times do |i| | |
column = @cells.map { |row| row[i] } | |
return column[0] if line_winner?(column) | |
end | |
nil | |
end | |
def check_diagonals | |
diagonal1 = [0, 1, 2].map { |i| @cells[i][i] } | |
diagonal2 = [0, 1, 2].map { |i| @cells[i][2 - i] } | |
return diagonal1[0] if line_winner?(diagonal1) | |
return diagonal2[0] if line_winner?(diagonal2) | |
nil | |
end | |
def check_draw | |
'Draw' if @cells.all? { |row| row.none?(&:empty?) } | |
end | |
def line_winner?(line) | |
line[0] != '' && line.uniq.size == 1 | |
end | |
end | |
# This class handles user input. | |
class InputHandler | |
attr_accessor :highlighted_row, :highlighted_col | |
attr_reader :game_instance | |
def initialize(game_instance) | |
@game_instance = game_instance | |
@highlighted_row = 0 | |
@highlighted_col = 0 | |
end | |
def handle_move(event) | |
action_map = { | |
'left' => method(:move_left), | |
'right' => method(:move_right), | |
'up' => method(:move_up), | |
'down' => method(:move_down), | |
'return' => method(:make_current_move), | |
'enter' => method(:make_current_move) | |
} | |
action_map[event.key]&.call | |
end | |
private | |
def move_left | |
@highlighted_col = [0, @highlighted_col - 1].max | |
end | |
def move_right | |
@highlighted_col = [2, @highlighted_col + 1].min | |
end | |
def move_up | |
@highlighted_row = [0, @highlighted_row - 1].max | |
end | |
def move_down | |
@highlighted_row = [2, @highlighted_row + 1].min | |
end | |
def make_current_move | |
@game_instance.make_move(@highlighted_row, @highlighted_col) | |
end | |
# Methods like `handle_move`, `move_left`, `move_right`, etc. | |
end | |
# This class represents a Tic Tac Toe game. | |
class TicTacToe | |
attr_reader :game_over, :board, :input_handler | |
def initialize | |
@board = Board.new | |
@input_handler = InputHandler.new(self) # Pass self (TicTacToe instance) | |
@current_player = 'X' | |
initialize_game | |
end | |
def initialize_game | |
@input_handler.highlighted_row = 0 | |
@input_handler.highlighted_col = 0 | |
@game_over = false | |
end | |
def make_move(row, col) | |
return if @board.cells[row][col] != '' || @game_over | |
@board.cells[row][col] = @current_player | |
check_game_status | |
end | |
def handle_input(event) | |
if @game_over | |
initialize_game if event.key == 'r' | |
else | |
@input_handler.handle_move(event) | |
end | |
end | |
def current_state | |
{ | |
board: @board, | |
highlighted_row: @input_handler.highlighted_row, | |
highlighted_col: @input_handler.highlighted_col, | |
game_over: @game_over, | |
winner: @winner | |
} | |
end | |
def check_game_status | |
win = @board.check_win | |
if win | |
@game_over = true | |
@winner = win | |
else | |
@current_player = @current_player == 'X' ? 'O' : 'X' | |
end | |
end | |
end | |
def draw_game(game) | |
clear | |
state = game.current_state | |
draw_grid(state) | |
draw_moves(state[:board]) | |
draw_result(state) if state[:game_over] | |
end | |
def draw_grid(state) | |
3.times do |row| | |
3.times do |col| | |
Square.new(x: col * 100, y: row * 100, size: 100, color: 'gray') if should_highlight?(row, col, state) | |
draw_line(col, row) | |
end | |
end | |
end | |
def should_highlight?(row, col, state) | |
row == state[:highlighted_row] && col == state[:highlighted_col] && !state[:game_over] | |
end | |
def draw_line(col, row) | |
Line.new(x1: col * 100, y1: 0, x2: col * 100, y2: 300, width: 2) | |
Line.new(x1: 0, y1: row * 100, x2: 300, y2: row * 100, width: 2) | |
end | |
def draw_moves(board) | |
board.cells.each_with_index do |row, y| | |
row.each_with_index do |cell, x| | |
Text.new(cell, x: x * 100 + 40, y: y * 100 + 40, size: 40, color: 'white', z: 20) unless cell.empty? | |
end | |
end | |
end | |
def draw_result(state) | |
message = state[:winner] == 'Draw' ? "It's a draw!" : "#{state[:winner]} wins!" | |
Text.new(message, x: 20, y: 140, size: 30, color: 'yellow') | |
Text.new("Press 'R' to restart", x: 20, y: 170, size: 20, color: 'yellow') | |
end | |
game = TicTacToe.new | |
set width: 300, height: 300 | |
set title: 'Tic Tac Toe' | |
on :key_down do |event| | |
game.handle_input(event) | |
draw_game(game) | |
end | |
on :mouse_down do |event| | |
row = event.y / 100 | |
col = event.x / 100 | |
game.make_move(row, col) unless game.game_over | |
draw_game(game) | |
end | |
draw_game(game) | |
show |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment