Reputation: 29
I am making a simple Tic tac toe game where the rows are represented by three arrays and columns are items in those arrays. For my computer choice I use two separate functions to get the row and column.
$first = ["-","-","-"]
$secound = ["-","-","-"]
$third = ["-","-","-"]
def get_row
row = nil
case rand(3)
when 0
row = $first
when 1
row = $secound
else
row = $third
end
row
end
def get_col
col = nil
case rand(3)
when 0
col = 0
when 1
col = 1
else
col = 2
end
col
end
I then use a third function that keeps generating a new "guess" until it finds an empty spot, at which point it marks an 0.
def computer_guess
temp = false
try = get_row[get_col]
while temp == false
if try == "-"
get_row[get_col] = "O"
temp = true
else
try = get_row[get_col]
end
end
end
My problem is that i cant return the guess to the same position that i check for validity. Is the there a way to sync these or do i need a different approach?
Upvotes: 1
Views: 72
Reputation: 114228
The problem with your approach is that get_row[get_col]
returns a random element every time it is called, so likewise get_row[get_col] = "O"
will set a random element to "O"
. You inspect one element and then set another.
You could fix this quickly by modifying the retrieved element in-place:
if try == "-"
try.replace("O")
# ...
But semantically, I don't like that fix very much. Thinking of a tic-tac-toe board, I'd rather assign an "O"
to the free spot than transforming the existing placeholder from "-"
into "O"
.
Did you notice that your get_row
method returns an array whereas get_col
returns an index? I think this mixture of arrays and indices makes your code a bit convoluted.
It's much easier (in my opinion) to access both, row and column via an index.
To do so, you could put your three rows into another array:
$board = [
['-', '-', '-'],
['-', '-', '-'],
['-', '-', '-']
]
The first row can be accessed via $board[0]
and its first element via $board[0][0]
. To set an element you'd use: $board[1][2] = 'O'
(this sets the middle row's right-most element to "O"
). Of course, you can also use variables, e.g. $board[row][col]
.
With this two-dimensional array, your computer_guess
could be rewritten using just two random indices: (get_row
and get_col
aren't needed anymore)
def computer_guess
loop do
row = rand(3) # random row index
col = rand(3) # random column index
if $board[row][col] == '-' # if corresponding spot is "-"
$board[row][col] = 'O' # set that spot to "O"
break # and break out of the loop
end
end
end
Note however that "blindly" guessing spots until you find a free one might not be the best approach.
Instead, you could generate a list of "free" spots. This can be done by first generating an array of all coordinates1 and then select
-ing those row/col pairs whose spot is "-"
:
def free_spots
coordinates = [0, 1, 2].product([0, 1, 2])
coordinates.select { |row, col| $board[row][col] == '-' }
end
Now you just have to chose a random pair (via sample
) and set the corresponding spot to "O"
:
def computer_guess
row, col = free_spots.sample
$board[row][col] = 'O'
end
1 Array#product
returns the Cartesian product of the given arrays. It's an easy way to get all pairs:
[0, 1, 2].product([0, 1, 2])
#=> [[0, 0], [0, 1], [0, 2],
# [1, 0], [1, 1], [1, 2],
# [2, 0], [2, 1], [2, 2]]
Upvotes: 1
Reputation: 1333
when there is only one available spot, it might take time to guess it.
So it might be better to collate the available spots and get a random value from there:
def computer_guess
free_spots=[]
free_spots.concat($first.each_with_index.map{|x,i|x=='-' ? ['$first',i] : nil}.compact)
free_spots.concat($secound.each_with_index.map{|x,i|x=='-' ? ['$secound',i] : nil}.compact)
free_spots.concat($third.each_with_index.map{|x,i|x=='-' ? ['$third',i] : nil}.compact)
try=free_spots.sample
eval"#{try[0]}[#{try[1]}]='O'" unless try.nil?
end
Upvotes: 1
Reputation: 2543
Here is another approach. This code imitates game computer against itself. You can easily replace one computer's move with user input.
ROWS = 3
COLUMNS = 3
MAX_TURNS = ROWS * COLUMNS
def random_cell
{ row: rand(ROWS), column: rand(COLUMNS) }
end
def empty_random_cell(board)
while true
cell = random_cell
return cell if board_value(board, cell).nil?
end
end
def board_value(board, cell)
board[cell[:row]][cell[:column]]
end
def turn(board, cell, value)
row = cell[:row]
column = cell[:column]
board[row][column] = value
end
def print_board(board)
ROWS.times do |row|
COLUMNS.times do |column|
value = board[row][column]
print value.nil? ? '_' : value
print ' '
end
puts
end
end
board = Array.new(ROWS) { Array.new(COLUMNS) }
print_board(board)
turns_counter = 0
while true
cell = empty_random_cell(board)
turn(board, cell, 'X')
turns_counter += 1
break if turns_counter == MAX_TURNS
cell = empty_random_cell(board)
turn(board, cell, '0')
turns_counter += 1
break if turns_counter == MAX_TURNS
end
print_board(board)
Upvotes: 1