Help with Ruby Tic Tac Toe Game

I am working on a tic tac toe game in the IRB but can only get the game
to partially work, depending on where the user makes a first move on the
board. I am using a hash but having trouble with some parts of the
program. I have attached a copy of the game. Can anyone please help me
debug program?

Thanks

Don’t ask me where I was with the head in but…
I couldn’t resist the tempation of doing another (in zilions) tic tac
toe game (in Ruby).
(next time I’ll try to take a breath, relax and do something more
“productive”).

I did it as an exercise because I found your code hard to follow.

My try was more “object oriented” (be whatever it means).
I’ve based some fragments on your code.

Well… just tell me something…
Are you trying to follow this code here?
http://www.jisaacks.com/ruby-tutorial-make-a-tic-tac-toe-game

Too much sleepy to go on improving the code.

Abinoam Jr.

On Sep 22, 2013, at 10:04 PM, John A. [email protected] wrote:

I am working on a tic tac toe game in the IRB but can only get the game
to partially work, depending on where the user makes a first move on the
board. I am using a hash but having trouble with some parts of the
program. I have attached a copy of the game. Can anyone please help me
debug program?

Learn pry instead of using IRB. < http://pryrepl.org/ > Your debugging
life will be much happier.

Abinoam Jr. wrote in post #1122229:

Don’t ask me where I was with the head in but…
I couldn’t resist the tempation of doing another (in zilions) tic tac
toe game (in Ruby).
(next time I’ll try to take a breath, relax and do something more
“productive”).

I did it as an exercise because I found your code hard to follow.

My try was more “object oriented” (be whatever it means).
I’ve based some fragments on your code.

GitHub - abinoam/tic_tac_toe: Tic Tac Toe game at console. A Ruby exercise.

Well… just tell me something…
Are you trying to follow this code here?
http://www.jisaacks.com/ruby-tutorial-make-a-tic-tac-toe-game

Too much sleepy to go on improving the code.

Abinoam Jr.

Hey Abinoam,
Thanks for the help. I saw your code, pretty good…
Yes… it was based on that code but I am trying to do mine with hashes.
A question for you: For example, let’s say at “12” and “22” there are
'X’s and the computer put a random choice at “31” - the computer’s
second turn it looks at row “11”, “12”, “13” first and sees that it has
a " (space) X (space)" pattern and returns ‘use random’ instead of
returning “XX (space)” and ‘last value block’ because it looks at the
scenario index 0 first, and never gets past that.

I need a way it looks at all the scenarios vs. the 0 indexed key, then
all the scenarios vs. the 2nd indexed key, etc.

This seems to work but I have not tested it carefully. It may have some
bugs and it is not very DRY.
I didn’t get any sleep last night so it is difficult for me to
concentrate
on this, now.
If you are interested, you can modify it as necessary.

The computer plays with a simple strategy not randomly.

h = {“x”=>[],“o”=>[]}
a,b = (1…9).to_a, (0…9).to_a
puts
p [b[8],b[1],b[6]].map{|f| f.to_s}
p [b[3],b[5],b[7]].map{|f| f.to_s}
p [b[4],b[9],b[2]].map{|f| f.to_s}
puts
puts “Your turn, x. Choose the number of your position.”
x = gets.chomp.to_i
h[“x”] << x
b[x] = “x”
a -= [x]
p [b[8],b[1],b[6]].map{|f| f.to_s}
p [b[3],b[5],b[7]].map{|f| f.to_s}
p [b[4],b[9],b[2]].map{|f| f.to_s}
puts

4.times do
#Random selection
o = a.sample

If 1, choose open direction if possible.

open_d = a.combination(2).to_a.select{|e,f|
h[“o”][-1]+e+f==15}.flatten[0] if h[“o”].size>0
o = open_d || o

If 2 x’s in line, block it.

block_it = h[“x”].combination(2).to_a.map{|u,w| 15-u-w}.select{|e|
b[e]==e && e!=0}
o = b[block_it[0]] if block_it.size>0 && b[block_it[0]]==block_it[0]

If 2 o’s in line, choose third in line for the win.

win = h[“o”].combination(2).to_a.map{|u,w| 15-u-w}.select{|e| b[e]==e
&&
e!=0}
o = b[win[0]] if win.size > 0 && b[win[0]]==win[0]

h[“o”] << o
b[o] = “o”
a -= [o]
p [b[8],b[1],b[6]].map{|f| f.to_s}
p [b[3],b[5],b[7]].map{|f| f.to_s}
p [b[4],b[9],b[2]].map{|f| f.to_s}
puts
if h[“o”].map{|f| f}.combination(3).to_a.map{|u| u.inject{|c,d|
c+d}}.include?(15)
p “o wins”
break
end

puts “Your turn, x. Choose the number of your position.”
x = gets.chomp.to_i
p x
h[“x”] << x
b[x] = “x”
a -= [x]
if h[“x”].map{|f| f}.combination(3).to_a.map{|u| u.inject{|c,d|
c+d}}.include?(15)
p “x wins”
p [b[8],b[1],b[6]].map{|f| f.to_s}
p [b[3],b[5],b[7]].map{|f| f.to_s}
p [b[4],b[9],b[2]].map{|f| f.to_s}
break
end
end

Harry

Hey John,

Please don’t get angry with me for what I’ll tell you.

The real problem with your code is the “foundation”.
You’re trying to build a good house over pure beach sand.
Someday it’ll fall.

I asked about the blog because it says (almost as an ADVICE of DANGER):

“I started playing around with Ruby recently and it is a really fun
language, albeit the syntax can look a little strange at first.”
started *** playing *** recently *** look…strange *** at first
http://www.jisaacks.com/ruby-tutorial-make-a-tic-tac-toe-game

So, you’re trying to make grow a code over something the guy did
almost 2 years ago when he was LEARNING Ruby.
(I bet he is probably much better now)

The blog is beautiful (graphically) but the code is not as beautiful as
it.
The reason is on his resum. He is pretty good at graphics design (as he
says).
http://www.jisaacks.com/resume

So, it’s difficult to help you go on.
You are looking at a Ruby code without the “good part” of Ruby.
It seems more like a C program “translated” into Ruby.

(Note: I’m not claiming that my code is pretty much different!)

But… just to not disappoint you, I’ll try to do a step-by-step
refactor.
And let’s see if somebody more experienced than me can agree and give
some better advice.

Original code:
def finding_patterns
temp_array = @senarios.map {|element| element == ‘X’ || element ==
‘O’ ? element : " "}
new_array = temp_array.each_slice(3)
times = 0
while times < 12
poss_match = new_array.next.join
@master_hash.each do |key, value|
if key == poss_match
return value,times
end
end
times =+ 1 #!!! this was in
the wrong place and messed up the pattern finding
end
“No finding” #!!!
iterating through @scenarios and @master_hash, it might be better
end #!!! to give the
index counter a name that reflects this

First fall… this kind of iteration using “while” conditionals

with a limit is not usual.

If the array changes size you have to change that “12” limit

We have to use some iterator and with_index

def finding_patterns
temp_array = @senarios.map {|element| element == ‘X’ || element ==
‘O’ ? element : " "}
new_array = temp_array.each_slice(3)
new_array.map.with_index do |ary, ary_ix|
#times = 0
#while times < 12
#poss_match = new_array.next.join
poss_match = ary.join # the “next” array is already at ‘ary’ var
@master_hash.each do |key, value|
if key == poss_match
return value,times
end
end
# times =+ 1 #!!! this was
in the wrong place and messed up the pattern finding
end
“No finding” #!!!
iterating through @scenarios and @master_hash, it might be better
end #!!! to give the
index counter a name that reflects this

Here you’re having the problem because you ‘return’ at the first

finding “breaking” the iteration

What you really want is to ‘select’ ALL the matching itens. Is it

this?

def finding_patterns
temp_array = @senarios.map {|element| element == ‘X’ || element ==
‘O’ ? element : " "}
new_array = temp_array.each_slice(3)
new_array.map.with_index do |ary, ary_ix|
poss_match = ary.join
# @master_hash.each do |key, value|
@master_hash.select do |key, value|
# if key == poss_match
# return value,times
# end
key == poss_match
end
end
“No finding” #!!!
iterating through @scenarios and @master_hash, it might be better
end #!!! to give the
index counter a name that reflects this

But this gives you a Array of 2-elements Array (or an empty array).

And you’re original return value is [value, times]

So you have to “map” these arrays to the proper “format”.

If 'finding_patterns" return an empty Array then it is the "No

finding"

def finding_patterns
temp_array = @senarios.map {|element| element == ‘X’ || element ==
‘O’ ? element : " "}
new_array = temp_array.each_slice(3)
new_array.map.with_index do |ary, ary_ix|
poss_match = ary.join
@master_hash.select do |key, value|
key == poss_match
end.map { |key, value| [value, ary_ix] } # Because ary_ix is the
“new” ‘times’
end.flatten
#“No finding” #!!!
iterating through @scenarios and @master_hash, it might be better
end #!!! to give the
index counter a name that reflects this

For testing, you should just comment the final lines

#print “\n\nYou are X. Please go first.”
#TicTacToeGame.new.game_play

fire up an IRB session and

load “./tic_tac_toe.rb”

=> true

t = TicTacToeGame.new

=> #<TicTacToeGame:0x000000026186c0 @master_hash={“XXX”=>“winner_X”,

“OOO”=>“winner_O”, "XX “=>“last_value_win”, “X X”=>“middle_value_win”,
" XX”=>“first_value_win”, "OO “=>“last_value_block”, “O
O”=>“middle_value_block”, " OO”=>“first_value_block”, "X
“=>“last_two_values_add”, " X “=>“either_end_values_add”, "
X”=>“first_two_values_add”}, @game_board={“11”=>” “, “12”=>” “,
“13”=>” “, “21”=>” “, “22”=>” “, “23”=>” “, “31”=>” “, “32”=>” “,
“33”=>” "}, @senarios=[“11”, “12”, “13”, “21”, “22”, “23”, “31”, “32”,
“33”, “11”, “21”, “31”, “12”, “22”, “32”, “13”, “23”, “33”, “11”,
“22”, “33”, “13”, “22”, “31”], @players={:X=>“X”, :O=>“O”}, @turn=:X>

t.send(:finding_patterns)

t.send(:player_choice) # Choose any line and column

t.send(:finding_patterns) # See the patterns that it finds now

and see the results

Well… done?

Not yet.

Now that we changed the return value of this method we have to change

the code wherever it is used.

For example, the code bellow

#!!! here is the actual code for computer_choice
#!!! the logic was sequential in your code but it needed to be 

nested
value, times = finding_patterns # <— This is not possible anymore
location = go_for_the_win(value, times)
if location == nil
location = perform_block(value, times)
if location == nil #!!! debug statement
location = add_to_existing(value, times)
if location == nil
location = random_move
end
end
end
marker_placement_hash(location, ‘O’)
marker_placement_array(location, ‘O’)

At the code above, now you have to iterate over each “pattern”

instead of doing it once.

Do you really want to go on with this?

Brave guy! But…

“The paths of the ruby winsdom to find you need”

“But alone now to go you have”

“Because your friend’s time now short it is”

“And dangerous path your friend thinks you choose”

“Perhaps other day to join you again he will”

Hope it helps you!
Send us any news about the code!
Abinoam Jr.

On Thu, Sep 26, 2013 at 1:41 PM, [email protected] wrote:

Am 24.09.2013 15:26, schrieb John A.:

Yes… it was based on that code but I am trying to do mine with hashes.
A question for you: For example, let’s say at “12” and “22” there are
'X’s and the computer put a random choice at “31”
[…]

Hashes…? A two-dimensional array would be more appropriate.

Regards,
Marcus

I couldn’t agree more!

Look at the “initial commit”

Abinoam Jr.

This is about the same as the code in my previous post, but I removed
some
unnecessary stuff, used more descriptive variable names, and slightly
improved the computer’s strategy.
There is no error checking, so don’t input numbers that are not
available
or letters, etc.
Sorry, no hashes or 2D arrays.

x_moves, o_moves = [],[]
open_spaces,grid = (1…9).to_a, (0…9).to_a
puts
p [grid[8],grid[1],grid[6]].map{|f| f.to_s}
p [grid[3],grid[5],grid[7]].map{|f| f.to_s}
p [grid[4],grid[9],grid[2]].map{|f| f.to_s}
puts
puts “You are x. Please go first. Choose a numbered position.”
x = gets.chomp.to_i
x_moves << x
grid[x] = “x”
open_spaces -= [x]
p [grid[8],grid[1],grid[6]].map{|f| f.to_s}
p [grid[3],grid[5],grid[7]].map{|f| f.to_s}
p [grid[4],grid[9],grid[2]].map{|f| f.to_s}
puts

4.times do
#Random selection
o = open_spaces.sample

o = 5 if o_moves.size==0 && x_moves[-1] != 5
o = 2 if o_moves.size==0 && x_moves[-1] == 5

If there is an open direction, go for it.

open_direction = []
open_direction = open_spaces.combination(2).to_a.select{|e,f|
o_moves[-1]+e+f==15}.flatten if o_moves.size>0
o = open_direction[0] if open_direction.size>0

If 2 x’s in line, block it.

block_it = x_moves.combination(2).to_a.map{|u,w| 15-u-w}.select{|e|
e>0
&& grid[e]==e}
o = block_it[0] if block_it.size>0

If 2 o’s in line, choose third in line for the win.

win = o_moves.combination(2).to_a.map{|u,w| 15-u-w}.select{|e| e>0 &&
grid[e]==e}
o = win[0] if win.size > 0

o_moves << o
grid[o] = “o”
open_spaces -= [o]
puts
p [grid[8],grid[1],grid[6]].map{|f| f.to_s}
p [grid[3],grid[5],grid[7]].map{|f| f.to_s}
p [grid[4],grid[9],grid[2]].map{|f| f.to_s}
puts
if o_moves.combination(3).to_a.map{|a,b,c| a+b+c}.include?(15)
p “o wins”
break
end

puts “Your turn.”
x = gets.chomp.to_i
x_moves << x
grid[x] = “x”
open_spaces -= [x]
if x_moves.combination(3).to_a.map{|a,b,c| a+b+c}.include?(15)
p “x wins”
p [grid[8],grid[1],grid[6]].map{|f| f.to_s}
p [grid[3],grid[5],grid[7]].map{|f| f.to_s}
p [grid[4],grid[9],grid[2]].map{|f| f.to_s}
break
end
end

Harry

Am 24.09.2013 15:26, schrieb John A.:

Yes… it was based on that code but I am trying to do mine with hashes.
A question for you: For example, let’s say at “12” and “22” there are
'X’s and the computer put a random choice at “31”
[…]

Hashes…? A two-dimensional array would be more appropriate.

Regards,
Marcus

Thanks for help now my tic tac toe 2 player is working