Created
February 7, 2016 17:59
-
-
Save todlazarov/64012f4fa88cf0480e05 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 | |
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 |
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
line 169 (reset) doesn't get executed when the win count has reached 5.
i would place that at the beginning of the loop.