1

This is for a tic tac toe game. I have an array board with nine string elements, and a nested array WIN_COMBINATIONS with position combinations from board:

board = ["X", "X", "X", " ", " ", " ", " ", " ", " "]
WIN_COMBINATIONS = [
 [0, 1, 2],
 [0, 3, 6],
 [0, 4, 8],
 [3, 4, 5],
 [6, 7, 8],
 [6, 4, 2],
 [1, 4, 7],
 [2, 5, 8]
]

How do I choose the array combinations from board that are all "X" or all "O" using the combinations found in WIN_COMBINATIONS?

For example a different board than the one above in which X wins in the right diagonal.

board = ["X", "O", "X", "O", "X", "O", "X", "X", "O"]
# X | O | X
# ---+---+---
# O | X | O
# ---+---+---
# X | X | O
won?(board) #=> [2,4,6]
Stefan
115k14 gold badges156 silver badges231 bronze badges
asked Jan 11, 2016 at 9:23
10
  • 2
    Please provide the examples of these arrays. Commented Jan 11, 2016 at 9:24
  • 2
    Give example input-output. Show what you have tried so far. Commented Jan 11, 2016 at 9:24
  • Ok added the arrays will add the code I put which is very wrong/bad/beginner-y as you may see :p Commented Jan 11, 2016 at 9:37
  • 1
    Please make ur question more clear. Give an example if possible. Commented Jan 11, 2016 at 9:40
  • 1
    @TalC thanks for adding the arrays, but I still don't understand your last sentence. Could you provide the expected output, i.e. the combinations you want to choose? Commented Jan 11, 2016 at 9:42

5 Answers 5

5

A slight variation of ndn's answer:

board = %w(X O X
 O X O
 X X O)
WIN_COMBINATIONS.select { |c| board.values_at(*c).join =~ /XXX|OOO/ }
#=> [[6, 4, 2]]

Explanation:

  • select returns all elements for which the block returns true.

  • values_at returns the values at the specified indices:

    board.values_at(*[0, 1, 2])
    #=> ["X", "O", "X"]
    

    * converts the array to an argument list, so the above becomes values_at(0, 1, 2)

  • join returns a string with the concatenated elements:

    ["X", "O", "X"].join
    #=> "XOX"
    
  • =~ checks if the string matches the regular expression /XXX|OOO/, i.e. either XXX or OOO

You can replace select with find if you just want to retrieve the first winning combination.

answered Jan 11, 2016 at 10:00

Comments

2

Edit Code incorporates Stefan's suggestion.

Do this once:

WIN_COMBINATIONS.each(&:sort!)

Then,

h = board.each_index.group_by{|i| board[i]}
# => {"X"=>[0, 1, 2], " "=>[3, 4, 5, 6, 7, 8]}
WIN_COMBINATIONS.find{|a| (h["X"] & a) == a or (h["O"] & a) == a}
# => [0, 1, 2]
answered Jan 11, 2016 at 10:06

2 Comments

You might want to explain &.&, it's quite new.
Actually, &. is not necessary, because (nil & a) == a evaluates to false == a
1
WIN_COMBINATIONS.find do |combination|
 values_at_positions = board.values_at(*combination).uniq
 values_at_positions.size == 1 and ['X', 'O'].include?(*values_at_positions)
end # => [0, 1, 2]
answered Jan 11, 2016 at 9:43

3 Comments

Why "X" and "Y"?
A more straight-forwand (and shorter) check would be values = board.values_at(*combination); values == %w(X X X) || values == %w(O O O)
@Stefan, I didn't think it through initially. Now it would be practically copying your answer.
1

Just out of curiosity (a slightly updated @Stefan’s answer):

WIN_COMBINATIONS.index do |wc|
 board.values_at(*wc).join =~ /(?<l>\w)\k<l>\k<l>/
 # or, as suggested by @Stefan: board.values_at(*wc).join =~ /(\w)1円1円/
end
#⇒ 5

Here we match the combinations to three same symbols, which is likely the most semantically correct interpretation of tic-tac-toe game.

answered Jan 11, 2016 at 10:23

2 Comments

I like your regex approach, but I'd use /(\w)1円1円/.
@Stefan Indeed, updated, thanks. I left index, though, to make it more flexible (to have a backward connection to WIN_COMBINATIONS array, since it probably makes it easier to debug the wrong values there.)
0

You can try this.

def won?(board)
 xpos = []
 opos = []
 who_won = nil;
 board.each_with_index{|x,i| xpos << i if x == "X" }
 board.each_with_index{|x,i| xpos << i if x == "O" }
 WIN_COMBINATIONS.each do |com|
 temp = com & xpos
 who_won = "X" if temp == com
 temp = com & opos
 who_won = "O" if temp == com
 break if !who_won.nil?
 end
 return who_won
end
won?(board) #=> X if x wins, O if O wins. nil if no body wins.

Its untested but should work.

answered Jan 11, 2016 at 10:00

4 Comments

won? doesn't seem to return anything.
need to pass the board.
@Stefan i missed return statement.
@Stefan But i really like your answer. its really short and sweet.

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.