1. Navigate 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

2. Set 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!

3. Select 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!

4. Run 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


class Board:
    def __init__(self):
        self.grid = [" "] * 9

    def display(self):
        print("Current Board:")
        print(f" {self.grid[0]} | {self.grid[1]} | {self.grid[2]}")
        print("---+---+---")
        print(f" {self.grid[3]} | {self.grid[4]} | {self.grid[5]}")
        print("---+---+---")
        print(f" {self.grid[6]} | {self.grid[7]} | {self.grid[8]}")
        print()

    def display_reference(self):
        print("Board positions:")
        print(" 1 | 2 | 3")
        print("---+---+---")
        print(" 4 | 5 | 6")
        print("---+---+---")
        print(" 7 | 8 | 9")
        print()

    def is_full(self):
        return " " not in self.grid

    def make_move(self, position, symbol):
        if not isinstance(position, int) or position < 1 or position > 9:
            print("โŒ Invalid position. Choose a number between 1 and 9.")
            return False
        if self.grid[position - 1] != " ":
            print("โŒ That spot is already taken. Try again.")
            return False
        
        self.grid[position - 1] = symbol
        return True

    def check_winner(self, symbol):
        win_combinations = [
            [0, 1, 2], [3, 4, 5], [6, 7, 8],  # Rows
            [0, 3, 6], [1, 4, 7], [2, 5, 8],  # Columns
            [0, 4, 8], [2, 4, 6]              # Diagonals
        ]
        for combo in win_combinations:
            if (self.grid[combo[0]] == symbol and
                self.grid[combo[1]] == symbol and
                self.grid[combo[2]] == symbol):
                return True
        return False

    def get_empty_positions(self):
        return [i + 1 for i in range(9) if self.grid[i] == " "]


class JupyterTicTacToe:
    def __init__(self):
        self.board = Board()
        self.players = [Player("Player 1", "X"), Player("Player 2", "O")]
        self.current_player_index = 0
        self.game_over = False
        self.winner = None
        
    def get_current_player(self):
        return self.players[self.current_player_index]
    
    def start_game(self):
        """Call this to start a new game"""
        self.board = Board()
        self.current_player_index = 0
        self.game_over = False
        self.winner = None
        
        print("๐ŸŽฎ Welcome to Tic-Tac-Toe! ๐ŸŽฎ")
        print("=" * 30)
        print(f"{self.players[0].name} is '{self.players[0].symbol}'")
        print(f"{self.players[1].name} is '{self.players[1].symbol}'")
        print()
        
        self.board.display_reference()
        self.show_game_state()
    
    def show_game_state(self):
        """Display current game state"""
        self.board.display()
        
        if not self.game_over:
            current_player = self.get_current_player()
            available = self.board.get_empty_positions()
            print(f"๐ŸŽฏ {current_player.name} ({current_player.symbol})'s turn")
            print(f"Available positions: {available}")
            print(f"โžก๏ธ  To make a move, run: game.make_move(position)")
            print(f"    Example: game.make_move(5)")
        
    def make_move(self, position):
        """Make a move at the specified position"""
        if self.game_over:
            print("โŒ Game is over! Start a new game with: game.start_game()")
            return
            
        current_player = self.get_current_player()
        print(f"๐ŸŽฏ {current_player.name} ({current_player.symbol}) plays position {position}")
        
        if self.board.make_move(position, current_player.symbol):
            print("โœ… Move successful!")
            
            # Check for winner
            if self.board.check_winner(current_player.symbol):
                self.game_over = True
                self.winner = current_player
                self.board.display()
                print(f"๐ŸŽ‰๐ŸŽ‰ {current_player.name} ({current_player.symbol}) WINS! ๐ŸŽ‰๐ŸŽ‰")
                print("๐Ÿ”„ Start a new game with: game.start_game()")
                return
            
            # Check for tie
            if self.board.is_full():
                self.game_over = True
                self.board.display()
                print("๐Ÿค It's a TIE! Good game!")
                print("๐Ÿ”„ Start a new game with: game.start_game()")
                return
            
            # Switch players and continue
            self.current_player_index = 1 - self.current_player_index
            self.show_game_state()
        else:
            print("โŒ Invalid move. Try again.")
            self.show_game_state()
    
    def show_help(self):
        """Show help instructions"""
        print("๐Ÿ†˜ HOW TO PLAY:")
        print("=" * 20)
        print("1. Start a new game: game.start_game()")
        print("2. Make moves: game.make_move(1-9)")
        print("3. Check game state: game.show_game_state()")
        print("4. Show this help: game.show_help()")
        print()
        print("๐Ÿ“‹ Board positions are numbered 1-9:")
        self.board.display_reference()


# Create a global game instance
game = JupyterTicTacToe()

# Auto-start the first game
print("๐Ÿš€ Initializing Tic-Tac-Toe for Jupyter Notebook...")
print("=" * 50)
game.start_game()

print("\n๐Ÿ’ก QUICK START:")
print("   Make your first move by running: game.make_move(5)")
print("   Need help? Run: game.show_help()")
game.make_move(1)
๐Ÿš€ Initializing Tic-Tac-Toe for Jupyter Notebook...
==================================================
๐ŸŽฎ Welcome to Tic-Tac-Toe! ๐ŸŽฎ
==============================
Player 1 is 'X'
Player 2 is 'O'

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

Current Board:
   |   |  
---+---+---
   |   |  
---+---+---
   |   |  

๐ŸŽฏ Player 1 (X)'s turn
Available positions: [1, 2, 3, 4, 5, 6, 7, 8, 9]
โžก๏ธ  To make a move, run: game.make_move(position)
    Example: game.make_move(5)

๐Ÿ’ก QUICK START:
   Make your first move by running: game.make_move(5)
   Need help? Run: game.show_help()
๐ŸŽฏ Player 1 (X) plays position 1
โœ… Move successful!
Current Board:
 X |   |  
---+---+---
   |   |  
---+---+---
   |   |  

๐ŸŽฏ Player 2 (O)'s turn
Available positions: [2, 3, 4, 5, 6, 7, 8, 9]
โžก๏ธ  To make a move, run: game.make_move(position)
    Example: game.make_move(5)
game.make_move(3)

๐ŸŽฏ Player 2 (O) plays position 3
โœ… Move successful!
Current Board:
 X | O | O
---+---+---
   | X |  
---+---+---
   |   |  

๐ŸŽฏ Player 1 (X)'s turn
Available positions: [4, 6, 7, 8, 9]
โžก๏ธ  To make a move, run: game.make_move(position)
    Example: game.make_move(5)