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]
-
2Please provide the examples of these arrays.Aleksei Matiushkin– Aleksei Matiushkin2016年01月11日 09:24:46 +00:00Commented Jan 11, 2016 at 9:24
-
2Give example input-output. Show what you have tried so far.ndnenkov– ndnenkov2016年01月11日 09:24:55 +00:00Commented 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 :pTal C– Tal C2016年01月11日 09:37:14 +00:00Commented Jan 11, 2016 at 9:37
-
1Please make ur question more clear. Give an example if possible.Harry Bomrah– Harry Bomrah2016年01月11日 09:40:54 +00:00Commented 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?Stefan– Stefan2016年01月11日 09:42:44 +00:00Commented Jan 11, 2016 at 9:42
5 Answers 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 returnstrue
.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 becomesvalues_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. eitherXXX
orOOO
You can replace select
with find
if you just want to retrieve the first winning combination.
Comments
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]
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
"X"
and "Y"
?values = board.values_at(*combination); values == %w(X X X) || values == %w(O O O)
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
/(\w)1円1円/
.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.)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.