Design of a Tic Tac Toe program in Python












1












$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 the ReportOnManyGames 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))









share|improve this question









New contributor




okcapp is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.







$endgroup$

















    1












    $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 the ReportOnManyGames 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))









    share|improve this question









    New contributor




    okcapp is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
    Check out our Code of Conduct.







    $endgroup$















      1












      1








      1





      $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 the ReportOnManyGames 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))









      share|improve this question









      New contributor




      okcapp is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
      Check out our Code of Conduct.







      $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 the ReportOnManyGames 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






      share|improve this question









      New contributor




      okcapp is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
      Check out our Code of Conduct.











      share|improve this question









      New contributor




      okcapp is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
      Check out our Code of Conduct.









      share|improve this question




      share|improve this question








      edited 7 mins ago









      Jamal

      30.3k11117227




      30.3k11117227






      New contributor




      okcapp is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
      Check out our Code of Conduct.









      asked 1 hour ago









      okcappokcapp

      233




      233




      New contributor




      okcapp is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
      Check out our Code of Conduct.





      New contributor





      okcapp is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
      Check out our Code of Conduct.






      okcapp is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
      Check out our Code of Conduct.






















          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.










          draft saved

          draft discarded


















          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.










          draft saved

          draft discarded


















          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.




          draft saved


          draft discarded














          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





















































          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







          Popular posts from this blog

          Сан-Квентин

          Алькесар

          Josef Freinademetz