-
-
Save practicingruby/24ef3c8209877c1946bb to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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