Skip to content

Instantly share code, notes, and snippets.

@louije
Last active April 20, 2019 13:35
Show Gist options
  • Save louije/606b9950e16992bbe491ceab47de3770 to your computer and use it in GitHub Desktop.
Save louije/606b9950e16992bbe491ceab47de3770 to your computer and use it in GitHub Desktop.
Tic Tac Toe
# Represents a tic-tac-toe board, composed over a 9-values array
# that contains the moves as strings (empty is " ").
class Board
VALUES = %I(
a1 a2 a3
b1 b2 b3
c1 c2 c3
)
def initialize
@values = Array.new(9, " ")
end
def play(move, player)
return false if self[move] != " "
self[move] = player
end
def won?
# rows
same?(a1, a2, a3) or
same?(b1, b2, b3) or
same?(c1, c2, c3) or
# columns
same?(a1, b1, c1) or
same?(a2, b2, c2) or
same?(a3, b3, c3) or
# diagonals
same?(a1, b2, c3) or
same?(a3, b2, c1)
end
def draw?
[email protected]?(" ") && !won?
end
def to_s
%{
1 2 3
┌─────┬─────┬─────┐
A │ #{a1} │ #{a2} │ #{a3} │
├─────┼─────┼─────┤
B │ #{b1} │ #{b2} │ #{b3} │
├─────┼─────┼─────┤
C │ #{c1} │ #{c2} │ #{c3} │
└─────┴─────┴─────┘
}
end
private
# Define a convenience getter for each board position
VALUES.each do |v|
define_method v do
self[v]
end
end
# Access the @values array
def [](sym)
@values[spreadsheet_notation(sym)]
end
def []=(sym, val)
@values[spreadsheet_notation(sym)] = val
end
def same?(*args)
args.all? { |v| v == args[0] } && args[0] != " "
end
# Takes a symbol in spreadsheet form (a1, b2, etc.)
# and returns the correct value from @values.
def spreadsheet_notation(sym)
%w(a b c).index(sym.to_s[0].downcase) * 3 + (sym.to_s[1].to_i - 1)
end
end
require "./board"
# Contains the tic-tac-toe game loop and CLI user interaction
class Game
def initialize
@board = Board.new
next_player
tick
end
private
# Mark - Game loop
def tick
puts @board
puts
print "Your turn, #{@current_player}: "
input = gets.chomp
play input
end
def play(move)
sym = validate_position(move)
return wrong_move(move) if not sym
@board.play(sym, @current_player) || taken(move)
if @board.won?
winner
elsif @board.draw?
draw
else
next_player
tick
end
end
def next_player
# By default, the first player is ×.
@current_player = (@current_player == "×" ? "○" : "×")
end
# Mark - Invalid moves
def taken(move)
puts
puts "“#{move}” is already occupied."
tick
end
def wrong_move move
puts "“#{move}” wasn’t recognized as a valid board position."
puts "Type a combination of rows and columns, such as “b2” or “1C”."
tick
end
# MARK - End messages
def winner
puts @board
puts "#{@current_player} has won the game. Congratulations!"
end
def draw
puts @board
puts "Game over. It's a draw."
end
# Mark - Validate input
def validate_position(move)
# ensure the letter comes before the number
chars = move.split('').sort.reverse
# apply the constraints for a board position:
# a letter within A..C and a number within 1..3.
return false unless %w(a b c).include?(chars[0].downcase) and [1,2,3].include?(chars[1].to_i) and chars.count == 2
chars.join.to_sym
end
end
#!/usr/bin/env ruby
require "./game"
t = Game.new
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment