Design of a Tic Tac Toe program in Python
$begingroup$
Here is a program I wrote to reproduce the known win proportions for random Tic Tac Toe games (X always moving first). It takes about a minute to play the one million random games that will be played if you run main.py after putting all the below .py files in the same directory. (The program does reproduce the correct results.)
I'm studying design principles for keeping large code bodies structured for easier maintenance.
I'm trying to:
- focus on classes where everything is at the same level of abstraction
- make sure that everything in a class very clearly belongs in that class
- make it so that the reader can understand the current method he/she is reading without knowing what is happening anywhere else in the program
I'd appreciate some feedback on how I can improve on the above issues. The code should be a fast read. But to make it even faster, may I explain what I was thinking about each class? (main.py has no class of its own... it just gets the program going).
- For the
GamesEngine
class I was thinking that no force would cause games to play, moves to be made, or reports to be run, so the engine is the needed force. - For the
GameState
class I tried to put only the minimum information needed to hold the current board and the relevant information the comes with the current board. - For the
GameRules
class I tried to only put the rules that are needed to make conclusions about the current board (i.e. is there a win or a tie on the board). - Finally, the
ReportOnGame
class prints the final board for a single game along with who won, while theReportOnManyGames
class finds the proportions of X wins, O wins, and ties over many games.
Any thoughts on design would be much appreciated. (And if you see any improvements that can be made that aren't design related I'd love to hear about them.)
main.py
from games_engine import GamesEngine
from report_on_game import ReportOnGame
from report_on_many_games import ReportOnManyGames
games_engine = GamesEngine()
report_on_game = ReportOnGame()
report_on_many_games = ReportOnManyGames()
# Pass True to set_end_of_game_reporting to report on each game
report_on_game.set_end_of_game_reporting(False)
# parameter passed to play_games determines the number of games that will be played
games_engine.play_game(1000000)
# shows percentage of wins from X and O as well as percentage of ties
report_on_many_games.report_outcome_statistics()
games_engine.py
import random
from game_state import GameState
from game_rules import GameRules
from report_on_game import ReportOnGame
from report_on_many_games import ReportOnManyGames
class GamesEngine:
def __init__(self):
self.game_state = GameState()
self.game_rules = GameRules()
self.report_on_game = None
self.report_on_many_games = ReportOnManyGames()
# responsible for playing the number of games requested from main.py
def play_game(self, number_of_games):
while number_of_games > 0:
self.report_on_game = ReportOnGame()
self.game_state = GameState()
game_in_progress = True
while game_in_progress:
game_in_progress = self.make_move()
number_of_games -= 1
def make_move(self):
# randomly select an available square from the available_squares set, and store as a list of length 1
position_as_list_of_list = random.sample(self.game_state.available_squares, 1)
row_index = position_as_list_of_list[0][0]
column_index = position_as_list_of_list[0][1]
# remove the available square we will shortly use from the available_squares list
self.game_state.available_squares.remove([row_index, column_index])
# make move
self.game_state.board[row_index][column_index] = self.game_state.next_move
# call game_over to see if the game is over
if self.game_rules.game_over(self.game_state, row_index, column_index):
self.between_game_reporting(self.game_rules.winning_letter)
return False
# update who moves next
temp = self.game_state.next_move
self.game_state.next_move = self.game_state.previous_move
self.game_state.previous_move = temp
return True
def between_game_reporting(self, win_result='Tie'):
if self.report_on_game.end_of_game_report:
self.report_on_game.end_of_game_reporter(self.game_state, win_result)
self.report_on_many_games.track_game_outcomes(win_result)
game_state.py
class GameState:
def __init__(self):
self.board = None
self.available_squares = None
self.initialize_board_and_available_squares()
self.next_move = 'X'
self.previous_move = 'O'
def initialize_board_and_available_squares(self):
self.board = [[' ' for i in range(3)] for j in range(3)]
self.available_squares = [[i, j] for i in range(3) for j in range(3)]
# Printing an instance of the class will display a standard
# tic tac toe game image.
def __str__(self):
return "n" + self.board[0][0] + "|" + self.board[0][1] + "|" + self.board[0][2] + "n"+ self.board[1][0] + "|" + self.board[1][1] + "|" + self.board[1][2] + "n" + self.board[2][0] + "|" + self.board[2][1] + "|" + self.board[2][2]
game_rules.py
class GameRules:
def __init__(self):
self.letter_dict = {'X': -1, 'O': 1, ' ': 0}
self.winning_letter = None
def game_over(self, game_state, row_index, column_index):
# check row containing most recent move for win
total = 0
for column in range(3):
total = total + int(self.letter_dict[game_state.board[row_index][column]])
if abs(total) == 3:
self.winning_letter = game_state.board[row_index][column_index]
return True
# check column containing most recent move for win
total = 0
for row in range(3):
total = total + int(self.letter_dict[game_state.board[row][column_index]])
if abs(total) == 3:
self.winning_letter = game_state.board[row_index][column_index]
return True
# check for win on main-diagonal if it contains most recent move
if row_index == column_index:
total = 0
for diagonal_indexing in range(3):
total = total + int(self.letter_dict[game_state.board[diagonal_indexing][diagonal_indexing]])
if abs(total) == 3:
self.winning_letter = game_state.board[row_index][column_index]
return True
# check for win on off-diagonal if it contains most recent move
if row_index + column_index == 2:
total = 0
for off_diagonal_indexing in range(3):
total = total + int(self.letter_dict[game_state.board[off_diagonal_indexing][2 - off_diagonal_indexing]])
if abs(total) == 3:
self.winning_letter = game_state.board[row_index][column_index]
return True
if len(game_state.available_squares) == 0:
self.winning_letter = 'Tie'
return True
return False
report_on_game.py
class ReportOnGame:
end_of_game_report = False
@classmethod
def set_end_of_game_reporting(cls, boolean_set):
cls.end_of_game_report = boolean_set
# call turn_on_end_of_game_reporting from main.py to run this report method
@staticmethod
def end_of_game_reporter(board, result='Its a tie'):
print(board)
if result == 'X':
print(result + ' wonn')
elif result == 'O':
print(result + ' wonn')
else:
print(result + 'n')
report_on_many_games.py
class ReportOnManyGames:
number_of_x_wins = 0
number_of_o_wins = 0
number_of_ties = 0
@classmethod
def track_game_outcomes(cls, win_result):
if win_result == 'X':
cls.number_of_x_wins = cls.number_of_x_wins + 1
if win_result == 'O':
cls.number_of_o_wins = cls.number_of_o_wins + 1
if win_result == 'Tie':
cls.number_of_ties = cls.number_of_ties + 1
@classmethod
def report_outcome_statistics(cls):
total_games = cls.number_of_x_wins + cls.number_of_o_wins + cls.number_of_ties
print('Proportion of X wins: ' + str(cls.number_of_x_wins/total_games))
print('Proportion of O wins: ' + str(cls.number_of_o_wins / total_games))
print('Proportion of ties: ' + str(cls.number_of_ties / total_games))
python object-oriented tic-tac-toe simulation
New contributor
$endgroup$
add a comment |
$begingroup$
Here is a program I wrote to reproduce the known win proportions for random Tic Tac Toe games (X always moving first). It takes about a minute to play the one million random games that will be played if you run main.py after putting all the below .py files in the same directory. (The program does reproduce the correct results.)
I'm studying design principles for keeping large code bodies structured for easier maintenance.
I'm trying to:
- focus on classes where everything is at the same level of abstraction
- make sure that everything in a class very clearly belongs in that class
- make it so that the reader can understand the current method he/she is reading without knowing what is happening anywhere else in the program
I'd appreciate some feedback on how I can improve on the above issues. The code should be a fast read. But to make it even faster, may I explain what I was thinking about each class? (main.py has no class of its own... it just gets the program going).
- For the
GamesEngine
class I was thinking that no force would cause games to play, moves to be made, or reports to be run, so the engine is the needed force. - For the
GameState
class I tried to put only the minimum information needed to hold the current board and the relevant information the comes with the current board. - For the
GameRules
class I tried to only put the rules that are needed to make conclusions about the current board (i.e. is there a win or a tie on the board). - Finally, the
ReportOnGame
class prints the final board for a single game along with who won, while theReportOnManyGames
class finds the proportions of X wins, O wins, and ties over many games.
Any thoughts on design would be much appreciated. (And if you see any improvements that can be made that aren't design related I'd love to hear about them.)
main.py
from games_engine import GamesEngine
from report_on_game import ReportOnGame
from report_on_many_games import ReportOnManyGames
games_engine = GamesEngine()
report_on_game = ReportOnGame()
report_on_many_games = ReportOnManyGames()
# Pass True to set_end_of_game_reporting to report on each game
report_on_game.set_end_of_game_reporting(False)
# parameter passed to play_games determines the number of games that will be played
games_engine.play_game(1000000)
# shows percentage of wins from X and O as well as percentage of ties
report_on_many_games.report_outcome_statistics()
games_engine.py
import random
from game_state import GameState
from game_rules import GameRules
from report_on_game import ReportOnGame
from report_on_many_games import ReportOnManyGames
class GamesEngine:
def __init__(self):
self.game_state = GameState()
self.game_rules = GameRules()
self.report_on_game = None
self.report_on_many_games = ReportOnManyGames()
# responsible for playing the number of games requested from main.py
def play_game(self, number_of_games):
while number_of_games > 0:
self.report_on_game = ReportOnGame()
self.game_state = GameState()
game_in_progress = True
while game_in_progress:
game_in_progress = self.make_move()
number_of_games -= 1
def make_move(self):
# randomly select an available square from the available_squares set, and store as a list of length 1
position_as_list_of_list = random.sample(self.game_state.available_squares, 1)
row_index = position_as_list_of_list[0][0]
column_index = position_as_list_of_list[0][1]
# remove the available square we will shortly use from the available_squares list
self.game_state.available_squares.remove([row_index, column_index])
# make move
self.game_state.board[row_index][column_index] = self.game_state.next_move
# call game_over to see if the game is over
if self.game_rules.game_over(self.game_state, row_index, column_index):
self.between_game_reporting(self.game_rules.winning_letter)
return False
# update who moves next
temp = self.game_state.next_move
self.game_state.next_move = self.game_state.previous_move
self.game_state.previous_move = temp
return True
def between_game_reporting(self, win_result='Tie'):
if self.report_on_game.end_of_game_report:
self.report_on_game.end_of_game_reporter(self.game_state, win_result)
self.report_on_many_games.track_game_outcomes(win_result)
game_state.py
class GameState:
def __init__(self):
self.board = None
self.available_squares = None
self.initialize_board_and_available_squares()
self.next_move = 'X'
self.previous_move = 'O'
def initialize_board_and_available_squares(self):
self.board = [[' ' for i in range(3)] for j in range(3)]
self.available_squares = [[i, j] for i in range(3) for j in range(3)]
# Printing an instance of the class will display a standard
# tic tac toe game image.
def __str__(self):
return "n" + self.board[0][0] + "|" + self.board[0][1] + "|" + self.board[0][2] + "n"+ self.board[1][0] + "|" + self.board[1][1] + "|" + self.board[1][2] + "n" + self.board[2][0] + "|" + self.board[2][1] + "|" + self.board[2][2]
game_rules.py
class GameRules:
def __init__(self):
self.letter_dict = {'X': -1, 'O': 1, ' ': 0}
self.winning_letter = None
def game_over(self, game_state, row_index, column_index):
# check row containing most recent move for win
total = 0
for column in range(3):
total = total + int(self.letter_dict[game_state.board[row_index][column]])
if abs(total) == 3:
self.winning_letter = game_state.board[row_index][column_index]
return True
# check column containing most recent move for win
total = 0
for row in range(3):
total = total + int(self.letter_dict[game_state.board[row][column_index]])
if abs(total) == 3:
self.winning_letter = game_state.board[row_index][column_index]
return True
# check for win on main-diagonal if it contains most recent move
if row_index == column_index:
total = 0
for diagonal_indexing in range(3):
total = total + int(self.letter_dict[game_state.board[diagonal_indexing][diagonal_indexing]])
if abs(total) == 3:
self.winning_letter = game_state.board[row_index][column_index]
return True
# check for win on off-diagonal if it contains most recent move
if row_index + column_index == 2:
total = 0
for off_diagonal_indexing in range(3):
total = total + int(self.letter_dict[game_state.board[off_diagonal_indexing][2 - off_diagonal_indexing]])
if abs(total) == 3:
self.winning_letter = game_state.board[row_index][column_index]
return True
if len(game_state.available_squares) == 0:
self.winning_letter = 'Tie'
return True
return False
report_on_game.py
class ReportOnGame:
end_of_game_report = False
@classmethod
def set_end_of_game_reporting(cls, boolean_set):
cls.end_of_game_report = boolean_set
# call turn_on_end_of_game_reporting from main.py to run this report method
@staticmethod
def end_of_game_reporter(board, result='Its a tie'):
print(board)
if result == 'X':
print(result + ' wonn')
elif result == 'O':
print(result + ' wonn')
else:
print(result + 'n')
report_on_many_games.py
class ReportOnManyGames:
number_of_x_wins = 0
number_of_o_wins = 0
number_of_ties = 0
@classmethod
def track_game_outcomes(cls, win_result):
if win_result == 'X':
cls.number_of_x_wins = cls.number_of_x_wins + 1
if win_result == 'O':
cls.number_of_o_wins = cls.number_of_o_wins + 1
if win_result == 'Tie':
cls.number_of_ties = cls.number_of_ties + 1
@classmethod
def report_outcome_statistics(cls):
total_games = cls.number_of_x_wins + cls.number_of_o_wins + cls.number_of_ties
print('Proportion of X wins: ' + str(cls.number_of_x_wins/total_games))
print('Proportion of O wins: ' + str(cls.number_of_o_wins / total_games))
print('Proportion of ties: ' + str(cls.number_of_ties / total_games))
python object-oriented tic-tac-toe simulation
New contributor
$endgroup$
add a comment |
$begingroup$
Here is a program I wrote to reproduce the known win proportions for random Tic Tac Toe games (X always moving first). It takes about a minute to play the one million random games that will be played if you run main.py after putting all the below .py files in the same directory. (The program does reproduce the correct results.)
I'm studying design principles for keeping large code bodies structured for easier maintenance.
I'm trying to:
- focus on classes where everything is at the same level of abstraction
- make sure that everything in a class very clearly belongs in that class
- make it so that the reader can understand the current method he/she is reading without knowing what is happening anywhere else in the program
I'd appreciate some feedback on how I can improve on the above issues. The code should be a fast read. But to make it even faster, may I explain what I was thinking about each class? (main.py has no class of its own... it just gets the program going).
- For the
GamesEngine
class I was thinking that no force would cause games to play, moves to be made, or reports to be run, so the engine is the needed force. - For the
GameState
class I tried to put only the minimum information needed to hold the current board and the relevant information the comes with the current board. - For the
GameRules
class I tried to only put the rules that are needed to make conclusions about the current board (i.e. is there a win or a tie on the board). - Finally, the
ReportOnGame
class prints the final board for a single game along with who won, while theReportOnManyGames
class finds the proportions of X wins, O wins, and ties over many games.
Any thoughts on design would be much appreciated. (And if you see any improvements that can be made that aren't design related I'd love to hear about them.)
main.py
from games_engine import GamesEngine
from report_on_game import ReportOnGame
from report_on_many_games import ReportOnManyGames
games_engine = GamesEngine()
report_on_game = ReportOnGame()
report_on_many_games = ReportOnManyGames()
# Pass True to set_end_of_game_reporting to report on each game
report_on_game.set_end_of_game_reporting(False)
# parameter passed to play_games determines the number of games that will be played
games_engine.play_game(1000000)
# shows percentage of wins from X and O as well as percentage of ties
report_on_many_games.report_outcome_statistics()
games_engine.py
import random
from game_state import GameState
from game_rules import GameRules
from report_on_game import ReportOnGame
from report_on_many_games import ReportOnManyGames
class GamesEngine:
def __init__(self):
self.game_state = GameState()
self.game_rules = GameRules()
self.report_on_game = None
self.report_on_many_games = ReportOnManyGames()
# responsible for playing the number of games requested from main.py
def play_game(self, number_of_games):
while number_of_games > 0:
self.report_on_game = ReportOnGame()
self.game_state = GameState()
game_in_progress = True
while game_in_progress:
game_in_progress = self.make_move()
number_of_games -= 1
def make_move(self):
# randomly select an available square from the available_squares set, and store as a list of length 1
position_as_list_of_list = random.sample(self.game_state.available_squares, 1)
row_index = position_as_list_of_list[0][0]
column_index = position_as_list_of_list[0][1]
# remove the available square we will shortly use from the available_squares list
self.game_state.available_squares.remove([row_index, column_index])
# make move
self.game_state.board[row_index][column_index] = self.game_state.next_move
# call game_over to see if the game is over
if self.game_rules.game_over(self.game_state, row_index, column_index):
self.between_game_reporting(self.game_rules.winning_letter)
return False
# update who moves next
temp = self.game_state.next_move
self.game_state.next_move = self.game_state.previous_move
self.game_state.previous_move = temp
return True
def between_game_reporting(self, win_result='Tie'):
if self.report_on_game.end_of_game_report:
self.report_on_game.end_of_game_reporter(self.game_state, win_result)
self.report_on_many_games.track_game_outcomes(win_result)
game_state.py
class GameState:
def __init__(self):
self.board = None
self.available_squares = None
self.initialize_board_and_available_squares()
self.next_move = 'X'
self.previous_move = 'O'
def initialize_board_and_available_squares(self):
self.board = [[' ' for i in range(3)] for j in range(3)]
self.available_squares = [[i, j] for i in range(3) for j in range(3)]
# Printing an instance of the class will display a standard
# tic tac toe game image.
def __str__(self):
return "n" + self.board[0][0] + "|" + self.board[0][1] + "|" + self.board[0][2] + "n"+ self.board[1][0] + "|" + self.board[1][1] + "|" + self.board[1][2] + "n" + self.board[2][0] + "|" + self.board[2][1] + "|" + self.board[2][2]
game_rules.py
class GameRules:
def __init__(self):
self.letter_dict = {'X': -1, 'O': 1, ' ': 0}
self.winning_letter = None
def game_over(self, game_state, row_index, column_index):
# check row containing most recent move for win
total = 0
for column in range(3):
total = total + int(self.letter_dict[game_state.board[row_index][column]])
if abs(total) == 3:
self.winning_letter = game_state.board[row_index][column_index]
return True
# check column containing most recent move for win
total = 0
for row in range(3):
total = total + int(self.letter_dict[game_state.board[row][column_index]])
if abs(total) == 3:
self.winning_letter = game_state.board[row_index][column_index]
return True
# check for win on main-diagonal if it contains most recent move
if row_index == column_index:
total = 0
for diagonal_indexing in range(3):
total = total + int(self.letter_dict[game_state.board[diagonal_indexing][diagonal_indexing]])
if abs(total) == 3:
self.winning_letter = game_state.board[row_index][column_index]
return True
# check for win on off-diagonal if it contains most recent move
if row_index + column_index == 2:
total = 0
for off_diagonal_indexing in range(3):
total = total + int(self.letter_dict[game_state.board[off_diagonal_indexing][2 - off_diagonal_indexing]])
if abs(total) == 3:
self.winning_letter = game_state.board[row_index][column_index]
return True
if len(game_state.available_squares) == 0:
self.winning_letter = 'Tie'
return True
return False
report_on_game.py
class ReportOnGame:
end_of_game_report = False
@classmethod
def set_end_of_game_reporting(cls, boolean_set):
cls.end_of_game_report = boolean_set
# call turn_on_end_of_game_reporting from main.py to run this report method
@staticmethod
def end_of_game_reporter(board, result='Its a tie'):
print(board)
if result == 'X':
print(result + ' wonn')
elif result == 'O':
print(result + ' wonn')
else:
print(result + 'n')
report_on_many_games.py
class ReportOnManyGames:
number_of_x_wins = 0
number_of_o_wins = 0
number_of_ties = 0
@classmethod
def track_game_outcomes(cls, win_result):
if win_result == 'X':
cls.number_of_x_wins = cls.number_of_x_wins + 1
if win_result == 'O':
cls.number_of_o_wins = cls.number_of_o_wins + 1
if win_result == 'Tie':
cls.number_of_ties = cls.number_of_ties + 1
@classmethod
def report_outcome_statistics(cls):
total_games = cls.number_of_x_wins + cls.number_of_o_wins + cls.number_of_ties
print('Proportion of X wins: ' + str(cls.number_of_x_wins/total_games))
print('Proportion of O wins: ' + str(cls.number_of_o_wins / total_games))
print('Proportion of ties: ' + str(cls.number_of_ties / total_games))
python object-oriented tic-tac-toe simulation
New contributor
$endgroup$
Here is a program I wrote to reproduce the known win proportions for random Tic Tac Toe games (X always moving first). It takes about a minute to play the one million random games that will be played if you run main.py after putting all the below .py files in the same directory. (The program does reproduce the correct results.)
I'm studying design principles for keeping large code bodies structured for easier maintenance.
I'm trying to:
- focus on classes where everything is at the same level of abstraction
- make sure that everything in a class very clearly belongs in that class
- make it so that the reader can understand the current method he/she is reading without knowing what is happening anywhere else in the program
I'd appreciate some feedback on how I can improve on the above issues. The code should be a fast read. But to make it even faster, may I explain what I was thinking about each class? (main.py has no class of its own... it just gets the program going).
- For the
GamesEngine
class I was thinking that no force would cause games to play, moves to be made, or reports to be run, so the engine is the needed force. - For the
GameState
class I tried to put only the minimum information needed to hold the current board and the relevant information the comes with the current board. - For the
GameRules
class I tried to only put the rules that are needed to make conclusions about the current board (i.e. is there a win or a tie on the board). - Finally, the
ReportOnGame
class prints the final board for a single game along with who won, while theReportOnManyGames
class finds the proportions of X wins, O wins, and ties over many games.
Any thoughts on design would be much appreciated. (And if you see any improvements that can be made that aren't design related I'd love to hear about them.)
main.py
from games_engine import GamesEngine
from report_on_game import ReportOnGame
from report_on_many_games import ReportOnManyGames
games_engine = GamesEngine()
report_on_game = ReportOnGame()
report_on_many_games = ReportOnManyGames()
# Pass True to set_end_of_game_reporting to report on each game
report_on_game.set_end_of_game_reporting(False)
# parameter passed to play_games determines the number of games that will be played
games_engine.play_game(1000000)
# shows percentage of wins from X and O as well as percentage of ties
report_on_many_games.report_outcome_statistics()
games_engine.py
import random
from game_state import GameState
from game_rules import GameRules
from report_on_game import ReportOnGame
from report_on_many_games import ReportOnManyGames
class GamesEngine:
def __init__(self):
self.game_state = GameState()
self.game_rules = GameRules()
self.report_on_game = None
self.report_on_many_games = ReportOnManyGames()
# responsible for playing the number of games requested from main.py
def play_game(self, number_of_games):
while number_of_games > 0:
self.report_on_game = ReportOnGame()
self.game_state = GameState()
game_in_progress = True
while game_in_progress:
game_in_progress = self.make_move()
number_of_games -= 1
def make_move(self):
# randomly select an available square from the available_squares set, and store as a list of length 1
position_as_list_of_list = random.sample(self.game_state.available_squares, 1)
row_index = position_as_list_of_list[0][0]
column_index = position_as_list_of_list[0][1]
# remove the available square we will shortly use from the available_squares list
self.game_state.available_squares.remove([row_index, column_index])
# make move
self.game_state.board[row_index][column_index] = self.game_state.next_move
# call game_over to see if the game is over
if self.game_rules.game_over(self.game_state, row_index, column_index):
self.between_game_reporting(self.game_rules.winning_letter)
return False
# update who moves next
temp = self.game_state.next_move
self.game_state.next_move = self.game_state.previous_move
self.game_state.previous_move = temp
return True
def between_game_reporting(self, win_result='Tie'):
if self.report_on_game.end_of_game_report:
self.report_on_game.end_of_game_reporter(self.game_state, win_result)
self.report_on_many_games.track_game_outcomes(win_result)
game_state.py
class GameState:
def __init__(self):
self.board = None
self.available_squares = None
self.initialize_board_and_available_squares()
self.next_move = 'X'
self.previous_move = 'O'
def initialize_board_and_available_squares(self):
self.board = [[' ' for i in range(3)] for j in range(3)]
self.available_squares = [[i, j] for i in range(3) for j in range(3)]
# Printing an instance of the class will display a standard
# tic tac toe game image.
def __str__(self):
return "n" + self.board[0][0] + "|" + self.board[0][1] + "|" + self.board[0][2] + "n"+ self.board[1][0] + "|" + self.board[1][1] + "|" + self.board[1][2] + "n" + self.board[2][0] + "|" + self.board[2][1] + "|" + self.board[2][2]
game_rules.py
class GameRules:
def __init__(self):
self.letter_dict = {'X': -1, 'O': 1, ' ': 0}
self.winning_letter = None
def game_over(self, game_state, row_index, column_index):
# check row containing most recent move for win
total = 0
for column in range(3):
total = total + int(self.letter_dict[game_state.board[row_index][column]])
if abs(total) == 3:
self.winning_letter = game_state.board[row_index][column_index]
return True
# check column containing most recent move for win
total = 0
for row in range(3):
total = total + int(self.letter_dict[game_state.board[row][column_index]])
if abs(total) == 3:
self.winning_letter = game_state.board[row_index][column_index]
return True
# check for win on main-diagonal if it contains most recent move
if row_index == column_index:
total = 0
for diagonal_indexing in range(3):
total = total + int(self.letter_dict[game_state.board[diagonal_indexing][diagonal_indexing]])
if abs(total) == 3:
self.winning_letter = game_state.board[row_index][column_index]
return True
# check for win on off-diagonal if it contains most recent move
if row_index + column_index == 2:
total = 0
for off_diagonal_indexing in range(3):
total = total + int(self.letter_dict[game_state.board[off_diagonal_indexing][2 - off_diagonal_indexing]])
if abs(total) == 3:
self.winning_letter = game_state.board[row_index][column_index]
return True
if len(game_state.available_squares) == 0:
self.winning_letter = 'Tie'
return True
return False
report_on_game.py
class ReportOnGame:
end_of_game_report = False
@classmethod
def set_end_of_game_reporting(cls, boolean_set):
cls.end_of_game_report = boolean_set
# call turn_on_end_of_game_reporting from main.py to run this report method
@staticmethod
def end_of_game_reporter(board, result='Its a tie'):
print(board)
if result == 'X':
print(result + ' wonn')
elif result == 'O':
print(result + ' wonn')
else:
print(result + 'n')
report_on_many_games.py
class ReportOnManyGames:
number_of_x_wins = 0
number_of_o_wins = 0
number_of_ties = 0
@classmethod
def track_game_outcomes(cls, win_result):
if win_result == 'X':
cls.number_of_x_wins = cls.number_of_x_wins + 1
if win_result == 'O':
cls.number_of_o_wins = cls.number_of_o_wins + 1
if win_result == 'Tie':
cls.number_of_ties = cls.number_of_ties + 1
@classmethod
def report_outcome_statistics(cls):
total_games = cls.number_of_x_wins + cls.number_of_o_wins + cls.number_of_ties
print('Proportion of X wins: ' + str(cls.number_of_x_wins/total_games))
print('Proportion of O wins: ' + str(cls.number_of_o_wins / total_games))
print('Proportion of ties: ' + str(cls.number_of_ties / total_games))
python object-oriented tic-tac-toe simulation
python object-oriented tic-tac-toe simulation
New contributor
New contributor
edited 7 mins ago
Jamal♦
30.3k11117227
30.3k11117227
New contributor
asked 1 hour ago
okcappokcapp
233
233
New contributor
New contributor
add a comment |
add a comment |
0
active
oldest
votes
Your Answer
StackExchange.ifUsing("editor", function () {
return StackExchange.using("mathjaxEditing", function () {
StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix) {
StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
});
});
}, "mathjax-editing");
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "196"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: false,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: null,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
okcapp is a new contributor. Be nice, and check out our Code of Conduct.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f213277%2fdesign-of-a-tic-tac-toe-program-in-python%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
0
active
oldest
votes
0
active
oldest
votes
active
oldest
votes
active
oldest
votes
okcapp is a new contributor. Be nice, and check out our Code of Conduct.
okcapp is a new contributor. Be nice, and check out our Code of Conduct.
okcapp is a new contributor. Be nice, and check out our Code of Conduct.
okcapp is a new contributor. Be nice, and check out our Code of Conduct.
Thanks for contributing an answer to Code Review Stack Exchange!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
Use MathJax to format equations. MathJax reference.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f213277%2fdesign-of-a-tic-tac-toe-program-in-python%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown