New at Ruby so excuse the poor code. I would like to iterate through the multidimensional array WIN_COMBINATIONS
and check whether at least one array has all of its elements equal to 'X'
or all equal to 'O'
. If so, return the matched array. This is done through the won?
function but it seems to only be returning the entire multidimensional array. Any assistance would be appreciated.
class TicTacToe
WIN_COMBINATIONS = [
[0,1,2], # top_row
[3,4,5], # middle_row
[6,7,8], # bottom_row
[0,3,6], # left_column
[1,4,7], # center_column
[2,5,8], # right_column
[0,4,8], # left_diagonal
[6,4,2] # right_diagonal
]
def initialize
@board = Array.new(9, " ")
end
def display_board
puts " #{@board[0]} | #{@board[1]} | #{@board[2]} "
puts "-----------"
puts " #{@board[3]} | #{@board[4]} | #{@board[5]} "
puts "-----------"
puts " #{@board[6]} | #{@board[7]} | #{@board[8]} "
end
def input_to_index(board_position)
user_input = board_position.to_i
user_input - 1
end
def move(board_index, player_token = 'X')
@board[board_index] = player_token
end
def position_taken?(board_position)
if @board[board_position] == ' '
false
else
true
end
end
def valid_move?(board_position)
if board_position >= 0 and board_position <= 8
if @board[board_position] == ' '
true
end
else
false
end
end
def current_player
turn_count % 2 == 0 ? "X" : "O"
end
def turn_count
@board.count{|token| token == "X" || token == "O"}
end
def turn
puts "Select your move (1-9)\n"
move = gets.chomp
move_index = input_to_index(move)
if valid_move?(move_index)
token = current_player
move(move_index, token)
display_board
else
puts "Select your move (1-9)\n"
move = gets.chomp
end
end
def won?
WIN_COMBINATIONS.each do |combinations|
if combinations.all? {|combination| combination == 'X' or combination == 'O'}
combinations
else
false
end
end
end
def draw?
if full? and !won?
true
elsif won?
false
else
false
end
end
def over?
end
def winner
end
def play
end
end
[...] it seems to only be returning the entire multidimensional array.
There are several issues with your attempted solution:
WIN_COMBINATIONS
is an array of indices. These indices are numeric, so they will never be 'X'
or 'O'
. You have to check whether their corresponding values are 'X'
or 'O'
.
or
is a control-flow operator intended for do_this or fail
scenarios. The boolean "or" operator is ||
. Using or
instead of ||
might work but may have unexpected results due to its lower precedence. You almost always want ||
.
The expression array.all? { |element| element == 'X' || element == 'O' }
checks whether all elements are either 'X'
or 'O'
. It would be true
for ['X','O','O']
and false
for ['X',' ','O']
. That's because you put the conditional inside the block. What you want is to check whether the elements are all 'X'
, or all 'O'
:
array.all?('X') || array.all?('O')
Your method's return value is the result of WIN_COMBINATIONS.each { ... }
and Array#each
always returns the array itself (i.e. WIN_COMBINATIONS
) regardless of the blocks' result. To get the first element matching a condition, use find
.
Let's apply all this to your code. Given this board:
@board = %w[
X - O
O X -
- - X
]
You could get the first matching combination via:
WIN_COMBINATIONS.find do |indices|
values = @board.values_at(*indices)
values.all?('X') || values.all?('O')
end
#=> [0, 4, 8]
values_at
returns the values for the corresponding indices (*
transforms the indices
array to a list of arguments, so values_at(*[0,1,2])
becomes values_at(0,1,2)
). The block's 2nd line then checks whether these values are all 'X'
, or all 'O'
. Once this evaluates to true
, the loop breaks and find
returns the matching element. (or nil
if there was no match)