1
\$\begingroup\$

I am fairly new to Python and have tried to create a tic tac toe game that is completely automatic, where you can see if a player1 has an advantage if you let it start in the middle and I would love some feedback on it and improvement areas. That is, is it something that looks crazy, something that I do wrong or something that I should clarify regarding the structure and coding?

I ask because I'm so new to python that I have a hard time seeing where I should settle and where I should spend more time improving the code. I will also address where I think my code is weak below and would love to hear your input on this comment.

Briefly about how it works is that you start by calling main() and decide how many games you would like to play by answering the first question, then you have to decide how big playing surface you would like out of 3, 5 and 7 by answering the second question and last you pick your scenario (1 or 2) by answering the last question. 1 is the scenario when all moves are automatic and 2 is the scenario where player1 put its first move in the middle. Then you will get a plot with the statistic from all game rounds.

My biggest concern about it is that the error messages if you call the wrong value to board_size, games or scenario appears after you have answered all the questions and not immediately - what do you think about that? I have tried to specify the error messages to compensate that though I didn't find a way to have them appear straight away.

import numpy as np
import random 
import matplotlib.pyplot as plt


# creating an empty board
def create_board(board_size):
    return np.zeros((board_size,board_size), dtype=int)

def find_placement_scenario_one(board): 
 
    lst = [] 
   
    for i in range(len(board)): 
        for j in range(len(board)): 
           
            if board[i][j] == 0: 
                lst.append((i, j)) 
    return(lst)

def random_placeout_scenario_one(board, player): 
    selection = find_placement_scenario_one(board) 
    current_loc = random.choice(selection)
    board[current_loc] = player 
    return(board) 

def find_placement_scenario_two(board, board_size):
    l=[]
    
    for i in range(len(board)): 
         for j in range(len(board)): 
             
             if board_size == 3:
                 if board[i][j] == 0: 
                     l.append((1, 1)) 
             if board_size == 5:
                 if board[i][j] == 0: 
                     l.append((2, 2)) 
             if board_size == 7:
                 if board[i][j] == 0: 
                     l.append((3, 3)) 
    return(l) 

def placeout_scenario_two(board, player, board_size):
    
    player = 1
    selection = find_placement_scenario_two(board, board_size) 
    current_loc = random.choice(selection) 
    board[current_loc] = player 
    return(board) 

def check_row(board, player):
    for x in range(len(board)): 
        win = True
      
        for y in range(len(board)): 
            if board[x, y] != player: 
                win = False
                continue
              
        if win == True: 
            return(win) 
    return(win) 

def check_column(board, player):
      for x in range(len(board)): 
        win = True
      
        for y in range(len(board)): 
            if board[y][x] != player: 
                win = False
                continue
              
        if win == True: 
            return(win) 
      return(win) 

def check_diagonal(board, player):
    win = True
    y = 0
    for x in range(len(board)): 
        if board[x, x] != player: 
            win = False
    if win: 
        return win 
    win = True
    if win: 
        for x in range(len(board)): 
            y = len(board) - 1 - x 
            if board[x, y] != player: 
                win = False
    return win 


def evaluate(board): 
    winner = 0
   
    for player in [1, 2]: 
        if (check_row(board, player) or
            check_column(board,player) or
            check_diagonal(board,player)): 
              
            winner = player 
           
    if np.all(board != 0) and winner == 0: 
        winner = -1
    return winner

def choose_scenario(scenario, board_size):
    
    if board_size ==3 or board_size == 5 or board_size == 7:
        if scenario == 1 or scenario == 2:
        
            board, winner, counter = create_board(board_size), 0, 1
            
            if scenario == 1:
              
                while winner == 0: 
                    for player in [1, 2]:  
                        board = random_placeout_scenario_one(board, player)  
                        counter += 1
                        winner = evaluate(board) 
                        if winner != 0: 
                            break
                return(winner) 
            
            board, winner, counter = create_board(board_size), 0, 1
            
            if scenario == 2:
                
                board, winner, counter = create_board(board_size), 0, 1
              
                while winner == 0: 
                    for player in [2, 1]:  
                        board = placeout_scenario_two(board, player, board_size)
                        board = random_placeout_scenario_one(board, player) 
                        counter += 1
                        winner = evaluate(board) 
                        if winner != 0: 
                            break
                return(winner)       
            
    else:
        print('Playing surface does not exist, try again')
        
def save_stats(games, scenario, board_size):
    
    player1wins=0
    player2wins=0
    ties=0 
    
    for game in range(games):
        result=choose_scenario(scenario, board_size)
        if result==-1: ties+=1
        elif result==1: player1wins+=1
        else: player2wins+=1

    return [player1wins, player2wins, ties] # for returning
            
def print_and_save_stats(games, scenario, board_size):
    
     if board_size ==3 or board_size == 5 or board_size == 7:
        if scenario == 1 or scenario == 2:
                        
            player1wins, player2wins, ties = save_stats(games, scenario, board_size)
                            
            print('Player 1 wins:',player1wins)
            print('Player 2 wins:',player2wins)
            print('Tie:',ties)
                     
            # Fake dataset
            height = [player1wins, player2wins, ties]
            bars = ('Player 1', 'Player 2', 'Tie')
            y_pos = np.arange(len(bars))
             
            # Create bars and choose color
            plt.bar(y_pos, height, color = (0.5,0.1,0.5,0.6))
             
            # Add title and axis names
            plt.title('My title')
            plt.xlabel('')
            plt.ylabel('')
             
            # Limits for the Y axis
            plt.ylim(0,games)
             
            # Create names
            plt.xticks(y_pos, bars)
             
            # Show graphic
            plt.show()
            
        else:
            print('Scenario does not exist')
 
def main():
    
    try:
        
        games = int(input("How many games do you want to simulate? "))
        board_size = int(input("How big playing surface (3/5/7)? "))
        scenario = int(input('What scenario (1/2)? '))
        
        choose_scenario(scenario, board_size)
            
        print_and_save_stats(games, scenario, board_size)
        
    except ValueError:
        print('You have to answer all the question to start the game, try again')
\$\endgroup\$
2
  • \$\begingroup\$ Welcome to Code Review! The current question title, which states your concerns about the code, is too general to be useful here. Please edit to the site standard, which is for the title to simply state the task accomplished by the code. Please see How do I ask a good question?, as well as How to get the best value out of Code Review: Asking Questions for guidance on writing good question titles. \$\endgroup\$ Jan 9 at 14:35
  • \$\begingroup\$ Thank you @SᴀᴍOnᴇᴌᴀ, I have now edited the question title and I hope it clarifies what I'm asking for and adheres to the standard of the site. \$\endgroup\$
    – Ida
    Jan 9 at 17:14
1
\$\begingroup\$

This looks good.

# creating an empty board
def create_board(board_size):
    return np.zeros((board_size,board_size), dtype=int)

find_placement_scenario_one is both a noisy name and could be simplified. I'd write it like this.

def available_spaces(board): 
    return np.argwhere(board==0)

random_placeout_scenario_one is another odd name. The return statement is also odd, as you are mutating the original board anyway. I'd leave out the return, as this makes it very clear the that the original board is being modified.

def make_random_move(board, player): 
    spaces = available_spaces(board) 
    space = random.choice(spaces)
    board[space] = player 

The row and diagonal checks could be simplified with numpy.

def is_winning_by_row(board, player):
    return any(
       all(row == player)
       for row in board
    )

def is_winning_by_col(board, player):
    return is_winning_by_row(board.transpose(), player)

def is_winning_by_diag(board, player):
    return any(
        all(np.diag(board_rotation) == player)
        for board_rotation in [board, np.fliplr(board)]
    ) 

def is_winning(board, player):
  return (
    is_winning_by_row(board, player) or
    is_winning_by_col(board, player) or
    is_winning_by_diag(board, player)
  )

You could reduce mutation in save_stats().

def save_stats(games, scenario, board_size):   
    game_results = [
        play_game(scenario, board_size) # formerly choose_scenario
        for _ in range(games)
    ]

    player1wins = game_results.count(1)
    player2wins = game_results.count(2)
    ties = game_results.count(-1)

    return player1wins, player2wins, ties

Other

The board size checks are silly. If there is an invalid size, raise an exception as early as possible. Checking this criteria multiple times introduces noise into your code.

Also your random placement for situation 2 appears to only ever place moves in the middle, seems pretty broken.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.