Jupyter Notebook Guide

1. What is a Jupyter Notebook?

A Jupyter notebook is an interactive document that combines code, text, and output in a single file. It’s made up of cells that can contain different types of content.

Cell Types Example:

Cell 1 - Frontmatter (Metadata) [Raw Cell]

---
title: Python Tic Tac Toe Game
comments: false
layout: post
permalink: /games/tictactoe
author: Mihir Bapat, Yash Parikh, Ansh Kumar
---

Raw cells contain plain text that isn’t processed as code or markdown. Here, it contains frontmatter - special metadata that tells our website generator how to display this page.

Cell 2 - Documentation [Markdown Cell]

Markdown cells let you write formatted text, like this explanation. You can include headings, lists, links, and more!

This is where you’d typically explain what your code does.

Cell 3 - Code [Python Cell]

def print_board(board): 
    print("\n") 
    print(" " + board[0] + " | " + board[1] + " | " + board[2]) 
    print("---+---+---") 
    print(" " + board[3] + " | " + board[4] + " | " + board[5]) 
    print("---+---+---") 
    print(" " + board[6] + " | " + board[7] + " | " + board[8]) 
    print("\n") 

# This is your actual tic-tac-toe game code!

Code cells contain executable Python code. When you run these cells, the output appears below them.

2. How to Run the Code Cells

Before we can execute our tic-tac-toe code, we need to set up our environment properly. We’ll be doing a live demo of this process!

Environment Setup Steps:

  • Virtual Environment (venv): You must be working inside a Python virtual environment that has all your required packages installed
  • Kernel Selection: In VS Code, select your venv kernel from the kernel picker in the top-right of the notebook
  • Module Availability: Modules like nbconvert must be installed in your selected venv to work properly
  • Running Cells: Use Shift+Enter or the play button to execute code cells
# Check if you're in the right environment
import sys
print(f"Python path: {sys.executable}")
# This should show your venv path, not system Python!

⚠️ Common Issue

If your code cells aren’t running or modules aren’t found, check that you’ve selected the correct kernel that matches your virtual environment!

3. Understanding Frontmatter

Frontmatter is metadata written in YAML format at the top of your notebook. It tells your website how to process and display the content.

What each frontmatter field means:

  • title: The page title that appears in browsers and search results
  • comments: Whether to enable comments on the webpage (true/false)
  • layout: Which HTML template to use for styling the page
  • permalink: The custom URL path where the page will be accessible
  • author: Who created this content

4. From Notebook to Website

Here’s how your Jupyter notebook becomes a live webpage using the nbconvert module:

📓 Jupyter Notebook (Your .ipynb file with cells) → 🔄 nbconvert (Processes and converts) → 🌐 Web Page (HTML in _posts directory)

What nbconvert does:

  • Reads your notebook: Parses the .ipynb file structure
  • Processes frontmatter: Extracts metadata for website generation
  • Converts cells: Turns code/markdown cells into HTML
  • Applies styling: Uses CSS to make it look good on the web
  • Saves to _posts: Places the HTML file where your website can find it
# Basic nbconvert command
jupyter nbconvert --to html your_notebook.ipynb

# With custom template for your website
jupyter nbconvert --to html --template=custom your_notebook.ipynb

5. The Complete Workflow

👨‍💻 Developer Workflow:

  1. Create notebook: Write your tic-tac-toe game in Jupyter
  2. Add frontmatter: Include metadata in a raw cell
  3. Run nbconvert: Convert notebook to HTML
  4. Website displays: Your game appears at /games/tictactoe

✨ Key Takeaways

Remember these important concepts:

  • Jupyter notebooks combine code, documentation, and output in one file
  • Frontmatter provides metadata that controls how your page appears on the web
  • nbconvert is the bridge between notebooks and websites
  • Cell types (Raw, Markdown, Code) serve different purposes
  • The _posts directory is where your converted HTML files live
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("\n")
        print(" " + self.grid[0] + " | " + self.grid[1] + " | " + self.grid[2])
        print("---+---+---")
        print(" " + self.grid[3] + " | " + self.grid[4] + " | " + self.grid[5])
        print("---+---+---")
        print(" " + self.grid[6] + " | " + self.grid[7] + " | " + self.grid[8])
        print("\n")

    def display_reference(self):
        reference = ["1", "2", "3", "4", "5", "6", "7", "8", "9"]
        print("Board positions:\n")
        print("\n")
        print(" " + reference[0] + " | " + reference[1] + " | " + reference[2])
        print("---+---+---")
        print(" " + reference[3] + " | " + reference[4] + " | " + reference[5])
        print("---+---+---")
        print(" " + reference[6] + " | " + reference[7] + " | " + reference[8])
        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 > 8:
            print("Invalid position. Choose a number between 1 and 9.")
            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):
        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


class TicTacToe:
    def __init__(self, player1, player2):
        self.board = Board()
        self.players = [player1, player2]
        self.current_player = player1

    def switch_player(self):
        self.current_player = (
            self.players[1] if self.current_player == self.players[0] else self.players[0]
        )

    def play(self):
        print("Welcome to 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("Players take turns choosing a position (1–9).\n")

        self.board.display_reference()
        self.board.display()

        while True:
            try:
                move = int(input(f"{self.current_player.name} ({self.current_player.symbol}), enter your move (1-9): "))
            except ValueError:
                print("Invalid input. Please enter a number from 1 to 9.")
                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!")
                break

            if self.board.is_full():
                print("It's a tie!")
                break

            self.switch_player()


if __name__ == "__main__":
    player1 = Player("Player 1", "X")
    player2 = Player("Player 2", "O")
    game = TicTacToe(player1, player2)
    game.play()