-
-
Save bridgpal/6774295 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
require "Rumoji" # See; https://github.com/mwunsch/rumoji | |
# Customize grid space and player icons as emoji charcters from: http://www.emoji-cheat-sheet.com/ | |
require "Rainbow" | |
class Grid | |
attr_accessor :production_run, :player_x_moves, :player_o_moves | |
def initialize(production_run, player_x_moves, player_o_moves) | |
@is_debug_mode = false # set to true to invoke debugging statements if needed... | |
@production_run = production_run # | |
@player_x_moves = player_x_moves # the set of grid positions filled by player x | |
@player_o_moves = player_o_moves # the set of grid positions filled by player o | |
@player_x = Rumoji.decode(":negative_squared_cross_mark:") # "X" | |
@player_o = Rumoji.decode(":large_blue_circle:") # "O" | |
@box1 = Rumoji.decode(":one:") # "1" | |
@box2 = Rumoji.decode(":two:") # "2" | |
@box3 = Rumoji.decode(":three:") # "3" | |
@box4 = Rumoji.decode(":four:") # "4" | |
@box5 = Rumoji.decode(":five:") # "5" | |
@box6 = Rumoji.decode(":six:") # "6" | |
@box7 = Rumoji.decode(":seven:") # "7" | |
@box8 = Rumoji.decode(":eight:") # "8" | |
@box9 = Rumoji.decode(":nine:") # "9" | |
# Needed to keep the base number grid value settings useable again despite player move updates (x and o insertions) to the game grid boxes... | |
@box1_base = @box1.dup | |
@box2_base = @box2.dup | |
@box3_base = @box3.dup | |
@box4_base = @box4.dup | |
@box5_base = @box5.dup | |
@box6_base = @box6.dup | |
@box7_base = @box7.dup | |
@box8_base = @box8.dup | |
@box9_base = @box9.dup | |
# Create start grid state... | |
update_grid() | |
end # method initialize | |
def print_grid() | |
print "\n . . " | |
print "\n #{@box1} | #{@box2} | #{@box3} " | |
print "\n----+----+----" | |
print "\n #{@box4} | #{@box5} | #{@box6} " | |
print "\n----+----+----" | |
print "\n #{@box7} | #{@box8} | #{@box9} " | |
print "\n ' ' " | |
end # method print_grid | |
def reset_grid() | |
initialize(@production_run, [], []) | |
end # method reset_grid | |
def update_grid() | |
debug("\nlocation : at start of 'upgrade_grid' method.") if @is_debug_mode | |
#update visual grid boxes for player_x_moves.. | |
@player_x_moves.each do |x_move| | |
print "\nDEBUG: player_x_moves.each instance: #{get_icon(x_move)} \n" if @is_debug_mode | |
case x_move | |
when "1" | |
@box1 = @player_x.dup #"X" | |
print "\nDEBUG: setting #{@box1_base} to #{@player_x}\n" if @is_debug_mode | |
when "2" | |
@box2 = @player_x.dup #"X" | |
print "\nDEBUG: setting #{@box2_base} to #{@player_x}\n" if @is_debug_mode | |
when "3" | |
@box3 = @player_x.dup #"X" | |
print "\nDEBUG: setting #{@box3_base} to #{@player_x}\n" if @is_debug_mode | |
when "4" | |
@box4 = @player_x.dup #"X" | |
print "\nDEBUG: setting #{@box4_base} to #{@player_x}\n" if @is_debug_mode | |
when "5" | |
@box5 = @player_x.dup #"X" | |
print "\nDEBUG: setting #{@box5_base} to #{@player_x}\n" if @is_debug_mode | |
when "6" | |
@box6 = @player_x.dup #"X" | |
print "\nDEBUG: setting #{@box6_base} to #{@player_x}\n" if @is_debug_mode | |
when "7" | |
@box7 = @player_x.dup #"X" | |
print "\nDEBUG: setting #{@box7_base} to #{@player_x}\n" if @is_debug_mode | |
when "8" | |
@box8 = @player_x.dup #"X" | |
print "\nDEBUG: setting #{@box8_base} to #{@player_x}\n" if @is_debug_mode | |
when "9" | |
@box9 = @player_x.dup #"X" | |
print "\nDEBUG: setting #{@box9_base} to #{@player_x}\n" if @is_debug_mode | |
end # case | |
end # player_x_moves.each | |
#update visual grid boxes for player_o_moves | |
@player_o_moves.each do |o_move| | |
print "\nDEBUG: player_o_moves.each instance: #{get_icon(o_move)} \n" if @is_debug_mode | |
case o_move | |
when "1" | |
@box1 = @player_o.dup #"O" | |
print "\nDEBUG: setting #{@box1_base} to #{@player_o}\n" if @is_debug_mode | |
when "2" | |
@box2 = @player_o.dup #"O" | |
print "\nDEBUG: setting #{@box2_base} to #{@player_o}\n" if @is_debug_mode | |
when "3" | |
@box3 = @player_o.dup #"O" | |
print "\nDEBUG: setting #{@box3_base} to #{@player_o}\n" if @is_debug_mode | |
when "4" | |
@box4 = @player_o.dup #"O" | |
print "\nDEBUG: setting #{@box4_base} to #{@player_o}\n" if @is_debug_mode | |
when "5" | |
@box5 = @player_o.dup #"O" | |
print "\nDEBUG: setting #{@box5_base} to #{@player_o}\n" if @is_debug_mode | |
when "6" | |
@box6 = @player_o.dup #"O" | |
print "\nDEBUG: setting #{@box6_base} to #{@player_o}\n" if @is_debug_mode | |
when "7" | |
@box7 = @player_o.dup #"O" | |
print "\nDEBUG: setting #{@box7_base} to #{@player_o}\n" if @is_debug_mode | |
when "8" | |
@box8 = @player_o.dup #"O" | |
print "\nDEBUG: setting #{@box8_base} to #{@player_o}\n" if @is_debug_mode | |
when "9" | |
@box9 = @player_o.dup #"O" | |
print "\nDEBUG: setting #{@box9_base} to #{@player_o}\n" if @is_debug_mode | |
end # case | |
end # player_o_moves.each | |
# Print an updated display grid... | |
print "\n updated grid....\n" if @is_debug_mode | |
print_grid() | |
# Check if we have a winner yet... | |
if is_winner?("X") | |
print "\n\nPlayer #{@player_x} wins!\n\n" | |
if @production_run # continue to new game if in prodution_run mode... | |
print "\n--------------\n** New game **\n".color("#00CCCC") | |
reset_grid() | |
process_move() # start new game | |
end | |
elsif is_winner?("O") | |
print "\n\nPlayer #{@player_o} wins!\n\n" | |
if @production_run # continue to new gaae if in production_run mode... | |
print "\n--------------\n** New game **\n".color("#00CCCC") | |
reset_grid() | |
process_move() # start new game | |
end | |
elsif ( (@player_x_moves + @player_o_moves).length == 9) # no more moves left and no winner... | |
print "\n\nGame ended in a tie.\n\n" | |
if @production_run # contnue to new game if in production_run mode... | |
print "\n--------------\n** New game **\n".color("#00CCCC") | |
reset_grid() | |
process_move() # start new game | |
end | |
else | |
print "\n\nDEBUG: no winner yet...." if @is_debug_mode | |
end | |
end # method update_grid() | |
def is_winner?(player) | |
#check if each single array of player_moves contains any of the subsets the finite single array winning states | |
debug("\nlocation : at start of 'is_winner?' method.") if @is_debug_mode | |
winner = false | |
winning_grids = [ ["1","2","3"], ["4","5","6"], ["7","8","9"], ["1","4","7"], ["2","5","8"], ["3","6","9"], ["1","5","9"], ["3","5","7"] ] | |
(player == "X") ? (player_moves = @player_x_moves; player_icon = @player_x) : (player_moves = @player_o_moves; player_icon = @player_o) | |
print "\n\tDEBUG: player_moves passed into 'is_winner?': #{player_moves}" if @is_debug_mode | |
winning_grids.each do |win_pattern| | |
print "\n\tDEBUG: Checking against win_pattern #{win_pattern}" if @is_debug_mode | |
win_pattern_temp = win_pattern.dup #needed becauyse 'contains_all?' called in the next line destroys the second array passed-in | |
if ( contains_all?(player_moves, win_pattern_temp) ) | |
winner = true | |
end | |
end # winning_grids.each | |
print "\n\nDEBUG: winner state check: #{player_icon}\n" if @is_debug_mode | |
winner | |
end # method is_winner? | |
def is_odd?(number) | |
( (number % 2) == 0 ) ? false : true | |
end # method is_odd? | |
def contains_all? (first_array, second_array) | |
#method to return true if all the elements of second_array are contained within first_array | |
debug("\nlocation : at start of 'contains_all?' method.") if @is_debug_mode | |
#first_array.each { |e| if i = second_array.index(e) then second_array.delete_at(i) end} | |
first_array.each do |e| | |
print "\n\t\tDEBUG: first_array[e] = #{e}" if @is_debug_mode | |
if i = second_array.index(e) | |
second_array.delete_at(i) | |
print "\n\t\t\tDEBUG: second_array[#{e}] is at index #{i} and is being deleted." if @is_debug_mode | |
end | |
end | |
print "\n\t\tDEBUG: second_array.empty? = #{second_array.empty?}" if @is_debug_mode | |
second_array.empty? #if true then | |
end # method contains_all? | |
def is_valid_move?(player_move) | |
#puts "Player move = #{player_move}" | |
all_moves = @player_x_moves + @player_o_moves | |
player_move_temp = player_move.dup #needed becauyse 'contains_all?' called in the next line destroys the second array passed-in | |
not contains_all?(all_moves, player_move_temp.chars.to_a) #need the preceeding .chars here to get the .to_a to work | |
end # method is_valid_move? | |
def debug(message) | |
print "\n---------------------" | |
print "\nDEBUG:\n#{message}" | |
print "\n\tplayer_x_moves = #{@player_x_moves}" | |
print "\n\tplayer_o_moves = #{@player_o_moves}\n" | |
print_grid() | |
print "\n---------------------" | |
end # method debug(message) | |
def process_move() | |
all_moves = @player_x_moves + @player_o_moves | |
move_count = all_moves.length + 1 | |
debug("move count : #{move_count}\nlocation : at start of 'process_move' method.") if @is_debug_mode | |
if ( move_count <= 9 ) # there are only nine boxes on the grid... | |
is_odd?(move_count) ? ( player=@player_x ) : ( player=@player_o ) | |
print "\n\n#{move_count}. Player #{player} make your move [ #{@box1_base} - #{@box9_base} , q ]: " | |
player_move = gets.chomp #todo: validate input | |
if (player_move.upcase != "Q") | |
if ( ("1".."9").to_a.include? (player_move) ) | |
begin # begin..end section needed around the following exception code because there is a trailing 'else' from the above 'if' that needs to be clearly excluded from the ensure section below | |
raise DuplicateMoveError if ( not is_valid_move?(player_move) ) | |
(player == @player_x) ? ( @player_x_moves.push(player_move) ) : ( @player_o_moves.push(player_move) ) | |
rescue Exception => e | |
print "\n\tInvalid move: #{get_icon("base", player_move)} is already filled by player #{get_icon("move", player_move)} \n" | |
( print "\tDEBUG: " ; p e.backtrace.inspect if ( e.class == DuplicateMoveError ) ) if is_debug_mode | |
ensure | |
update_grid() | |
process_move() #continue the game | |
end # begin | |
# Original if-else-end logic replaced by begin..end section above. | |
# if ( is_valid_move?(player_move) ) | |
# (player == @player_x) ? ( @player_x_moves.push(player_move) ) : ( @player_o_moves.push(player_move) ) | |
# update_grid() | |
# process_move() #continue the game | |
# else | |
# print "\n\tInvalid move: #{get_icon("base", player_move)} is already filled by player #{get_icon("move", player_move)} \n" | |
# ( print "\tDEBUG: " ; p e.backtrace.inspect if ( e.class == DuplicateMoveError ) ) if is_debug_mode | |
# update_grid() | |
# process_move() #continue the game | |
# end | |
else | |
print "\n\tInvalid selection. Pick one of [ #{@box1_base} - #{@box9_base} , q ]\n" | |
update_grid() | |
process_move() #continue the game | |
end # if ( ("1".."9").to_a.include? (player_move) ) | |
else | |
print "\nQuiting game.\n\n" | |
exit | |
end # if (player_move.upcase != "Q") | |
else | |
#print "\n\nGame ended in a tie.\n\n" | |
update_grid() | |
end # if ( move_count <= 9 ) | |
end # method process_move() | |
def get_icon(type, box_selection) | |
case box_selection | |
when "1" | |
(type == "base") ? (return @box1_base) : (return @box1) | |
when "2" | |
(type == "base") ? (return @box2_base) : (return @box2) | |
when "3" | |
(type == "base") ? (return @box3_base) : (return @box3) | |
when "4" | |
(type == "base") ? (return @box4_base) : (return @box4) | |
when "5" | |
(type == "base") ? (return @box5_base) : (return @box5) | |
when "6" | |
(type == "base") ? (return @box6_base) : (return @box6) | |
when "7" | |
(type == "base") ? (return @box7_base) : (return @box7) | |
when "8" | |
(type == "base") ? (return @box8_base) : (return @box8) | |
when "9" | |
(type == "base") ? (return @box9_base) : (return @box9) | |
end | |
end # method get_icon | |
end # class Grid | |
class DuplicateMoveError < StandardError | |
# Creating my own exception... | |
end # class DuplicateMoveError | |
# RUN | |
production_run = true # set to true to have the main tic-tac-toe game run invoked... set to false to have the test runs (below) invoked... | |
# Main two player game... | |
if production_run | |
# Setup start empty game grid state.. | |
#grid = Grid.new(production_run, [], []) # so you can start the game in any state and continue... | |
# Test in-progress game state... | |
print "\n\nTest game in progress..." | |
player_x_moves = ["2", "4", "6", "7"] | |
player_o_moves = ["1", "3", "5"] | |
grid = Grid.new(production_run, player_x_moves, player_o_moves) # so you can start the game in any state and continue... | |
# Start or continue game... | |
grid.process_move() | |
end | |
#Test game grid states... | |
if ( not production_run ) | |
# Test player X wins... | |
print "\n\nTest player X wins..." | |
player_x_moves = ["1", "2", "3"] | |
player_o_moves = ["4", "5"] | |
grid = Grid.new(production_run, player_x_moves, player_o_moves) # so you can start the game in any state and continue... | |
print "--------------\n" | |
# Test player O wins... | |
print "\n\nTest player O wins..." | |
player_x_moves = ["2", "4", "6"] | |
player_o_moves = ["1", "5", "9"] | |
grid = Grid.new(production_run, player_x_moves, player_o_moves) # so you can start the game in any state and continue... | |
print "--------------\n" | |
# Test game tie... | |
print "\n\nTest game tie..." | |
player_x_moves = ["2", "4", "6", "7", "9"] | |
player_o_moves = ["1", "3", "5", "8"] | |
grid = Grid.new(production_run, player_x_moves, player_o_moves) # so you can start the game in any state and continue... | |
print "--------------\n" | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment