Skip to content

Instantly share code, notes, and snippets.

@justinledwards
Last active November 23, 2023 15:15
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save justinledwards/40db10f6e1d5e8ed5a8e581502822a20 to your computer and use it in GitHub Desktop.
Save justinledwards/40db10f6e1d5e8ed5a8e581502822a20 to your computer and use it in GitHub Desktop.
ruby 2d tic tac toe
# 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