Skip to content

Instantly share code, notes, and snippets.

@todlazarov
Created February 7, 2016 17:59
Show Gist options
  • Save todlazarov/64012f4fa88cf0480e05 to your computer and use it in GitHub Desktop.
Save todlazarov/64012f4fa88cf0480e05 to your computer and use it in GitHub Desktop.
# Tic Tac Toe is a 2 player board game played on a 3x3 grid. Players take turns
# making a square. The first player to mark 3 squares in a row wins.
# Nouns: board, player, square, grid
# Verbs: play, mark
# Board
# Square
# Player
# - mark
# - play
require 'pry'
class Board
WINNING_LINES = [[1, 2, 3], [4, 5, 6], [7, 8, 9]] + # rows
[[1, 4, 7], [2, 5, 8], [3, 6, 9]] + # columns
[[1, 5, 9], [3, 5, 7]] # diagonals
def initialize
@squares = {}
reset
end
def set_square_at(key, marker)
@squares[key].marker = marker
end
def unmarked_keys
@squares.keys.select { |key| @squares[key].unmarked? }
end
def full?
unmarked_keys.empty?
end
def someone_won?
!!winning_marker
end
def winning_marker
WINNING_LINES.each do |line|
squares = @squares.values_at(*line)
return squares.first.marker if three_identical_markers?(squares)
end
nil
end
def reset
(1..9).each { |key| @squares[key] = Square.new }
end
def draw
puts " | |"
puts " #{@squares[1]} | #{@squares[2]} | #{@squares[3]}"
puts " | |"
puts "-----+-----+-----"
puts " | |"
puts " #{@squares[4]} | #{@squares[5]} | #{@squares[6]}"
puts " | |"
puts "-----+-----+-----"
puts " | |"
puts " #{@squares[7]} | #{@squares[8]} | #{@squares[9]}"
puts " | |"
end
def []=(num, marker)
@squares[num].marker = marker
end
private
def three_identical_markers?(squares)
markers = squares.select(&:marked?).collect(&:marker)
return false if markers.size != 3
markers.min == markers.max
end
end
class Square
INITIAL_MARKER = ' '
attr_accessor :marker
def initialize(marker=INITIAL_MARKER)
@marker = marker
end
def to_s
@marker
end
def unmarked?
marker == INITIAL_MARKER
end
def marked?
marker != INITIAL_MARKER
end
end
class Player
attr_accessor :name, :score
attr_reader :marker
def initialize(marker)
@marker = marker
set_name
self.score = 0
end
end
class Human < Player
def set_name
n = ''
loop do
puts "What is your name?"
n = gets.chomp
break unless n.empty?
puts "That is not a valid choice."
end
self.name = n
end
def score_counter
self.score += 1
end
end
class Computer < Player
def set_name
self.name = ['R2D2', 'Chappie', 'Sonny', 'Optimus Prime', 'Well-E'].sample
end
def score_counter
self.score += 1
end
end
class TTTGame
HUMAN_MARKER = 'X'
COMPUTER_MARKER = 'O'
FIRST_TO_MOVE = HUMAN_MARKER
attr_accessor :board, :human, :computer
def initialize
@board = Board.new
@human = Human.new(HUMAN_MARKER)
@computer = Computer.new(COMPUTER_MARKER)
@current_marker = FIRST_TO_MOVE
end
def play
display_welcome_message
loop do
reset_scores
binding.pry
loop do
display_board
loop do
current_player_moves
display_board
break if board.someone_won? || board.full?
end
update_score
break if human.score == 5 || computer.score == 5
reset
end
display_results
break unless play_again?
end
display_goodbye_message
end
def update_score
human.score_counter if board.winning_marker == human.marker
computer.score_counter if board.winning_marker == computer.marker
end
def display_results
display_board
if human.score == 5
puts "#{human.name} won!"
elsif computer.score == 5
puts "#{computer.name} won!"
end
end
def reset_scores
human.score = 0
computer.score = 0
end
def display_board
clear
puts "#{human.name}, you are a #{human.marker}. #{computer.name} is a #{computer.marker}"
puts ''
puts "The score is: #{human.name} - #{human.score} and #{computer.name} - #{computer.score}"
puts ''
board.draw
puts ''
end
private
def display_welcome_message
puts "Welcome to Tic Tac Toe!"
puts ""
end
def display_goodbye_message
puts "Thanks for playing Tic Tac Toe! Goodbye!"
end
def human_moves
puts "#{human.name} please choose a square (#{board.unmarked_keys.join(', ')}): "
square = nil
loop do
square = gets.chomp.to_i
break if board.unmarked_keys.include?(square)
puts "Sorry that's not a valid choice"
end
board[square] = human.marker
end
def computer_moves
board.set_square_at(board.unmarked_keys.sample, computer.marker)
end
def play_again?
answer = nil
loop do
puts "Would you like to play again? (y/n)"
answer = gets.chomp.downcase
break if %w(y n).include? answer
puts "Sorry, must be y or n"
end
answer == 'y'
end
def clear
system 'clear'
end
def reset
board.reset
@current_marker = FIRST_TO_MOVE
sleep(1)
end
def current_player_moves
if @current_marker == HUMAN_MARKER
human_moves
@current_marker = COMPUTER_MARKER
else
computer_moves
@current_marker = HUMAN_MARKER
end
end
end
game = TTTGame.new
game.play
@cris-solomon
Copy link

also the play method with three nested loops is hard to understand. i would split it up into separate methods - main, play_series, play_game

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment