default_max_attempts_per_step: 3
classifier_model: "MODEL_1"
feedback_model: "MODEL_1"
tokens_for_ai_rubric: |
  Evaluate the student's ability to implement game theory concepts in Python.

  Consider:
  - Correct Python syntax
  - Understanding of game theory concepts
  - Code logic and structure
  - Use of appropriate data structures
  - Ability to translate concepts to code

sections:
  - section_id: introduction
    title: Programming Game Theory in Python
    steps:
      - step_id: welcome
        title: Code Meets Strategy
        content_blocks:
          - "# Game Theory Programming with Python 🐍🎮"
          - ""
          - "**Learn Python by implementing game theory!**"
          - ""
          - "You'll learn to:"
          - "✓ Represent games as data structures"
          - "✓ Implement payoff matrices"
          - "✓ Code Prisoner's Dilemma simulations"
          - "✓ Find Nash Equilibria programmatically"
          - "✓ Simulate repeated games with strategies"
          - ""
          - "**Prerequisites:**"
          - "- Basic Python knowledge (variables, functions, loops)"
          - "- Understanding of basic game theory (Nash Equilibrium, Prisoner's Dilemma)"
          - ""
          - "**Why this matters:**"
          - "- Learn to model strategic situations"
          - "- Practice data structures (dictionaries, lists)"
          - "- Build simulations and experiments"
          - "- Apply theory to real code"
        question: Ready to implement game theory in Python?
        tokens_for_ai: Accept positive as 'ready', language preference as 'set_language', else 'off_topic'
        buckets: [ready, set_language, off_topic]
        transitions:
          ready:
            next_section_and_step: payoff_matrix:step_1
          set_language:
            metadata_add: {language: "the-users-response"}
            counts_as_attempt: false
            next_section_and_step: introduction:welcome
          off_topic:
            counts_as_attempt: false
            next_section_and_step: introduction:welcome

  - section_id: payoff_matrix
    title: Representing Games as Data
    steps:
      - step_id: step_1
        title: Payoff Matrix Structure
        content_blocks:
          - "## Representing Payoff Matrices in Python 📊"
          - ""
          - "**The challenge:**"
          - "How do we represent a 2-player game in code?"
          - ""
          - "**Game structure:**"
          - "- Two players (Row, Column)"
          - "- Each has strategies (actions)"
          - "- Each outcome has payoffs for both players"
          - ""
          - "**Conceptual approach:**"
          - "A payoff matrix maps strategy pairs to payoff tuples"
          - "- Input: (player1_strategy, player2_strategy)"
          - "- Output: (player1_payoff, player2_payoff)"
          - ""
          - "**Data structure choice:**"
          - "Python dictionaries are perfect!"
          - "- Keys: tuples of strategy pairs"
          - "- Values: tuples of payoffs"
          - ""
          - "**Example concept (Prisoner's Dilemma):**"
          - "```"
          - "Strategies: 'cooperate' or 'defect'"
          - "Payoffs: (player1_years, player2_years)"
          - "If both cooperate: (-1, -1)"
          - "If both defect: (-2, -2)"
          - "If one defects while other cooperates: (0, -3) or (-3, 0)"
          - "```"
        question: "Write Python code to create a dictionary representing the Prisoner's Dilemma payoff matrix. Use strategy pairs as keys (tuples like ('cooperate', 'defect')) and payoff tuples as values."
        tokens_for_ai: |
          Looking for Python dictionary with:
          - Keys: tuples of (player1_strategy, player2_strategy)
          - Values: tuples of (player1_payoff, player2_payoff)
          - Four outcomes: (C,C), (C,D), (D,C), (D,D)

          Correct payoffs (years in prison):
          - ('cooperate', 'cooperate'): (-1, -1)
          - ('cooperate', 'defect'): (-3, 0)
          - ('defect', 'cooperate'): (0, -3)
          - ('defect', 'defect'): (-2, -2)

          Categorize as:
          - correct: Proper dictionary with all 4 outcomes and correct payoffs
          - correct_structure: Right structure, minor payoff errors
          - uses_dictionary: Uses dict but wrong format
          - wrong_approach: Different data structure
          - needs_help: Very basic or confused
          - set_language: Language preference
          - off_topic: Unrelated
        feedback_tokens_for_ai: |
          If correct:
          - Excellent! Dictionary maps strategy pairs to payoffs perfectly.
          - This structure makes lookups easy.
          - Show how to access: payoff_matrix[('cooperate', 'defect')] → (-3, 0)

          If structure right but payoffs wrong:
          - Great structure! But check payoffs:
          - Both cooperate: (-1, -1) - best mutual outcome
          - Both defect: (-2, -2) - mutual punishment
          - One defects: (0, -3) or (-3, 0) - betrayal

          If wrong approach:
          - Show correct dictionary structure with example.
          - Explain why dict with tuple keys is elegant for this.
        buckets: [correct, correct_structure, uses_dictionary, wrong_approach, needs_help, set_language, off_topic]
        transitions:
          correct:
            ai_feedback:
              tokens_for_ai: |
                Perfect implementation!
                Your dictionary elegantly maps strategy pairs to payoffs.
                Access is simple: matrix[('cooperate', 'defect')] gives (-3, 0).
                This structure scales to more complex games!
            metadata_add: {score: "n+2", concepts_mastered: "n+1"}
            next_section_and_step: payoff_matrix:step_2
          correct_structure:
            ai_feedback:
              tokens_for_ai: |
                Great structure! Minor payoff correction needed:
                - Both cooperate: (-1, -1)
                - Both defect: (-2, -2)
                - One defects: betrayer gets 0, cooperator gets -3
                Show the corrected version.
            metadata_add: {score: "n+1"}
            next_section_and_step: payoff_matrix:step_2
          uses_dictionary:
            ai_feedback:
              tokens_for_ai: |
                Good use of dictionary!
                For game matrices, use tuple keys:
                payoff_matrix = {
                  ('cooperate', 'cooperate'): (-1, -1),
                  ('cooperate', 'defect'): (-3, 0),
                  ...
                }
            next_section_and_step: payoff_matrix:step_1
          wrong_approach:
            ai_feedback:
              tokens_for_ai: |
                Python dictionaries with tuple keys work best!
                Example format:
                game = {('action1', 'action2'): (payoff1, payoff2)}
                This allows easy lookup of any strategy combination.
            next_section_and_step: payoff_matrix:step_1
          needs_help:
            content_blocks:
              - "Start with: game = {}"
              - "Add entries like: ('cooperate', 'cooperate'): (-1, -1)"
              - "You need 4 entries total for all strategy combinations"
            next_section_and_step: payoff_matrix:step_1
          set_language:
            metadata_add: {language: "the-users-response"}
            counts_as_attempt: false
            next_section_and_step: payoff_matrix:step_1
          off_topic:
            next_section_and_step: payoff_matrix:step_1

      - step_id: step_2
        title: Querying the Matrix
        content_blocks:
          - "## Using the Payoff Matrix 🔍"
          - ""
          - "**Now that you have a payoff matrix, let's use it!**"
          - ""
          - "**Task:** Write a function that determines outcomes"
          - ""
          - "**Function requirements:**"
          - "- Name: `get_payoffs`"
          - "- Parameters: `payoff_matrix`, `player1_action`, `player2_action`"
          - "- Returns: tuple of (player1_payoff, player2_payoff)"
          - ""
          - "**What the function does:**"
          - "Looks up the payoffs for the given strategy combination"
          - ""
          - "**Think about:**"
          - "- How do you access dictionary values?"
          - "- How do you create the lookup key from the two actions?"
        question: "Write a Python function called `get_payoffs` that takes a payoff matrix dictionary and two player actions, then returns the payoff tuple for that strategy combination."
        tokens_for_ai: |
          Looking for function that:
          - Takes 3 parameters: payoff_matrix (dict), player1_action, player2_action
          - Creates tuple key: (player1_action, player2_action)
          - Returns: payoff_matrix[(player1_action, player2_action)]

          Acceptable variations:
          - def get_payoffs(matrix, p1, p2): return matrix[(p1, p2)]
          - def get_payoffs(payoff_matrix, action1, action2): ...

          Categorize as:
          - correct: Proper function with correct lookup
          - correct_logic: Right idea, minor syntax issues
          - missing_tuple: Tries to lookup without creating tuple key
          - confused: Wrong approach
          - set_language: Language preference
          - off_topic: Unrelated
        feedback_tokens_for_ai: |
          If correct:
          - Perfect! Your function correctly creates a tuple key and looks it up.
          - Example: get_payoffs(game, 'cooperate', 'defect') → (-3, 0)
          - Clean, simple, and reusable!

          If correct logic but syntax issues:
          - Right approach! Small syntax fix needed.
          - Show corrected version.
          - Explain the fix.

          If missing tuple:
          - Remember: dictionary keys are tuples!
          - Need to create (player1_action, player2_action) first.
          - Then look it up in the matrix.
        buckets: [correct, correct_logic, missing_tuple, confused, set_language, off_topic]
        transitions:
          correct:
            ai_feedback:
              tokens_for_ai: |
                Excellent function!
                Your code cleanly creates the tuple key and returns the payoffs.
                This abstraction makes game simulation much easier.
                You can now query any strategy combination!
            metadata_add: {score: "n+2", concepts_mastered: "n+1"}
            next_section_and_step: simulation:step_1
          correct_logic:
            ai_feedback:
              tokens_for_ai: |
                Great logic! Minor syntax adjustment:
                Show corrected function.
                Explain what was fixed and why it matters.
            metadata_add: {score: "n+1"}
            next_section_and_step: simulation:step_1
          missing_tuple:
            ai_feedback:
              tokens_for_ai: |
                Close! Don't forget to create the tuple key:

                def get_payoffs(payoff_matrix, p1_action, p2_action):
                    key = (p1_action, p2_action)
                    return payoff_matrix[key]
            next_section_and_step: payoff_matrix:step_2
          confused:
            content_blocks:
              - "A function that takes the matrix and both actions"
              - "Creates a tuple from the two actions: (action1, action2)"
              - "Uses that tuple to look up the payoffs in the dictionary"
            next_section_and_step: payoff_matrix:step_2
          set_language:
            metadata_add: {language: "the-users-response"}
            counts_as_attempt: false
            next_section_and_step: payoff_matrix:step_2
          off_topic:
            next_section_and_step: payoff_matrix:step_2

  - section_id: simulation
    title: Simulating Strategic Interactions
    steps:
      - step_id: step_1
        title: One-Shot Game Simulator
        content_blocks:
          - "## Simulating Game Outcomes 🎲"
          - ""
          - "**Building a simple game simulator**"
          - ""
          - "**Requirements:**"
          - "- Function name: `play_game`"
          - "- Parameters: `payoff_matrix`, `strategy1`, `strategy2`"
          - "- Should call your `get_payoffs` function"
          - "- Print the outcome in a readable format"
          - "- Return the payoffs"
          - ""
          - "**Example output format:**"
          - "```"
          - "Player 1 chose: cooperate"
          - "Player 2 chose: defect"
          - "Payoffs: Player 1 = -3, Player 2 = 0"
          - "```"
          - ""
          - "**Conceptual flow:**"
          - "1. Get payoffs using your get_payoffs function"
          - "2. Display what each player chose"
          - "3. Display the resulting payoffs"
          - "4. Return the payoffs for further use"
        question: "Write a `play_game` function that simulates one round of a game, prints the outcome, and returns the payoffs. Use your `get_payoffs` function from earlier."
        tokens_for_ai: |
          Looking for function that:
          - Calls get_payoffs(payoff_matrix, strategy1, strategy2)
          - Prints player choices and payoffs
          - Returns the payoff tuple

          Should show understanding of:
          - Function composition (using get_payoffs)
          - Print statements for output
          - Returning values

          Categorize as:
          - correct: Complete function with print and return
          - missing_print: Has logic but doesn't print
          - missing_return: Prints but doesn't return
          - correct_concept: Right idea, minor issues
          - confused: Wrong approach
          - set_language: Language preference
          - off_topic: Unrelated
        feedback_tokens_for_ai: |
          If correct:
          - Excellent! Your simulator uses function composition nicely.
          - The print statements make outcomes clear.
          - Returning payoffs allows chaining simulations.
          - This is how game theory research is done programmatically!

          If missing print:
          - Good logic! Add print statements to show:
          - What each player chose
          - The resulting payoffs
          - Makes debugging and understanding easier!

          If missing return:
          - Good output! But also return the payoffs.
          - This lets you use the function in larger simulations.
          - return payoffs at the end.

          Show complete example if needed.
        buckets: [correct, missing_print, missing_return, correct_concept, confused, set_language, off_topic]
        transitions:
          correct:
            ai_feedback:
              tokens_for_ai: |
                Perfect simulator!
                You've built function composition (play_game uses get_payoffs).
                Print statements provide visibility.
                Return value enables further analysis.
                You're ready for repeated game simulation!
            metadata_add: {score: "n+2", concepts_mastered: "n+1"}
            next_section_and_step: repeated_games:step_1
          missing_print:
            ai_feedback:
              tokens_for_ai: |
                Good structure! Add print statements:
                print(f"Player 1 chose: {strategy1}")
                print(f"Player 2 chose: {strategy2}")
                print(f"Payoffs: Player 1 = {payoffs[0]}, Player 2 = {payoffs[1]}")
                Makes the simulation observable!
            metadata_add: {score: "n+1"}
            next_section_and_step: repeated_games:step_1
          missing_return:
            ai_feedback:
              tokens_for_ai: |
                Great output! Just add:
                return payoffs
                This lets you accumulate results over many rounds!
            metadata_add: {score: "n+1"}
            next_section_and_step: repeated_games:step_1
          correct_concept:
            ai_feedback:
              tokens_for_ai: |
                Right approach! Small improvements:
                Show polished version.
                Explain the refinements.
            next_section_and_step: repeated_games:step_1
          confused:
            content_blocks:
              - "Your function should:"
              - "1. Call get_payoffs to get the payoffs"
              - "2. Print what each player chose"
              - "3. Print the payoffs"
              - "4. Return the payoffs tuple"
            next_section_and_step: simulation:step_1
          set_language:
            metadata_add: {language: "the-users-response"}
            counts_as_attempt: false
            next_section_and_step: simulation:step_1
          off_topic:
            next_section_and_step: simulation:step_1

  - section_id: repeated_games
    title: Repeated Game Strategies
    steps:
      - step_id: step_1
        title: Tit-for-Tat Strategy
        content_blocks:
          - "## Implementing Strategic Behavior 🔄"
          - ""
          - "**The Tit-for-Tat Strategy:**"
          - "1. Start with cooperation"
          - "2. Then copy opponent's previous move"
          - ""
          - "**Implementation challenge:**"
          - "Create a function that implements Tit-for-Tat logic"
          - ""
          - "**Function requirements:**"
          - "- Name: `tit_for_tat`"
          - "- Parameter: `opponent_last_move` (or None for first move)"
          - "- Returns: 'cooperate' or 'defect'"
          - ""
          - "**Logic:**"
          - "- If it's the first move (opponent_last_move is None): return 'cooperate'"
          - "- Otherwise: return whatever the opponent played last"
          - ""
          - "**Why this is powerful:**"
          - "- Nice (starts with cooperation)"
          - "- Retaliatory (punishes defection)"
          - "- Forgiving (returns to cooperation)"
          - "- Simple to understand and implement"
        question: "Write a `tit_for_tat` function that takes an opponent's last move (or None for first round) and returns the appropriate strategy according to Tit-for-Tat logic."
        tokens_for_ai: |
          Correct logic:
          - If opponent_last_move is None: return 'cooperate'
          - Else: return opponent_last_move

          Acceptable implementations:
          - Simple if/else
          - Ternary operator
          - Return with 'or' default

          Categorize as:
          - correct: Proper Tit-for-Tat logic
          - correct_logic: Right idea, minor syntax
          - wrong_first_move: Doesn't handle None case
          - always_cooperates: Ignores opponent's move
          - confused: Wrong logic
          - set_language: Language preference
          - off_topic: Unrelated
        feedback_tokens_for_ai: |
          If correct:
          - Perfect Tit-for-Tat implementation!
          - First move: cooperate (nice)
          - After: copy opponent (retaliatory & forgiving)
          - This won Axelrod's tournament!
          - Show usage example.

          If correct logic:
          - Great logic! Small syntax refinement:
          - Show corrected version.

          If wrong first move:
          - Remember: Tit-for-Tat starts with cooperation!
          - Check if opponent_last_move is None (first round).
          - If None, return 'cooperate'.

          If always cooperates:
          - You need to copy the opponent's move!
          - After first round, return opponent_last_move.
          - That's what makes it "tit for tat"!
        buckets: [correct, correct_logic, wrong_first_move, always_cooperates, confused, set_language, off_topic]
        transitions:
          correct:
            ai_feedback:
              tokens_for_ai: |
                Excellent Tit-for-Tat implementation!
                Your code captures the strategy perfectly:
                - Nice: starts with cooperation
                - Retaliatory: copies opponent's defection
                - Forgiving: copies opponent's return to cooperation
                This simple strategy is remarkably effective!
            metadata_add: {score: "n+2", concepts_mastered: "n+1", activity_completed: "true"}
          correct_logic:
            ai_feedback:
              tokens_for_ai: |
                Great logic! Minor polish:
                Show refined version.
                Your understanding of the strategy is solid!
            metadata_add: {score: "n+1", activity_completed: "true"}
          wrong_first_move:
            ai_feedback:
              tokens_for_ai: |
                Almost there! Handle the first move:

                def tit_for_tat(opponent_last_move):
                    if opponent_last_move is None:
                        return 'cooperate'  # Be nice first
                    return opponent_last_move  # Then copy
            next_section_and_step: repeated_games:step_1
          always_cooperates:
            ai_feedback:
              tokens_for_ai: |
                That's "always cooperate," not Tit-for-Tat!
                Tit-for-Tat must COPY the opponent's last move.
                Only the FIRST move is automatically cooperate.
            next_section_and_step: repeated_games:step_1
          confused:
            content_blocks:
              - "Tit-for-Tat logic:"
              - "1. First move (when opponent_last_move is None): cooperate"
              - "2. All other moves: copy opponent's last move"
              - "Use an if statement to check for None"
            next_section_and_step: repeated_games:step_1
          set_language:
            metadata_add: {language: "the-users-response"}
            counts_as_attempt: false
            next_section_and_step: repeated_games:step_1
          off_topic:
            metadata_add: {activity_completed: "true"}
