As a basic project, I've programmed the game of noughts and crosses (tic-tac-toe) using classes. I made three classes: Board
, Player
and LazyRobot
, as well as the main game file.
I would like to know any improvements to code or any best practices I have neglected. I know there is some repetition within the code, but I would like to hear your thoughts.
Below is the main game.py file:
from copy import deepcopy
from board import Board, PositionError
from player import Player
from lazyrobot import LazyRobot
def game(player1, player2):
board = Board()
game_winner = None
turn = player1
print('Starting new game.')
print('{} is playing noughts.'.format(player1.getname()))
print('{} is playing crosses.'.format(player2.getname()))
board.display_board()
while len(board.available_positions()) != 0:
if turn == player1:
nought_turn(player1, board)
if board.is_winner() is True:
game_winner = player1
break
else:
turn = player2
else:
cross_turn(player2, board)
if board.is_winner() is True:
game_winner = player2
break
else:
turn = player1
if game_winner is None:
print('Game is tied.')
else:
print('{} has won the game!'.format(game_winner.getname()))
print('End of game.')
def nought_turn(player, board):
print('It is now {}\'s turn. (o)'.format(player.getname()))
while True:
try:
move = player.getmove(deepcopy(board))
board.add_nought(move)
break
except PositionError:
print('Sorry, the space has been taken. '
'Please try another space.')
board.display_board()
def cross_turn(player, board):
print('It is now {}\'s turn. (x)'.format(player.getname()))
while True:
try:
move = player.getmove(deepcopy(board))
board.add_cross(move)
break
except PositionError:
print('Sorry, the space has been taken. '
'Please try another space.')
board.display_board()
Here is the Board
class:
class PositionError(Exception):
pass
class Board(object):
def __init__(self):
self.board = {1: ' ', 2: ' ', 3: ' ', 4: ' ',
5: ' ', 6: ' ', 7: ' ', 8: ' ', 9: ' '}
self.nought_positions = []
self.cross_positions = []
def display_board(self):
line_1 = ' {} | {} | {} '.format(self.board[1],
self.board[2], self.board[3])
line_2 = ' {} | {} | {} '.format(self.board[4],
self.board[5], self.board[6])
line_3 = ' {} | {} | {} '.format(self.board[7],
self.board[8], self.board[9])
print(line_1)
print('-----------')
print(line_2)
print('-----------')
print(line_3)
def add_nought(self, position):
if self.board[position] == ' ':
self.board[position] = 'o'
self.nought_positions.append(position)
else:
raise PositionError('Space is occupied.')
def add_cross(self, position):
if self.board[position] == ' ':
self.board[position] = 'x'
self.cross_positions.append(position)
else:
raise PositionError('Space is occupied.')
def is_winner(self):
winning_positions = [{1,2,3}, {4,5,6},
{7,8,9}, {1,4,7},
{2,5,8}, {3,6,9},
{1,5,9}, {3,5,7}]
nought_set = set(self.nought_positions)
cross_set = set(self.cross_positions)
for position in winning_positions:
if nought_set & position == position:
return True
elif cross_set & position == position:
return True
return False
def available_positions(self):
nought_set = set(self.nought_positions)
cross_set = set(self.cross_positions)
all_positions = {1,2,3,4,5,6,7,8,9}
available_positions = (all_positions - nought_set) - cross_set
return list(available_positions)
Here is the Player
class:
class PositionError(Exception):
pass
class NumberError(Exception):
pass
class Player(object):
def __init__(self, name):
self.name = name
def getname(self):
return self.name
def getmove(self, board):
available_positions = board.available_positions()
print('{}, make your next move.'.format(self.getname()))
while True:
try:
move = int(input('Number: '))
if move > 9 or move < 1:
raise NumberError
if move not in available_positions:
raise PositionError
return move
except ValueError:
print('Sorry, please type a number.')
except NumberError:
print('Sorry, please type a number between 1 and 9.')
except PositionError:
print('Sorry, the space has been taken. '
'Please try another space.')
And here is the LazyRobot
class: (I'm not too worried about this, as I will make a robot with better strategy.)
from random import randint
class LazyRobot(object):
def __init__(self, name):
self.name = name
def getname(self):
return self.name
def getmove(self, board):
available_positions = board.available_positions()
index = randint(0, len(available_positions)-1)
return available_positions[index]