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]
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.

Sign up to request clarification or add additional context in comments.

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]

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]

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.

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.

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

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.