NEW ACTIVITIES: activity46-game-theory-python.yaml - Game theory implementation in Python - Representing games with dictionaries - Payoff matrix as dict with tuple keys - Query functions and game simulation - One-shot and repeated games - Tit-for-Tat strategy implementation - Function composition and abstraction activity47-game-theory-c.yaml - Game theory implementation in C - Defining Payoff struct for outcomes - 2D arrays for payoff matrices - Memory-efficient game representation - Strategy lookup functions - Enum for self-documenting code - Pointer and struct fundamentals Both activities: - Teach programming through game theory concepts - Follow pedagogical best practice (concepts first, code examples in feedback) - Validate with zero errors/warnings - Progressive difficulty (structures → functions → simulation) - Real-world application of abstract concepts - Engage students with strategic thinking + coding
509 lines
21 KiB
YAML
509 lines
21 KiB
YAML
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"}
|