简体   繁体   中英

Ruby For loop isn't running as expected

I have this array of arrays:

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

and I am defining this method:

def won?(board)
  for x in WIN_COMBINATIONS
    win_index_1 = x[0]
    win_index_2 = x[1]
    win_index_3 = x[2]
    p1 = board[win_index_1]
    p2 = board[win_index_2]
    p3 = board[win_index_3]

    if p1 == 'X' && p2 == 'X' && p3 == 'X'
      return x
    else
      false
    end
  end
end

and when

board = [' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ']

The won? method returns every item in WIN_COMBINATIONS instead of false. I have no idea why and I would appreciate it if someone would please help.

In ruby each block , returns something even if you don't explicitly return a value, in your case the for block is returning the collection over you are iterating I think that your logic is correct but you need to make a small change, something like:

def won?(board)
  for x in WIN_COMBINATIONS
    win_index_1 = x[0]
    win_index_2 = x[1]
    win_index_3 = x[2]
    p1 = board[win_index_1]
    p2 = board[win_index_2]
    p3 = board[win_index_3]

    return x if p1 == "X" && p2 == "X" && p3 == "X"
  end
  false
end

The code above will return x if any of the sequences are valid, false in any other case (although you should follow the convention and return true or false if you are using the signature ? , other option is remove the ? and return nil instead of false). Hope this helps!

Every block of code returns a value even if there is no explicit return keyword. for loop returns itself. With board = [' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '] you never hit the return x statement and you get WIN_COMBINATIONS back. If you replace return false with return [] , it will allow you to avoid 'expected a collection that can be converted to an array with #to_ary or #to_a, but got false' . But Ruby has a bunch of nice methods to work with arrays .

By convention, methods that end with ?should return a boolean value. Your method returns an array when the if statement is true. In your case, I'd rename the method and use the find method to iterate through WIN_COMBINATIONS :

def find_win_combination(board)
  # You can use decomposition as in the next example `do |wi_0, wi_1, wi_2|`
  win_combination = WIN_COMBINATIONS.find do |combination|
    win_index_0 = combination[0]
    win_index_1 = combination[1]
    win_index_2 = combination[2]
    # If it's true the find method will return the win combination
    board[win_index_0] == "X" && board[win_index_1] == "X" && board[win_index_2] == "X"
  end

  # return an empty array if win_combination is not found
  win_combination || []
end
board = [' ', ' ', ' ', 'X', 'X', 'X', ' ', ' ', ' ']
> find_win_combination(board)
 => [3, 4, 5]

board = [' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ']
> find_win_combination(board)
 => []

If you need won? method to return a boolean method, just use any? method on WIN_COMBINATIONS . The result of any? will be returned as a result of any? will be returned as a result of won`?

def won?(board)
  # Here is a decomposition of each item of `WIN_COMBINATIONS` into three variables
  WIN_COMBINATIONS.any? do |wi_0, wi_1, wi_2|
    board[wi_0] == "X" && board[wi_1] == "X" && board[wi_2] == "X"
  end
end
board = [' ', ' ', ' ', 'X', 'X', 'X', ' ', ' ', ' ']
> won?(board)
 => true

board = [' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ']
> won?(board)
 => false

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM