Last active
February 5, 2016 20:37
-
-
Save todlazarov/e6fcaf8f29ce3bc36ea0 to your computer and use it in GitHub Desktop.
This file contains 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
# 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 | |
loop do | |
display_board | |
loop do | |
current_player_moves | |
display_board | |
break if board.someone_won? || board.full? | |
end | |
display_board | |
update_score | |
break if human.score == 5 || computer.score == 5 | |
reset | |
end | |
display_results | |
reset_scores | |
binding.pry | |
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 | |
case board.winning_marker | |
when human.marker | |
puts "#{human.name} won!" | |
when computer.marker | |
puts "#{computer.name} won!" | |
else | |
puts "Its a tie!" | |
end | |
end | |
def reset_scores | |
human.score = 0 | |
computer.score = 0 | |
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 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 | |
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 | |
puts "Next round!" | |
@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 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment