1Navigate to the Notebook

Open the tic-tac-toe notebook file:

_notebooks/Foundation/F-projects/2025-08-18-tictactoe-game.ipynb

You can do this by using VSCode's file explorer

2Set Up Virtual Environment

Run the virtual environment setup script:

./scripts/venv.sh

This script will:

  • Create or activate your Python virtual environment
  • Install necessary packages
  • Prepare your environment for running Jupyter notebooks

⚠️ Important

Make sure you're in your project root directory when running this command!

3Select the Correct Kernel

In VS Code or Jupyter, select your virtual environment kernel:

  1. Click on "Select Kernel" (usually in the top-right of the notebook)
  2. Choose "Python Environments"
  3. Select your venv kernel from the list

💡 Pro Tip

The kernel should show your venv path, not system Python!

4Run the Game

Execute the code cells to start playing:

  • Click the play button next to each cell
  • Follow the game prompts in the output

🎉 Success!

You're ready to play! Choose positions 1-9 to make your moves.

🔧Troubleshooting

Common Issues

If the game doesn't run, check that you've selected the correct venv kernel and that all packages are installed in your virtual environment.

class Player:
    def __init__(self, name, symbol):
        self.name = name
        self.symbol = symbol
        self.wins = 0
        self.losses = 0
        self.ties = 0
    
    def add_win(self):
        self.wins += 1
    
    def add_loss(self):
        self.losses += 1
    
    def add_tie(self):
        self.ties += 1
    
    def get_stats(self):
        total_games = self.wins + self.losses + self.ties
        if total_games == 0:
            return f"{self.name}: No games played"
        
        win_rate = (self.wins / total_games) * 100
        return f"{self.name}: {self.wins}W-{self.losses}L-{self.ties}T ({win_rate:.1f}% win rate)"

class Board:
    def __init__(self, size=3):
        self.size = size
        self.grid = [" "] * (size * size)
    
    def display(self):
        print("\n")
        for row in range(self.size):
            row_str = " "
            for col in range(self.size):
                index = row * self.size + col
                row_str += self.grid[index]
                if col < self.size - 1:
                    row_str += " | "
            print(row_str)
            
            if row < self.size - 1:
                separator = "---" + "+---" * (self.size - 1)
                print(separator)
        print("\n")
    
    def display_reference(self):
        print("Board positions:\n")
        for row in range(self.size):
            row_str = " "
            for col in range(self.size):
                position = row * self.size + col + 1
                row_str += str(position).rjust(2)
                if col < self.size - 1:
                    row_str += " |"
            print(row_str)
            
            if row < self.size - 1:
                separator = "----" + "+----" * (self.size - 1)
                print(separator)
        print("\n")
    
    def is_full(self):
        return " " not in self.grid
    
    def make_move(self, position, symbol):
        index = position - 1
        if index < 0 or index >= len(self.grid):
            print(f"Invalid position. Choose a number between 1 and {len(self.grid)}.")
            return False
        if self.grid[index] != " ":
            print("That spot is already taken. Try again.")
            return False
        self.grid[index] = symbol
        return True
    
    def check_winner(self, symbol):
        # Check rows
        for row in range(self.size):
            if all(self.grid[row * self.size + col] == symbol for col in range(self.size)):
                return True
        
        # Check columns
        for col in range(self.size):
            if all(self.grid[row * self.size + col] == symbol for row in range(self.size)):
                return True
        
        # Check main diagonal (top-left to bottom-right)
        if all(self.grid[i * self.size + i] == symbol for i in range(self.size)):
            return True
        
        # Check anti-diagonal (top-right to bottom-left)
        if all(self.grid[i * self.size + (self.size - 1 - i)] == symbol for i in range(self.size)):
            return True
        
        return False

class TicTacToe:
    def __init__(self, player1, player2, board_size=3):
        self.board = Board(board_size)
        self.players = [player1, player2]
        self.current_player = player1
        self.board_size = board_size
        self.game_number = 1
    
    def switch_player(self):
        self.current_player = (
            self.players[1] if self.current_player == self.players[0] 
            else self.players[0]
        )
    
    def display_scores(self):
        print("\n" + "="*50)
        print("CURRENT SCORES:")
        print("="*50)
        for player in self.players:
            print(player.get_stats())
        print("="*50 + "\n")
    
    def reset_board(self):
        self.board = Board(self.board_size)
        self.current_player = self.players[0]  # Reset to player 1
    
    def play_single_game(self):
        print(f"\n--- GAME {self.game_number} ---")
        print(f"Welcome to {self.board_size}x{self.board_size} Tic-Tac-Toe!")
        print(f"{self.players[0].name} is '{self.players[0].symbol}'")
        print(f"{self.players[1].name} is '{self.players[1].symbol}'")
        print(f"Players take turns choosing a position (1–{self.board_size * self.board_size}).\n")
        
        self.board.display_reference()
        self.board.display()
        
        while True:
            try:
                max_position = self.board_size * self.board_size
                move = int(input(f"{self.current_player.name} ({self.current_player.symbol}), enter your move (1-{max_position}): "))
            except ValueError:
                print(f"Invalid input. Please enter a number from 1 to {self.board_size * self.board_size}.")
                continue
            
            if not self.board.make_move(move, self.current_player.symbol):
                continue
            
            self.board.display()
            
            if self.board.check_winner(self.current_player.symbol):
                print(f"{self.current_player.name} ({self.current_player.symbol}) wins!")
                # Update scores
                self.current_player.add_win()
                other_player = self.players[1] if self.current_player == self.players[0] else self.players[0]
                other_player.add_loss()
                return "win"
            
            if self.board.is_full():
                print("It's a tie!")
                # Update scores for both players
                for player in self.players:
                    player.add_tie()
                return "tie"
            
            self.switch_player()
    
    def play(self):
        print("Welcome to Enhanced Tic-Tac-Toe with Score Tracking!")
        
        while True:
            # Play a single game
            result = self.play_single_game()
            self.game_number += 1
            
            # Display updated scores
            self.display_scores()
            
            # Ask if players want to continue
            while True:
                choice = input("Play another game? (y/n): ").lower().strip()
                if choice in ['y', 'yes']:
                    self.reset_board()
                    break
                elif choice in ['n', 'no']:
                    print("\nFinal Scores:")
                    self.display_scores()
                    print("Thanks for playing!")
                    return
                else:
                    print("Please enter 'y' for yes or 'n' for no.")

def get_player_names():
    print("Player Setup")
    
    player1_name = ""
    while not player1_name:
        try:
            player1_name = input("Enter Player 1 name (X): ").strip()
            if not player1_name:
                player1_name = "Player 1"
        except:
            player1_name = "Player 1"
    
    player2_name = ""
    while not player2_name:
        try:
            player2_name = input("Enter Player 2 name (O): ").strip()
            if not player2_name:
                player2_name = "Player 2"
        except:
            player2_name = "Player 2"
    
    return player1_name, player2_name

def get_board_size():
    while True:
        try:
            user_input = input("Enter board size (3 for 3x3, 4 for 4x4, etc.): ")
            user_input = user_input.strip()
            
            # Skip if empty input
            if not user_input:
                print("Please enter a number.")
                continue
                
            size = int(user_input)
            if size < 3:
                print("Board size must be at least 3x3.")
                continue
            if size > 10:
                print("Board size too large. Maximum is 10x10.")
                continue
            return size
        except ValueError:
            print("Invalid input. Please enter a number.")
        except:
            print("Please enter a number.")

def show_menu():
    print("\nGAME MENU")
    print("1. Start new game session")
    print("2. View current scores")
    
    while True:
        try:
            choice = input("Choose an option (1-2): ")
            choice = choice.strip()
            
            if choice == "1" or choice == "2":
                return choice
            elif not choice:
                print("Please enter 1 or 2.")
                continue
            else:
                print("Invalid option. Please choose 1-2.")
        except:
            print("Please enter 1 or 2.")

if __name__ == "__main__":
    print("Enhanced Tic-Tac-Toe with Score Tracking!\n")
    
    # Get player names
    player1_name, player2_name = get_player_names()
    player1 = Player(player1_name, "X")
    player2 = Player(player2_name, "O")
    
    # Get board size
    board_size = get_board_size()
    
    # Create game instance
    game = TicTacToe(player1, player2, board_size)
    
    choice = show_menu()
    
    if choice == "1":
        game.play()
    elif choice == "2":
        game.display_scores()
Enhanced Tic-Tac-Toe with Score Tracking!

Player Setup

GAME MENU
1. Start new game session
2. View current scores
Welcome to Enhanced Tic-Tac-Toe with Score Tracking!

--- GAME 1 ---
Welcome to 3x3 Tic-Tac-Toe!
bob is 'X'
ross is 'O'
Players take turns choosing a position (1–9).

Board positions:

  1 | 2 | 3
----+----+----
  4 | 5 | 6
----+----+----
  7 | 8 | 9




   |   |  
---+---+---
   |   |  
---+---+---
   |   |  




 X |   |  
---+---+---
   |   |  
---+---+---
   |   |  




 X | O |  
---+---+---
   |   |  
---+---+---
   |   |  




 X | O |  
---+---+---
 X |   |  
---+---+---
   |   |  




 X | O |  
---+---+---
 X | O |  
---+---+---
   |   |  




 X | O |  
---+---+---
 X | O |  
---+---+---
 X |   |  


bob (X) wins!

==================================================
CURRENT SCORES:
==================================================
bob: 1W-0L-0T (100.0% win rate)
ross: 0W-1L-0T (0.0% win rate)
==================================================


Final Scores:

==================================================
CURRENT SCORES:
==================================================
bob: 1W-0L-0T (100.0% win rate)
ross: 0W-1L-0T (0.0% win rate)
==================================================

Thanks for playing!