Forum: Ruby Help with Ruby Tic Tac Toe Game

7a573de916076211e72f6a54802609dc?d=identicon&s=25 John A. (john_a54)
on 2013-09-23 05:04
Attachment: fix_tic_tac_toe.txt (9 KB)
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
Aa082c8b00a50928e5860dcd70bf2368?d=identicon&s=25 tamouse m. (tamouse_m)
on 2013-09-23 06:41
(Received via mailing list)
On Sep 22, 2013, at 10:04 PM, John A. <lists@ruby-forum.com> 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.
09a32175057418748822c587ac08c429?d=identicon&s=25 Abinoam Jr. (abinoampraxedes_m)
on 2013-09-24 09:25
(Received via mailing list)
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.

https://github.com/abinoam/tic_tac_toe

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

Too much sleepy to go on improving the code.

Abinoam Jr.
7a573de916076211e72f6a54802609dc?d=identicon&s=25 John A. (john_a54)
on 2013-09-24 15:26
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.
>
> https://github.com/abinoam/tic_tac_toe
>
> Well... just tell me something...
> Are you trying to follow this code here?
> http://www.jisaacks.com/ruby-tutorial-make-a-tic-t...
>
> 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.
09a32175057418748822c587ac08c429?d=identicon&s=25 Abinoam Jr. (abinoampraxedes_m)
on 2013-09-24 17:56
(Received via mailing list)
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-t...

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.
2f4d4f9c35ea851bffb9a9cc2e086365?d=identicon&s=25 Harry Kakueki (Guest)
on 2013-09-25 06:33
(Received via mailing list)
>
>
> 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
15000f55138ae94b0f362ed7c625461a?d=identicon&s=25 unknown (Guest)
on 2013-09-26 18:42
(Received via mailing list)
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
09a32175057418748822c587ac08c429?d=identicon&s=25 Abinoam Jr. (abinoampraxedes_m)
on 2013-09-26 20:29
(Received via mailing list)
On Thu, Sep 26, 2013 at 1:41 PM,  <sto.mar@web.de> 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"

https://github.com/abinoam/tic_tac_toe/commit/111b...

Abinoam Jr.
2f4d4f9c35ea851bffb9a9cc2e086365?d=identicon&s=25 Harry Kakueki (Guest)
on 2013-09-27 09:27
(Received via mailing list)
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
Please log in before posting. Registration is free and takes only a minute.
Existing account

NEW: Do you have a Google/GoogleMail, Yahoo or Facebook account? No registration required!
Log in with Google account | Log in with Yahoo account | Log in with Facebook account
No account? Register here.