First and foremost, I want to acknowledge the help of several people at the Flatiron School community program for walking me through this one. Thanks again to @thomasshines, @lord_of_yoghurt and @theredshirtman for the help and encouragement. It was a painful experience but extremely rewarding.

Overall, I learned much about enumeration, the find method, and nested arrays. So, let’s get to it. Also, spoiler alerts as you really should give it the old college try. I went two days and got extremely close before realizing it was time to reach out. During that time I learned more about how other enumerators worked. This method of learning is painful but rewarding.

My first mistake was in forgetting how to access the nested array.

Let’s look back at where I ended day 2.

1 2 3 4 5 |
def won?(board) WIN_COMBINATIONS.each do |i| end end |

Each will iterate over each of the arrays in the WIN_COMBINATIONS constant. Let’s look at that again.

1 2 3 4 5 6 7 8 9 10 |
WIN_COMBINATIONS =[ [0,1,2], [3,4,5], [6,7,8], [0,3,6], [1,4,7], [2,5,8], [0,4,8], [2,4,6] ] |

So, what was first pointed out is that I need to think of what is being iterated through. Therefore my ending method from Day 2 should first be changed to this:

1 2 3 4 5 |
def won?(board) WIN_COMBINATIONS.each do |win_combo| end end |

A small, but significant change as it tells me what I’m looking at. When win_combo is called it is the first of the nested arrays. And

1 2 3 4 5 |
def won?(board) WIN_COMBINATIONS.each do |win_combo| win_combo[0] end end |

that win_combo[0] will pull the first index spot of each of the nested arrays.

So, this is a part I really got stuck on. Looking back, the each method will iterate through the top level of the array, which is WIN_COMBINATIONS. When we add the [0] part that is accessing the next level down. To solve this we must be able to access the individual indexes INSIDE the nested arrays.

Next, I had settled on select as the method that would work. select however would not work as it will go through the entire array and return everything that returns true as an array. The method that will work best is find. find (or detect for those who want to use that. They are the same.) will go through and stop iterating once it has satisfied the conditions given. So, here is where we are so far:

1 2 3 4 5 |
def won?(board) WIN_COMBINATIONS.find do |win_combo| end end |

Now, at this point we have enough knowledge to solve the first part. We know that this:

1 |
win_combo[0] |

will iterate through each of the nested arrays and return back the first index of each. Therefore, win_combo[1] and win_combo[2] will grab the last few indexes.

What we need to satisfy in this method is that three indexes in the WIN_COMBINATIONS nested arrays return all three and that they match, giving us three in a row. Right now, it does not matter if X or O wins, only that they match. To do this we need to use some boolean logic.

So, if win_combo[0] is equal to win_combo[1] and if win_combo[0] is also equal to win_combo[2], that means all three match. So, let’s put this logic into our method, remembering that we are also passing in a board that the users are going to update as the play. That board is an array. Here’s an example:

board = [“X”, “X”, “X”, ” “, ” “, ” “, ” “, ” “, ” “]

and in this example, there are 3 X’s in the top row to win. And here is the updated board:

1 2 3 4 5 |
def won?(board) WIN_COMBINATIONS.find do |win_combo| board[win_combo[0]] == board[win_combo[1]] && board[win_combo[0]] == board[win_combo[2]] end end |

Let’s read through the code here.

The find method comes to the first array in the WIN_COMBINATIONS constant which is [0, 1, 2]. At this point we are inside the block. We get to the first part of the logic within:

1 |
board[win_combo[0]] |

And if we look at the board array above we see it will return “X”.

And it then asks if it is equal to the next part,

1 |
board[win_combo[1]] |

This will also return “X”. So far, so good! At this point we come to the && statement. This tells us that for this to come back true we will need both sides of the equation to come back true. So far, the left hand side is true. Will the right hand side be? Next part:

1 |
board[win_combo[0]] |

OK, we already see this before and it was “X”. We use this as sort of an anchor. If the first side has returned true, we could use either index to compare the last index. So, last one:

1 |
board[win_combo[2]] |

And this one is also “X”. This means we have a true result and find will stop iterating. It will return the [0, 1, 2] array.

Let’s see if it works for the middle row, which would be [3, 4, 5]…

Here’s the board:

1 |
board = [" ", " ", " ", "X", "X", "X", " ", " ", " "] |

So, let’s read through it our code again, starting from the top.

1 |
board[win_combo[0]] |

Will return ” “. OK,

1 |
board[win_combo[1]] |

Will return ” “. OK, and…

1 |
board[win_combo[2]] |

Will return ” “. AND IT STOPS ITERATING AND RETURNS [0, 1, 2]. OK, not the time to panic. This actually makes logical sense as ” ” == ” ” and ” ” == ” “. We need a way to tell Ruby to not accept empty spaces as part of our logic. Thankfully, we built this method before!

1 2 3 |
def position_taken?(board, index) !(board[index].nil? || board[index] == " ") end |

Just a quick explanation. The method here says if the board[index] is nil or empty that it is not taken. We want our method to only consider taken spots. So, I originally wanted to slam this on the end of every one of the board[win_combo[*]] pieces. That was the wrong impulse. Instead, what we want to do is add it to our logic. We are asking Ruby to ask if it is true that the positions are taken. So, let’s add it and see what happens.

1 2 3 4 5 |
def won?(board) WIN_COMBINATIONS.find do |win_combo| board[win_combo[0]] == board[win_combo[1]] && board[win_combo[0]] == board[win_combo[2]] && position_taken?(board, win_combo[1]) end end |

OK, let’s read through it quickly. We already now that the first array, [0, 1, 2] had all returned ” ” and ruby originally found that true. Now, it will fail the last condition as the ” ” is false for position_taken?. Awesome!

So, we now begin iterating through [3, 4, 5]. Remember we start back at the 0 column in the index:

1 |
board[win_combo[0]] |

This returns “X”

1 |
board[win_combo[1]] |

This returns “X”

1 |
board[win_combo[2]] |

And this returns “X”! And it stops! [3, 4, 5] is the returned array as it matches the won? conditions. And that is it.

If you have questions, please ask them in comments. I hope this helps everyone struggling to understand this. I can take very little credit for solving it on my own, though I tried like hell, but I hope my write up helps answer the questions I had and that you may have.