opencompletion.com/research/activity46-game-theory-python.yaml
Claude 4e72fed8be
Add game theory programming courses for Python and C
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
2025-11-09 15:36:12 +00:00

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"}