Skip to content

Instantly share code, notes, and snippets.

@everylittlefox
Created August 12, 2022 11:50
Show Gist options
  • Select an option

  • Save everylittlefox/33e0e2ab96337b0fb456c02a4f379262 to your computer and use it in GitHub Desktop.

Select an option

Save everylittlefox/33e0e2ab96337b0fb456c02a4f379262 to your computer and use it in GitHub Desktop.
# game has two players who take turns placing either an X or an O on the board.
# game's solved if either the grid fills up or a player wins.
# a player wins if they're able to align their character (X or O) along a vertical,
# horizontal, or diagonal set of cells.
class Player
attr_accessor :character
attr_reader :name
def initialize(name)
@name = name
end
# #play takes a view of the game's grid and returns a number in range 1..9
# number represents the position of the intended cell.
def play(game_state)
gets.to_i
end
def to_s
name
end
end
class TicTacToe
SIZE = 3
attr_accessor :player_one, :player_two, :winner, :next_player, :board, :state
def initialize
@board = Array.new(SIZE) { |i| Array.new(SIZE) { |j| (SIZE * i) + j + 1 } }
end
def start
self.state = :started
loop do
puts "Welcome to TicTacToe. Enter a name for player one."
while (player_one_name = gets.strip).empty?
puts "Name must be a none-empty string."
end
puts "\nEnter a name for player two. Player Two's name cannot be '#{player_one_name}'"
while (player_two_name = gets.strip).empty? || player_two_name == player_one_name
puts "Player Two, pick a (non-empty) name different from player one."
end
self.player_one = Player.new(player_one_name)
self.player_two = Player.new(player_two_name)
puts "\nWho get's to play first will be determined by a coin toss. Ready? LFG!"
if rand > 0.5
self.player_one.character = :x
self.player_two.character = :o
self.next_player = player_one
else
self.player_one.character = :o
self.player_two.character = :x
self.next_player = player_two
end
puts "\n#{self.next_player} goes first."
while self.state == :started
print_game_board
puts "\n#{self.next_player}, enter a number (1-9) for an available cell to place an '#{self.next_player.character}'"
until self.empty_cells.include?(chosen_cell = self.next_player.play(self.board))
puts "\nSorry, you can't play at #{chosen_cell}. Pick another."
end
update_board(chosen_cell)
update_game_state
update_next_player
end
print_game_board
case self.state
when :has_winner
puts "\nGame over! #{self.winner}, playing as '#{self.winner.character}', wins."
when :no_winner
puts "\nGame over! Too bad, nobody wins :("
end
puts "\nWould you like to play a new game? Press 'y' for yes or 'n' for no."
break if gets.strip.downcase[0] == 'n'
end
end
private
def empty_cells
self.board.flatten.select { |c| c.to_s.to_i.between?(1,9) }
end
def print_game_board
row_strs = board.map { |row| row.map { |cell| cell.to_s.center(3) }.join("|") }
puts "\n" + row_strs.join("\n---+---+---\n")
end
def col_from_cell(chosen_cell)
(chosen_cell - 1) % SIZE
end
def row_from_cell(chosen_cell)
(chosen_cell - 1 - col_from_cell(chosen_cell)) / SIZE
end
def update_next_player
if next_player.name == player_one.name
self.next_player = player_two
else
self.next_player = player_one
end
end
def update_board(chosen_cell)
self.board[row_from_cell(chosen_cell)][col_from_cell(chosen_cell)] = next_player.character
end
def update_game_state
if board.none? { |row| row.any? { |c| c.to_s.to_i.between?(1, 9) } }
self.state = :no_winner
return
end
cells_to_check = [
[1,2,3],
[4,5,6],
[7,8,9],
[1,4,7],
[2,5,8],
[3,6,9],
[1,5,9],
[3,5,7]
]
cells_to_check.each do |cells|
first = board[row_from_cell(cells[0])][col_from_cell(cells[0])]
if cells.all? { |cell| board[row_from_cell(cell)][col_from_cell(cell)] == first }
self.state = :has_winner
self.winner = self.next_player
return
end
end
end
end
game = TicTacToe.new
game.start
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment