Create a gist now

Instantly share code, notes, and snippets.

Start with a simple data structure
---------------------------------------------------------------------
data = [[nil,nil,nil],
[nil,nil,nil],
[nil,nil,nil]]
---------------------------------------------------------------------
Gives us the sort of accessors we want:
---------------------------------------------------------------------
data[1][1] = :X
data[1][0] = :O
data[0][0] = :X
data[2][2] = :O
---------------------------------------------------------------------
With some display code, we can see the effects:
---------------------------------------------------------------------
puts data.map { |row| row.map { |e| e || " " }.join("|") }.join("\n")
*******************************************************OUTPUT********
X| |
O|X|
| |O
---------------------------------------------------------------------
We also need a bit of code for swapping between players.
---------------------------------------------------------------------
players = [:X, :O].cycle
players.next #=> :X
players.next #=> :0
players.next #=> :X
players.next #=> :0
---------------------------------------------------------------------
If we wrap a simple loop around the code we've seen so far, we can make
an interactive prompt that lets players enter their moves.
---------------------------------------------------------------------
board = [[nil,nil,nil],
[nil,nil,nil],
[nil,nil,nil]]
players = [:X, :O].cycle
loop do
current_player = players.next
puts board.map { |row| row.map { |e| e || " " }.join("|") }.join("\n")
print "\n>> "
row, col = gets.split.map { |e| e.to_i }
puts
board[row][col] = current_player
end
*******************************************************OUTPUT********
| |
| |
| |
>> 1 1
| |
|X|
| |
>> 0 0
O| |
|X|
| |
>> 1 0
O| |
X|X|
| |
>> 1 2
O| |
X|X|O
| |
>> 1 2
O| |
X|X|X
| |
>> 1 3
O| |
X|X|X|O
| |
---------------------------------------------------------------------
Everything looks great up until those last two moves, which both violate
the rules of Tic-Tac-Toe. Let's make some small fixes and try again.
---------------------------------------------------------------------
board = [[nil,nil,nil],
[nil,nil,nil],
[nil,nil,nil]]
players = [:X, :O].cycle
current_player = players.next
loop do
puts board.map { |row| row.map { |e| e || " " }.join("|") }.join("\n")
print "\n>> "
row, col = gets.split.map { |e| e.to_i }
puts
begin
cell_contents = board.fetch(row).fetch(col)
rescue IndexError
puts "Out of bounds, try another position"
next
end
if cell_contents
puts "Cell occupied, try another position"
next
end
board[row][col] = current_player
current_player = players.next
end
*******************************************************OUTPUT********
# previous moves omitted
>> 1 0
O| |
O|X|X
| |
>> 1 0
Cell occupied, try another position
O| |
O|X|X
| |
>> 0 1
O|X|
O|X|X
| |
>> 1 3
Out of bounds, try another position
O|X|
O|X|X
| |
>> 0 2
O|X|O
O|X|X
| |
>> 2 1
O|X|O
O|X|X
|X|
---------------------------------------------------------------------
While the complexity has gone up a bit, our new error checking code
gracefully handles the two rule violations that our previous code
ignored. All we need to add is the winning and draw conditions and
we'll have a fully functioning game.
---------------------------------------------------------------------
board = [[nil,nil,nil],
[nil,nil,nil],
[nil,nil,nil]]
left_diagonal = [[0,0],[1,1],[2,2]]
right_diagonal = [[2,0],[1,1],[0,2]]
players = [:X, :O].cycle
current_player = players.next
loop do
puts board.map { |row| row.map { |e| e || " " }.join("|") }.join("\n")
print "\n>> "
row, col = gets.split.map { |e| e.to_i }
puts
begin
cell_contents = board.fetch(row).fetch(col)
rescue IndexError
puts "Out of bounds, try another position"
next
end
if cell_contents
puts "Cell occupied, try another position"
next
end
board[row][col] = current_player
lines = []
[left_diagonal, right_diagonal].each do |line|
lines << line if line.include?([row,col])
end
lines << (0..2).map { |c1| [row, c1] }
lines << (0..2).map { |r1| [r1, col] }
win = lines.any? do |line|
line.all? { |row,col| board[row][col] == current_player }
end
if win
puts "#{current_player} wins!"
exit
end
if board.flatten.compact.length == 9
puts "It's a draw!"
exit
end
current_player = players.next
end
*******************************************************OUTPUT********
# Win condition (previous moves omitted)
>> 0 1
O|O|X
O|X|X
| |
>> 2 0
X wins!
# Draw condition (previous moves omitted)
>> 1 0
O|O|X
X|X|
O|X|
>> 1 2
O|O|X
X|X|O
O|X|
>> 2 2
It's a draw!
---------------------------------------------------------------------
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment