patternpythonMinor
Tic-Tac-Toe solver
Viewed 0 times
tacsolvertictoe
Problem
As a programming exercise, I've attempted a Tic-Tac-Toe solver which takes a finished board and determines a winner.
On top of checks for malformed input/ambiguous games (e.g. both players with winning positions), what else can I do to improve this? In particular, is the coding style acceptable and are there better ways to check for win conditions?
For convenience, here's a little test function too:
```
def test_tic_tac():
def pretty_print(b):
for row in b:
print row
gameboard = [[0,0,1],[0,1,0],[1,1,0]]
pretty_print(gameboard)
tictac(gameboard)
gameboard = [[0,1,1],[0,0,0],[1,1,0]]
pretty_print(gameboard)
tictac(gameboard)
gameboard = [[1,0,1],[0,0,1],[0,1,0]]
pretty_print(gameboard)
tictac(gameboard)
gameboard = [[0,0,1,0],[1,1,0,1],[0,1,1,1],[0,1,0,1]]
pretty_print(
On top of checks for malformed input/ambiguous games (e.g. both players with winning positions), what else can I do to improve this? In particular, is the coding style acceptable and are there better ways to check for win conditions?
def tictac(b):
"""
Parses a tic-tac-toe board and returns a winner.
Input: a list of lists containing values 0 or 1.
0 corresponds to 'noughts' and 1 to 'crosses'
e.g.
>>> gameboard = [[0,0,1],[0,1,0],[1,1,0]]
>>> tictac(gameboard)
X wins
"""
# If the sum of a column/row/diagonal is 0, O wins.
# If the sum is number of rows/columns, X wins.
winner = ""
board_range = range(len(b))
# Check rows and columns.
for i in board_range:
row_sum = sum(b[i])
col_sum = sum([x[i] for x in b])
if row_sum == 0 or col_sum == 0:
winner = "O wins"
elif row_sum == len(b) or col_sum == len(b):
winner = "X wins"
# Check the diagonals.
fwd_diag_sum = sum([b[i][i] for i in board_range])
bck_diag_sum = sum([b[i][len(b)-i-1] for i in board_range])
if fwd_diag_sum == 0 or bck_diag_sum == 0:
winner = "O wins"
if fwd_diag_sum == len(b) or bck_diag_sum == len(b):
winner = "X wins"
if winner:
print winner
else:
print "Game is a tie!"For convenience, here's a little test function too:
```
def test_tic_tac():
def pretty_print(b):
for row in b:
print row
gameboard = [[0,0,1],[0,1,0],[1,1,0]]
pretty_print(gameboard)
tictac(gameboard)
gameboard = [[0,1,1],[0,0,0],[1,1,0]]
pretty_print(gameboard)
tictac(gameboard)
gameboard = [[1,0,1],[0,0,1],[0,1,0]]
pretty_print(gameboard)
tictac(gameboard)
gameboard = [[0,0,1,0],[1,1,0,1],[0,1,1,1],[0,1,0,1]]
pretty_print(
Solution
Missing handling for incomplete boards of finished games
The program works only with complete boards. It won't work with this finished game with incomplete board, which is kind of a big drawback for a game evaluator:
Return result instead of printing
It would be better to not have the board evaluation logic and printing in the same method.
You should split these steps into separate functions.
This will make some optimizations easier (see my next point).
It will also help improving your testing technique.
Testing by reading output is not convenient.
It's best when tests either pass or fail,
so that you can verify if the program behaves correctly just by checking that everything passes.
One way to achieve this is using assertions, for example:
A better way is using the
and splitting your test method to distinct test cases for different kinds of winning positions, such as win by row sum, col sum, diagonal.
Wasted operations
In the loop that checks the row and column sums,
once you found a winner, the loop happily continues,
and after the loop you check the diagonals,
when in fact you could return the winner as soon as you see it:
By returning sooner,
the final
Duplicated string literals
The "O wins", "X wins" string literals appear multiple times.
It would be better to avoid such code duplication,
as it's prone to errors if you ever change the message format,
you have to remember to make the same change everywhere.
Coding style
You should follow PEP8.
The program works only with complete boards. It won't work with this finished game with incomplete board, which is kind of a big drawback for a game evaluator:
o
x o o
o x xReturn result instead of printing
It would be better to not have the board evaluation logic and printing in the same method.
You should split these steps into separate functions.
This will make some optimizations easier (see my next point).
It will also help improving your testing technique.
Testing by reading output is not convenient.
It's best when tests either pass or fail,
so that you can verify if the program behaves correctly just by checking that everything passes.
One way to achieve this is using assertions, for example:
assert "X wins" == evaluate_board([[0, 0, 1], [0, 1, 0], [1, 1, 0]])A better way is using the
unittest package of Python,and splitting your test method to distinct test cases for different kinds of winning positions, such as win by row sum, col sum, diagonal.
Wasted operations
In the loop that checks the row and column sums,
once you found a winner, the loop happily continues,
and after the loop you check the diagonals,
when in fact you could return the winner as soon as you see it:
if row_sum == 0 or col_sum == 0:
return "O wins"By returning sooner,
the final
if winner statements could be replaced with a simple return "Game is a tie!"Duplicated string literals
The "O wins", "X wins" string literals appear multiple times.
It would be better to avoid such code duplication,
as it's prone to errors if you ever change the message format,
you have to remember to make the same change everywhere.
Coding style
You should follow PEP8.
Code Snippets
o
x o o
o x xassert "X wins" == evaluate_board([[0, 0, 1], [0, 1, 0], [1, 1, 0]])if row_sum == 0 or col_sum == 0:
return "O wins"Context
StackExchange Code Review Q#79317, answer score: 8
Revisions (0)
No revisions yet.