Games
Different negotiation scenario implementations for various research contexts.
Negotiation Games
Complete collection of negotiation game implementations for the platform.
This module provides access to all available negotiation game types, from simple bilateral price negotiations to complex multi-issue resource allocation scenarios. Each game implements the BaseGame interface while providing unique mechanics, scoring systems, and strategic challenges.
- Available Games:
BaseGame: Abstract foundation for all negotiation implementations
CompanyCarGame: Bilateral price negotiation with market dynamics
ResourceAllocationGame: Multi-resource distribution between teams
IntegrativeNegotiationsGame: Multi-issue office space and responsibility allocation
- Game Categories:
Bilateral Negotiations: Two-party scenarios with clear opposing interests - CompanyCarGame: Buyer vs. Seller price negotiations
Multi-Issue Negotiations: Complex scenarios with multiple decision points - IntegrativeNegotiationsGame: IT vs. Marketing office resource allocation
Resource Distribution: Allocation of limited resources between parties - ResourceAllocationGame: Development vs. Marketing team resource sharing
- Design Philosophy:
All games follow consistent design principles: - Clear role definitions with distinct interests and priorities - Realistic constraints and market dynamics - BATNA (Best Alternative to Negotiated Agreement) mechanisms - Win-win outcome possibilities requiring creative problem-solving - Structured proposal systems for clear communication - Comprehensive utility tracking and performance analysis
- Usage Patterns:
Games can be used individually for specific research questions or combined in comparative studies to analyze negotiation strategies across different scenarios and complexity levels.
Example
>>> from negotiation_platform.games import (
... CompanyCarGame, ResourceAllocationGame, IntegrativeNegotiationsGame
... )
>>>
>>> # Simple bilateral price negotiation
>>> car_config = {"starting_price": 42000, "rounds": 5}
>>> car_game = CompanyCarGame(car_config)
>>>
>>> # Complex multi-resource allocation
>>> resource_config = {"total_gpus": 10, "total_budget": 100000}
>>> resource_game = ResourceAllocationGame(resource_config)
>>>
>>> # Multi-issue integrative negotiation
>>> office_config = {"rounds": 8, "batna_decay": 0.02}
>>> office_game = IntegrativeNegotiationsGame(office_config)
- Research Applications:
AI negotiation strategy development and testing
Comparative analysis of negotiation algorithms
Human-AI negotiation interaction studies
Game theory and mechanism design research
Organizational behavior and team dynamics analysis
- class negotiation_platform.games.BaseGame(game_id: str, config: Dict[str, Any])[source]
Bases:
ABCAbstract base class defining the interface and common infrastructure for all negotiation games.
BaseGame provides the template and shared functionality for all negotiation game types within the platform. It implements common game management features while defining abstract methods that concrete games must implement for game-specific logic.
- Design Pattern:
Implements the Template Method pattern where this base class handles common game lifecycle management (state tracking, action history, round counting) while delegating game-specific logic to concrete implementations.
- Key Responsibilities:
Game state management and lifecycle tracking
Player registration and management
Action history maintenance and validation
Round counting and termination detection
Common configuration parameter handling
Abstract interface definition for game-specific logic
- actions_history
Complete chronological action log.
- Type:
List[PlayerAction]
- Abstract Methods:
Concrete games must implement: - initialize_game(): Set up initial game state with players - is_valid_action(): Validate player actions against game rules - process_action(): Update game state based on valid actions - check_end_conditions(): Determine if game should terminate - calculate_scores(): Compute final player scores - get_game_prompt(): Generate player-specific prompts
- Game Lifecycle:
Construction: Game created with configuration
Initialization: Players assigned and game state prepared
Active Phase: Players take turns, actions processed
Termination: End conditions met or max rounds reached
Scoring: Final scores calculated and results prepared
Example
>>> class CustomGame(BaseGame): ... def initialize_game(self, players): ... self.players = players ... self.state = GameState.ACTIVE ... return True ... # ... implement other abstract methods >>> game = CustomGame("game_1", {"max_rounds": 5}) >>> success = game.initialize_game(["player1", "player2"])
Note
This is an abstract class and cannot be instantiated directly. Use concrete implementations like CompanyCarGame, ResourceAllocationGame, etc.
- __init__(game_id: str, config: Dict[str, Any])[source]
Initialize a new game instance with identifier and configuration.
Sets up the basic game infrastructure including state tracking, player management, and configuration storage. The game starts in WAITING state until players are assigned via initialize_game().
- Parameters:
game_id (str) – Unique identifier for this game instance. Used for logging, result tracking, and debugging. Should be unique across all concurrent game sessions.
config (Dict[str, Any]) –
Configuration dictionary containing game parameters. Common parameters include:
max_rounds (int): Maximum negotiation rounds (default: 10)
time_limit (int): Session time limit in seconds
game_specific_params: Parameters unique to the game type
- Initialization Process:
Stores game identifier and configuration
Sets initial state to WAITING
Initializes empty player list
Resets round counter to 0
Sets up empty action history
Prepares game data dictionary for game-specific state
Example
>>> config = {"max_rounds": 5, "time_limit": 1800} >>> game = ConcreteGame("negotiation_001", config) >>> print(game.state) GameState.WAITING >>> print(game.max_rounds) 5
Note
After construction, call initialize_game() to assign players and transition to ACTIVE state before game play can begin.
- abstract is_valid_action(player: str, action: Dict[str, Any], game_state: Dict[str, Any]) bool[source]
Check if an action is valid in current game state
- abstract process_action(action: PlayerAction) Dict[str, Any][source]
Process a player action and update game state
- abstract get_game_prompt(player_id: str) str[source]
Get the current game prompt for a specific player
- add_action(action: PlayerAction)[source]
Add a player action to the game’s chronological history log.
Maintains a complete record of all player actions taken during the negotiation session for analysis, metrics calculation, and debugging.
- Parameters:
action (PlayerAction) – The player action to add to history. Must contain player_id, action_type, action_data, timestamp, and round_number for complete action tracking.
Example
>>> action = PlayerAction( ... player_id="player1", ... action_type="offer", ... action_data={"price": 42000}, ... timestamp=1609459200.0, ... round_number=3 ... ) >>> game.add_action(action)
- get_game_info() Dict[str, Any][source]
Retrieve comprehensive information about the current game state.
Provides a structured overview of the game’s current status including identifiers, state information, player list, round tracking, and configuration parameters. Useful for debugging, logging, and analysis.
- Returns:
- Dictionary containing:
game_id (str): Unique game identifier
state (str): Current game state (waiting/active/completed/failed)
players (List[str]): List of participating player identifiers
current_round (int): Current round number (0-based)
max_rounds (int): Maximum rounds before forced termination
config (Dict[str, Any]): Complete game configuration parameters
- Return type:
Dict[str, Any]
Example
>>> info = game.get_game_info() >>> print(f"Game {info['game_id']} is in {info['state']} state") >>> print(f"Round {info['current_round']}/{info['max_rounds']}") Game negotiation_001 is in active state Round 3/5
- class negotiation_platform.games.CompanyCarGame(config: Dict[str, Any])[source]
Bases:
BaseGameCompany car price negotiation game implementing realistic bilateral bargaining dynamics.
This class manages structured price negotiations between a buyer and seller for a company vehicle purchase. The game incorporates realistic market dynamics, time pressure through BATNA decay, and strategic guidance to create authentic negotiation experiences.
The game simulates a common business scenario where organizations must negotiate vehicle purchases, balancing budget constraints with value optimization. Both parties have alternatives (BATNAs) that become less attractive over time, encouraging timely agreement.
- Key Features:
Realistic price negotiation with market constraints
Dynamic BATNA values that decay to simulate time pressure
Strategic guidance system with contextual prompts
JSON-based structured proposal system for clear communication
Win-win outcome detection based on surplus above BATNA values
Comprehensive utility tracking and performance analysis
- Game Mechanics:
Buyer and seller alternate making price proposals
Each proposal is validated against budget and cost constraints
Players can make offers, counteroffers, or accept/reject proposals
BATNA values decay each round to encourage timely resolution
Game ends on acceptance or maximum rounds reached
Final utilities calculated based on achieved price vs. BATNA
Example
>>> config = { ... "starting_price": 42000, ... "buyer_budget": 45000, ... "seller_cost": 38000, ... "buyer_batna": 41000, ... "seller_batna": 39000, ... "rounds": 5, ... "batna_decay": 0.02 ... } >>> game = CompanyCarGame(config) >>> game.initialize_game(["buyer_agent", "seller_agent"]) >>> >>> # Buyer makes initial offer >>> action = {"type": "offer", "price": 40000} >>> valid = game.is_valid_action("buyer_agent", action) >>> if valid: ... game.process_actions({"buyer_agent": action}, game_state)
- Strategic Considerations:
Buyer Strategy: Start low but stay above seller’s likely cost
Seller Strategy: Start high but consider buyer’s budget limits
Both: Monitor BATNA decay and time pressure effects
Optimal: Find price range where both parties gain vs. their BATNAs
- Outcome Analysis:
Game success measured by: - Agreement reached within round limit - Both parties achieve positive surplus above BATNA - Efficient price discovery within feasible range - Balanced utility distribution between parties
- __init__(config: Dict[str, Any])[source]
Initialize company car negotiation game with configuration parameters.
Sets up the bilateral price negotiation environment with buyer and seller roles, BATNA values, budget constraints, and time decay mechanisms. Validates that all required configuration parameters are provided.
- Parameters:
config (Dict[str, Any]) – Configuration dictionary containing: - starting_price (int): Initial vehicle price for reference - buyer_budget (int): Maximum amount buyer can afford - seller_cost (int): Minimum amount seller needs to receive - buyer_batna (float): Buyer’s best alternative value - seller_batna (float): Seller’s best alternative value - rounds (int): Maximum negotiation rounds allowed - batna_decay (float): Per-round BATNA decay rate (0.0-1.0)
- Raises:
ValueError – If any required configuration field is missing.
Example
>>> config = { ... "starting_price": 42000, ... "buyer_budget": 45000, ... "seller_cost": 38000, ... "buyer_batna": 41000, ... "seller_batna": 39000, ... "rounds": 5, ... "batna_decay": 0.02 ... } >>> game = CompanyCarGame(config)
- validate_json_response(response: str) bool[source]
Validate that a response string contains properly formatted JSON.
Checks if the provided response can be parsed as valid JSON and contains the required “type” field for action identification. Used for input validation before processing player responses.
- Parameters:
response (str) – Raw response string from player to validate.
- Returns:
- True if response is valid JSON with “type” field,
False otherwise.
- Return type:
Example
>>> valid_response = '{"type": "offer", "price": 42000}' >>> game.validate_json_response(valid_response) True >>> invalid_response = 'I want to offer 42000' >>> game.validate_json_response(invalid_response) False
- parse_json_response(response: str) Dict[str, Any][source]
Parse and normalize JSON response from players into standard format.
Extracts decision data from various JSON response formats, handling both direct action format and structured response format. Provides robust error recovery with fallback parsing for malformed responses.
- Parameters:
response (str) – Raw JSON response string from player.
- Returns:
- Parsed response containing:
decision (Dict[str, Any]): Extracted action data with “type” field
raw_response (str): Original response for debugging
- Return type:
Dict[str, Any]
Example
>>> response = '{"type": "offer", "price": 42000}' >>> parsed = game.parse_json_response(response) >>> print(parsed["decision"]["type"]) offer >>> print(parsed["decision"]["price"]) 42000
Note
Falls back to {“type”: “reject”} for unparseable responses to ensure graceful handling of malformed input.
- initialize_game(players: List[str]) Dict[str, Any][source]
Process a single player action and update the game state accordingly.
Handles individual player actions by adding them to history and delegating to the batch process_actions method. Required by BaseGame interface for single-action processing.
- Parameters:
action (PlayerAction) – Player action to process containing player_id, action_type, action_data, timestamp, and round_number.
- Returns:
Updated game state after processing the action.
- Return type:
Dict[str, Any]
Example
>>> action = PlayerAction( ... player_id="buyer", ... action_type="offer", ... action_data={"price": 42000}, ... timestamp=1609459200.0, ... round_number=3 ... ) >>> new_state = game.process_action(action)
- get_current_batna(player: str, round_num: int) float[source]
Calculate time-adjusted BATNA value for specified player and round.
Applies exponential decay to the player’s initial BATNA value based on the current round number, simulating decreasing value of alternatives over time. This creates time pressure encouraging earlier agreements.
- Parameters:
- Returns:
Time-adjusted BATNA value for the specified round.
- Return type:
Example
>>> # Initial buyer BATNA: 41000, decay rate: 0.02 >>> round_1_batna = game.get_current_batna("buyer", 1) >>> print(f"Round 1 BATNA: €{round_1_batna:,.0f}") Round 1 BATNA: €40,180 >>> round_3_batna = game.get_current_batna("buyer", 3) >>> print(f"Round 3 BATNA: €{round_3_batna:,.0f}") Round 3 BATNA: €39,572
Note
BATNA decay formula: initial_batna * (1 - decay_rate)^round_num
- is_valid_action(player: str, action: Dict[str, Any], game_state: Dict[str, Any]) bool[source]
Validate player action against game rules with flexible format support.
Validates negotiation actions including offers, acceptances, and rejections. Supports both direct action format and structured response format with “decision” wrapper. Ensures actions comply with game constraints including proposal limits and valid action types.
- Parameters:
player (str) – Identifier of the player taking the action. Must be registered buyer or seller.
action (Dict[str, Any]) – Action data to validate. Supported formats: - Direct: {“type”: “offer”, “price”: 42000} - Structured: {“decision”: {“type”: “offer”, “price”: 42000}}
game_state (Dict[str, Any]) – Current game state containing round information, proposal counts, and negotiation history.
- Returns:
True if action is valid and can be processed, False otherwise.
- Return type:
- Validation Rules:
Action must have valid “type” field (offer, accept, reject)
Offers must include numeric “price” field
Player must not exceed proposal limits
Price offers must be positive numeric values
Example
>>> # Valid offer action >>> action = {"type": "offer", "price": 42000} >>> is_valid = game.is_valid_action("buyer", action, game_state) >>> print(is_valid) True
>>> # Valid structured format >>> structured = {"decision": {"type": "accept"}} >>> is_valid = game.is_valid_action("seller", structured, game_state) >>> print(is_valid) True
Note
Invalid actions are logged but do not raise exceptions, allowing graceful handling of malformed AI model responses.
- process_actions(actions: Dict[str, Dict[str, Any]], game_state: Dict[str, Any]) Dict[str, Any][source]
Process simultaneous player actions with proposal tracking and validation.
Handles bilateral negotiation actions including offers, acceptances, and rejections. Enforces proposal limits, validates action compatibility, and determines negotiation outcomes. Updates game state with action results and manages agreement detection.
- Parameters:
actions (Dict[str, Dict[str, Any]]) – Mapping of player identifiers to their action data. Expected format: {“player_id”: {“type”: “offer”, “price”: 42000}}
game_state (Dict[str, Any]) – Current game state containing: - current_round: Round number for tracking - proposal counts: Limits for each player - negotiation history: Previous actions and offers
- Returns:
- Updated game state containing:
agreement_reached: Boolean indicating successful negotiation
final_price: Agreed price if agreement reached
final_utilities: Utility scores for each player
game_over: Boolean indicating termination
Updated proposal counts and action history
- Return type:
Dict[str, Any]
- Processing Logic:
Initialize and validate proposal counters
Process each player’s action with validation
Check for mutual acceptances (agreement)
Handle offers and update last offer tracking
Update proposal counts and game state
Determine if negotiation should continue
- Agreement Detection:
Mutual acceptance: Both players accept in same round
Offer acceptance: One player accepts other’s previous offer
Price agreement: Players converge on acceptable price
Example
>>> actions = { ... "buyer": {"type": "offer", "price": 41000}, ... "seller": {"type": "offer", "price": 43000} ... } >>> new_state = game.process_actions(actions, current_state) >>> print(new_state["agreement_reached"]) False # No agreement, continue negotiating
Note
Players exceeding proposal limits receive rejection responses. Invalid actions are logged and may result in negotiation failure.
- is_game_over(game_state: Dict[str, Any]) bool[source]
Determine if the negotiation game has reached a terminal state.
Checks for various end conditions including agreement reached, maximum rounds exceeded, or explicit rejections that end negotiation.
- Parameters:
game_state (Dict[str, Any]) – Current game state to evaluate.
- Returns:
True if game should terminate, False if negotiation continues.
- Return type:
Example
>>> # Agreement reached >>> game_state = {"agreement_reached": True} >>> game.is_game_over(game_state) True >>> # Maximum rounds exceeded >>> game_state = {"current_round": 6, "agreement_reached": False} >>> game.is_game_over(game_state) # max_rounds = 5 True
- get_winner(game_state: Dict[str, Any]) str | None[source]
Determine the winning player based on positive utility surplus analysis.
Evaluates final utilities and surplus values to identify the player who achieved the highest positive surplus above their BATNA. Only players with positive surplus can be considered winners.
- Parameters:
game_state (Dict[str, Any]) – Final game state with utility data.
- Returns:
- Player identifier of winner, or None if:
No agreement was reached
No player has positive surplus
Utilities are tied or unavailable
- Return type:
Optional[str]
Example
>>> # Buyer achieved higher surplus >>> game_state = { ... "agreement_reached": True, ... "utility_surplus": {"buyer": 1000, "seller": 500} ... } >>> winner = game.get_winner(game_state) >>> print(f"Winner: {winner}") Winner: buyer
- process_action(action: PlayerAction) Dict[str, Any][source]
Process a single player action and update the game state accordingly.
Handles individual player actions by adding them to history and delegating to the batch process_actions method. Required by BaseGame interface for single-action processing.
- Parameters:
action (PlayerAction) – Player action to process containing player_id, action_type, action_data, timestamp, and round_number.
- Returns:
Updated game state after processing the action.
- Return type:
Dict[str, Any]
Example
>>> action = PlayerAction( ... player_id="buyer", ... action_type="offer", ... action_data={"price": 42000}, ... timestamp=1609459200.0, ... round_number=3 ... ) >>> new_state = game.process_action(action)
- check_end_conditions() bool[source]
Check if the negotiation game should terminate based on current state.
Evaluates termination conditions by delegating to the is_game_over method. Required by BaseGame interface for consistent end condition checking across all game implementations.
- Returns:
True if game should end, False if negotiation continues.
- Return type:
Example
>>> game.check_end_conditions() True # If agreement reached or max rounds exceeded
- calculate_scores() Dict[str, float][source]
Calculate final utility scores for all participating players.
Computes final negotiation outcomes based on whether agreement was reached. If successful agreement occurred, returns actual utility values achieved by each player. If negotiation failed, returns zero scores for all players to reflect lack of value creation.
- Returns:
- Mapping of player identifiers to final utility scores.
For successful negotiations, contains actual utility values. For failed negotiations, contains 0.0 for all players.
- Return type:
Example
>>> # Successful price agreement at $42,000 >>> scores = game.calculate_scores() >>> print(scores) {'buyer_player': 7.5, 'seller_player': 8.2}
>>> # Failed negotiation (no agreement) >>> scores = game.calculate_scores() >>> print(scores) {'buyer_player': 0.0, 'seller_player': 0.0}
Note
Required by BaseGame interface for consistent scoring across all negotiation game implementations. Scores reflect relative success in achieving negotiation objectives.
- get_game_prompt(player_id: str) str[source]
Generate comprehensive negotiation prompt with neutral role terminology.
Creates detailed, contextual prompts for car price negotiations using neutral role labels (“Party A”/”Party B”) instead of loaded terms (“buyer”/”seller”) to reduce cognitive bias and role-based behavioral influences. Includes current game state, strategic guidance, and structured action formatting requirements.
- Parameters:
player_id (str) – Identifier of the player requesting the prompt. Must be one of the registered players (buyer or seller).
- Returns:
- Comprehensive negotiation prompt containing:
Neutral role context and scenario description
Current round and proposal count information
BATNA thresholds and acceptance criteria
Opponent’s latest offer (if available)
Available actions and JSON formatting requirements
Strategic guidance for decision making
- Return type:
- Prompt Elements:
Bias-reduced role terminology (Party A/B vs buyer/seller)
Current negotiation state and round tracking
BATNA-based decision criteria and thresholds
Offer history and opponent action visibility
Structured JSON response format requirements
Strategic guidance for proposal and acceptance decisions
Example
>>> prompt = game.get_game_prompt("player1") >>> print("Party A" in prompt) # Neutral terminology True >>> print("JSON" in prompt) # Format requirements True >>> print("BATNA" in prompt) # Strategic guidance True
Note
Returns error message if game is not properly initialized with buyer and seller assignments. Prompts adapt to current game state including round limits and proposal constraints.
- class negotiation_platform.games.ResourceAllocationGame(config: Dict[str, Any])[source]
Bases:
BaseGameComplex multi-resource allocation negotiation between Development and Marketing teams.
This class implements a sophisticated resource distribution scenario where two organizational teams must negotiate the allocation of limited computational and human resources for a shared project. The game simulates realistic organizational resource conflicts with complex interdependencies and trade-offs.
The negotiation involves multiple resource types with different valuations for each team, requiring creative resource sharing and package deals to achieve mutually beneficial outcomes. Teams must balance their specific needs against organizational constraints and counterpart requirements.
- Key Features:
Multi-resource negotiation (GPUs, developers, budget allocation)
Team-specific utility functions with different resource valuations
Complex resource interdependencies and constraints
BATNA values representing alternative resource sources
Structured JSON proposal system for resource requests
Win-win solution detection based on efficient resource utilization
- Resource Types:
GPU Hours: Computational resources for processing tasks - Development Team: High priority for model training and testing - Marketing Team: Moderate priority for data analysis
Developer Hours: Human resource allocation - Development Team: Critical for implementation work - Marketing Team: Needed for integration and campaign development
Budget Allocation: Financial resources for project components - Development Team: Infrastructure and tool costs - Marketing Team: Campaign execution and market research
- Game Mechanics:
Teams propose resource allocation packages
Proposals validated against total resource constraints
Utility calculated based on team-specific resource valuations
Teams can negotiate, trade, or share resources creatively
BATNA decay encourages timely resolution
Success measured by total utility maximization
- team_utilities
Team-specific utility functions defining how each team values different resource combinations.
- Type:
Dict[str, Dict]
- resource_weights
Team-specific importance weights for each resource type in utility calculations.
Example
>>> config = { ... "max_rounds": 5, ... "total_gpus": 10, ... "total_developers": 8, ... "total_budget": 100000, ... "batna_decay": 0.02 ... } >>> game = ResourceAllocationGame(config) >>> game.initialize_game(["dev_team", "marketing_team"]) >>> >>> # Development team proposes resource allocation >>> proposal = { ... "gpu_hours": 7, # High GPU need for development ... "developer_hours": 5, # Core development team ... "budget": 60000 # Infrastructure costs ... } >>> action = {"type": "proposal", "allocation": proposal} >>> valid = game.is_valid_action("dev_team", action)
- Strategic Considerations:
Development Team: Prioritize GPU and developer resources
Marketing Team: Focus on budget and developer support
Both teams: Identify resource trades that create mutual value
Optimal: Find allocations where total utility exceeds individual BATNAs
- Resource Efficiency:
Game encourages: - Creative resource sharing arrangements - Time-based resource allocation (sequential usage) - Hybrid solutions combining different resource types - Recognition of complementary resource needs between teams
- __init__(config: Dict[str, Any])[source]
Initialize resource allocation negotiation game with team configurations.
Sets up multi-resource negotiation between Development and Marketing teams with utility functions, BATNA values, resource constraints, and time decay mechanisms. Validates required configuration parameters.
- Parameters:
config (Dict[str, Any]) – Configuration dictionary containing: - batnas (Dict[str, float]): BATNA values for each team - rounds (int): Maximum negotiation rounds allowed - batna_decay (float): Per-round BATNA decay rate (0.0-1.0) - total_resources (Dict[str, int]): Available resource pools - constraints (Dict): Resource allocation constraints - utility_functions (Dict): Team-specific utility parameters - uncertainty (Dict, optional): Uncertainty parameters
- Raises:
ValueError – If any required configuration field is missing.
Example
>>> config = { ... "batnas": {"development": 50, "marketing": 45}, ... "rounds": 5, ... "batna_decay": 0.02, ... "total_resources": {"gpu": 100, "cpu": 100}, ... "constraints": {"max_gpu_per_team": 80}, ... "utility_functions": { ... "development": {"gpu_coefficient": 0.8, "cpu_coefficient": 0.2}, ... "marketing": {"gpu_coefficient": 0.3, "cpu_coefficient": 0.7} ... } ... } >>> game = ResourceAllocationGame(config)
- validate_json_response(response: str) bool[source]
Validate that a response string contains properly formatted JSON.
Checks if the provided response can be parsed as valid JSON and contains the required “type” field for action identification. Used for input validation before processing team responses.
- Parameters:
response (str) – Raw response string from team to validate.
- Returns:
- True if response is valid JSON with “type” field,
False otherwise.
- Return type:
Example
>>> valid_response = '{"type": "propose", "gpu": 60, "cpu": 40}' >>> game.validate_json_response(valid_response) True >>> invalid_response = 'We want 60 GPU hours' >>> game.validate_json_response(invalid_response) False
- parse_json_response(response: str) Dict[str, Any][source]
Parse and normalize JSON response from teams into standard format.
Extracts decision data from various JSON response formats, handling both direct action format and structured response format. Provides robust error recovery with fallback parsing for malformed responses.
- Parameters:
response (str) – Raw JSON response string from team.
- Returns:
- Parsed response containing:
decision (Dict[str, Any]): Extracted action data with “type” field
raw_response (str): Original response for debugging
- Return type:
Dict[str, Any]
Example
>>> response = '{"type": "propose", "gpu": 60, "cpu": 40}' >>> parsed = game.parse_json_response(response) >>> print(parsed["decision"]["type"]) propose >>> print(parsed["decision"]["gpu"]) 60
Note
Falls back to {“type”: “reject”} for unparseable responses to ensure graceful handling of malformed input.
- initialize_game(players: List[str]) Dict[str, Any][source]
Initialize multi-resource allocation negotiation between development and marketing teams.
Sets up the negotiation environment with randomized role assignments to minimize order bias, initializes team-specific utility functions, establishes resource constraints, and prepares the game state for active negotiation.
- Parameters:
players (List[str]) – List of exactly 2 player identifiers representing the negotiating teams. Order is randomized for role assignment.
- Returns:
- Initial game state containing:
players: List of player identifiers with assigned roles
current_round: Starting round number (1)
max_rounds: Maximum negotiation rounds allowed
total_gpu_hours: Total GPU resources available (10)
total_cpu_hours: Total CPU resources available (10)
private_info: Team-specific utility functions and preferences
resource_history: Empty history for tracking allocations
agreement_reached: False (negotiation not yet concluded)
- Return type:
Dict[str, Any]
- Initialization Process:
Validate exactly 2 players provided
Randomly assign development and marketing roles
Set up team-specific utility functions and preferences
Initialize resource constraints and tracking
Create private information for each team
Prepare negotiation state tracking
- Role Assignment:
Development Team: Higher GPU preference, moderate CPU needs
Marketing Team: Higher CPU preference, moderate GPU needs
Random assignment prevents order bias effects
- Resource Setup:
Total GPU Hours: 10 (must be allocated between teams)
Total CPU Hours: 10 (must be allocated between teams)
Team-specific utility functions for each resource type
Example
>>> players = ["model_a", "model_b"] >>> initial_state = game.initialize_game(players) >>> print(initial_state["total_gpu_hours"]) 10 >>> print("private_info" in initial_state) True
- Raises:
ValueError – If number of players is not exactly 2.
Note
Role assignments are logged for debugging but kept private from players to maintain negotiation authenticity.
- get_current_batna(player: str, round_num: int) float[source]
Calculate time-adjusted BATNA value for specified team and round.
Applies exponential decay to the team’s initial BATNA value based on the current round number, simulating decreasing value of alternative resource sources over time. Creates time pressure encouraging agreement.
- Parameters:
- Returns:
Time-adjusted BATNA value for the specified round.
- Return type:
Example
>>> # Initial development BATNA: 50, decay rate: 0.02 >>> round_1_batna = game.get_current_batna("development", 1) >>> print(f"Round 1 BATNA: {round_1_batna:.1f}") Round 1 BATNA: 49.0 >>> round_3_batna = game.get_current_batna("development", 3) >>> print(f"Round 3 BATNA: {round_3_batna:.1f}") Round 3 BATNA: 48.0
Note
BATNA decay formula: initial_batna * (1 - decay_rate)^round_num
- calculate_utility(player: str, gpu_hours: float, cpu_hours: float, round_num: int) float[source]
Calculate team-specific utility value for proposed resource allocation.
Computes utility scores using configurable team-specific coefficients and uncertainty factors. Each team has different preferences for GPU vs CPU resources based on their operational needs and strategic priorities.
- Parameters:
- Returns:
- Total utility value including base utility and uncertainty factor.
Higher values indicate more attractive proposals for the team.
- Return type:
- Utility Calculation:
Base utility = (gpu_coeff × gpu_hours) + (cpu_coeff × cpu_hours) Final utility = base_utility + random_uncertainty_factor
- Team Coefficients:
Development: Higher GPU coefficient, moderate CPU coefficient
Marketing: Higher CPU coefficient, moderate GPU coefficient
Configurable via utility_functions in game setup
- Uncertainty Factor:
Random value within team-specific bounds to model negotiation uncertainty and prevent deterministic outcomes.
Example
>>> # Development team evaluating GPU-heavy allocation >>> utility = game.calculate_utility("dev_team", 8.0, 2.0, 1) >>> print(f"Development utility: {utility:.1f}") Development utility: 28.3 # Including uncertainty
Note
Uncertainty factors add realism but may cause slight result variations between identical runs. Set narrow bounds for more predictable behavior.
- check_constraints_and_update(gpu_hours: float, cpu_hours: float) None[source]
Validate resource allocation against all constraints and update game state.
Performs comprehensive validation of proposed resource allocation against multiple constraint types including total resource limits, bandwidth constraints, and resource coupling requirements. Updates game state with detailed validation results and error messages.
- Parameters:
- Side Effects:
Updates self.game_data[‘constraint_check’] with detailed validation results including constraint status, violation messages, and resource utilization analysis.
- Constraint Validation:
Total Resource Limit: gpu_hours + cpu_hours ≤ total_resources
GPU Bandwidth: 4×gpu_hours + 4×cpu_hours ≤ gpu_bandwidth
Individual Resource Bounds: Non-negative allocations
Resource Coupling: Interdependency constraints
- Game State Updates:
- Creates or updates ‘constraint_check’ entry containing:
constraints_met: Boolean overall validation result
messages: List of specific constraint violation descriptions
gpu_hours: Validated GPU allocation
cpu_hours: Validated CPU allocation
total_usage: Combined resource utilization
Example
>>> # Valid allocation within all constraints >>> game.check_constraints_and_update(4.0, 6.0) >>> print(game.game_data['constraint_check']['constraints_met']) True
>>> # Invalid allocation exceeding total resources >>> game.check_constraints_and_update(8.0, 12.0) >>> print(game.game_data['constraint_check']['constraints_met']) False >>> print(game.game_data['constraint_check']['messages']) ['Total resources exceeded: 20.0 > 10.0']
Note
This method provides detailed constraint analysis for debugging and user feedback, supporting complex multi-constraint validation scenarios in resource allocation negotiations.
- is_valid_action(player: str, action: Dict[str, Any], game_state: Dict[str, Any]) bool[source]
Validate team action against resource allocation rules and constraints.
Comprehensive validation of negotiation actions including proposals and acceptances. Supports both direct action format and structured response format with “decision” wrapper. Ensures actions comply with resource constraints, proposal limits, and valid action types.
- Parameters:
player (str) – Identifier of the team taking the action. Must be registered development or marketing team.
action (Dict[str, Any]) – Action data to validate. Supported formats: - Direct: {“type”: “propose”, “gpu”: 6, “cpu”: 4} - Structured: {“decision”: {“type”: “propose”, “gpu”: 6, “cpu”: 4}}
game_state (Dict[str, Any]) – Current game state containing round information, proposal counts, and resource constraints.
- Returns:
True if action is valid and can be processed, False otherwise.
- Return type:
- Validation Rules:
Action must have valid “type” field (propose, accept)
Proposals must include numeric “gpu” and “cpu” fields
Resource allocations must respect total resource constraints
GPU + CPU allocations must not exceed available pools
Resource values must be non-negative numbers
Player must not exceed proposal limits
- Resource Constraints:
Total GPU hours available: 10
Total CPU hours available: 10
Individual allocations must be ≤ total resources
Combined team allocations must sum to ≤ totals
Example
>>> # Valid resource proposal >>> action = {"type": "propose", "gpu": 6, "cpu": 4} >>> is_valid = game.is_valid_action("dev_team", action, game_state) >>> print(is_valid) True
>>> # Invalid proposal exceeding resources >>> invalid = {"type": "propose", "gpu": 12, "cpu": 8} >>> is_valid = game.is_valid_action("mkt_team", invalid, game_state) >>> print(is_valid) False
Note
Invalid actions are logged but do not raise exceptions, allowing graceful handling of malformed AI model responses and constraint violations.
- process_actions(actions: Dict[str, Dict[str, Any]], game_state: Dict[str, Any]) Dict[str, Any][source]
Process player actions with proposal limits and enhanced validation.
- is_game_over(game_state: Dict[str, Any]) bool[source]
Determine if the resource allocation negotiation has reached a terminal state.
Checks for various end conditions including resource agreement reached, maximum rounds exceeded, or explicit rejections that end negotiation.
- Parameters:
game_state (Dict[str, Any]) – Current game state to evaluate.
- Returns:
True if game should terminate, False if negotiation continues.
- Return type:
Example
>>> # Agreement reached >>> game_state = {"agreement_reached": True} >>> game.is_game_over(game_state) True >>> # Maximum rounds exceeded >>> game_state = {"current_round": 6, "agreement_reached": False} >>> game.is_game_over(game_state) # max_rounds = 5 True
- get_game_prompt(player_id: str) str[source]
Generate comprehensive resource allocation negotiation prompt for teams.
Creates detailed, contextual prompts for GPU and CPU resource negotiations between Development and Marketing teams. Includes current game state, resource constraints, utility calculations, and structured action formatting requirements. Uses neutral role terminology to minimize cognitive bias.
- Parameters:
player_id (str) – Identifier of the team requesting the prompt. Must be either development or marketing team identifier.
- Returns:
- Comprehensive negotiation prompt containing:
Team-specific role context and resource priorities
Current resource allocation status and constraints
BATNA thresholds and utility calculations
Opponent’s latest proposal (if available)
Available actions and JSON formatting requirements
Strategic guidance for resource optimization
Proposal limits and round tracking information
- Return type:
- Prompt Components:
Neutral role terminology (Team A/B vs development/marketing)
Resource constraint specifications (GPU/CPU limits)
Team-specific utility functions and preferences
Current negotiation state and round progression
BATNA-based acceptance criteria
Structured JSON response format requirements
Strategic recommendations for win-win solutions
- Resource Context:
Total GPU hours: 10 (split between teams)
Total CPU hours: 10 (split between teams)
Team-specific utility calculations
Time-decaying BATNA values
Example
>>> prompt = game.get_game_prompt("dev_team") >>> print("GPU hours" in prompt) # Resource context True >>> print("Team A" in prompt) # Neutral terminology True >>> print("JSON" in prompt) # Format requirements True
Note
Returns error message if game is not properly initialized with team assignments. Prompts adapt to current resource constraints and proposal limits.
- process_action(action: PlayerAction) Dict[str, Any][source]
Process a single team action and update the game state accordingly.
Handles individual team actions by converting to batch format and delegating to the process_actions method. Required by BaseGame interface for single-action processing compatibility.
- Parameters:
action (PlayerAction) – Team action to process containing player_id, action_type, action_data, timestamp, and round_number.
- Returns:
Updated game state after processing the action.
- Return type:
Dict[str, Any]
Example
>>> action = PlayerAction( ... player_id="development", ... action_type="propose", ... action_data={"gpu": 60, "cpu": 40}, ... timestamp=1609459200.0, ... round_number=2 ... ) >>> new_state = game.process_action(action)
- check_end_conditions() bool[source]
Check if the resource allocation negotiation should terminate.
Evaluates termination conditions by delegating to the is_game_over method. Required by BaseGame interface for consistent end condition checking across all game implementations.
- Returns:
True if game should end, False if negotiation continues.
- Return type:
Example
>>> game.check_end_conditions() True # If agreement reached or max rounds exceeded
- calculate_scores() Dict[str, float][source]
Calculate final utility scores for all participating teams.
Returns final utility values if resource agreement was reached, or BATNA values for both teams if negotiation failed. Required by BaseGame interface for consistent scoring across implementations.
- Returns:
- Mapping of team identifiers to final utility
scores. Positive values indicate successful resource allocation.
- Return type:
Example
>>> # Successful negotiation >>> scores = game.calculate_scores() >>> print(scores) {'development': 56.0, 'marketing': 44.0} >>> # Failed negotiation >>> scores = game.calculate_scores() >>> print(scores) {'development': 50.0, 'marketing': 45.0} # BATNA values
- class negotiation_platform.games.IntegrativeNegotiationsGame(config: Dict[str, Any])[source]
Bases:
BaseGameIntegrative negotiations game between IT and Marketing teams with price bargaining logic.
Four issues with point values (unchanged from original): - Server Room Size: 50 sqm (10), 100 sqm (30), 150 sqm (60) - Meeting Room Access: 2 days/week (10), 4 days/week (30), 7 days/week (60) - Cleaning Responsibility: IT handles (10), Shared (30), Outsourced (60) - Branding Visibility: Minimal (10), Moderate (30), Prominent (60)
- __init__(config: Dict[str, Any])[source]
Initialize integrative negotiations game with configuration parameters.
Sets up multi-issue office space negotiation between IT and Marketing teams. Configures team preferences, BATNA values, issue structures, and decay rates based on provided configuration dictionary.
- Parameters:
config (Dict[str, Any]) – Game configuration containing: - batnas (Dict[str, float]): BATNA values for IT and Marketing teams - rounds (int): Maximum negotiation rounds allowed - batna_decay (float or Dict): Decay rate(s) for time pressure - issues (Dict, optional): Issue configurations and point values - weights (Dict, optional): Team preference weights by issue
- Raises:
ValueError – If required configuration fields are missing.
Example
>>> config = { ... "batnas": {"IT": 35, "Marketing": 30}, ... "rounds": 8, ... "batna_decay": 0.02 ... } >>> game = IntegrativeNegotiationsGame(config)
Note
Supports both hardcoded fallback values and flexible configuration for research customization and parameter sensitivity analysis.
- validate_json_response(response: str) bool[source]
Validate that AI model response is properly formatted JSON with required structure.
Performs structural validation of negotiation responses to ensure they contain the minimum required fields for processing. Used as a quick validation step before detailed parsing and action processing.
- Parameters:
response (str) – Raw response string from AI model to validate.
- Returns:
- True if response is valid JSON dict with “type” field,
False for malformed JSON or missing required structure.
- Return type:
- Validation Criteria:
Must be valid JSON syntax
Must parse to a dictionary object
Must contain “type” field for action identification
Example
>>> game.validate_json_response('{"type": "propose", "proposal": {}}') True >>> game.validate_json_response('invalid json') False >>> game.validate_json_response('{"proposal": {}}') False
Note
This is a lightweight validation step. Full semantic validation of proposals and action types occurs in subsequent processing steps.
- parse_json_response(response: str) Dict[str, Any][source]
Parse and extract decision data from AI model JSON responses with error recovery.
Handles multiple response formats and provides robust parsing with graceful error recovery for malformed responses. Extracts decision data and preserves raw response for debugging purposes.
- Parameters:
response (str) – Raw JSON response string from AI model containing negotiation decision and potentially surrounding text.
- Returns:
- Parsed response containing:
decision (Dict): Extracted action data with type and parameters
raw_response (str): Original unmodified response for debugging
- Return type:
Dict[str, Any]
- Response Format Handling:
Pure JSON: {“type”: “propose”, “proposal”: {…}}
Embedded JSON: Text containing JSON objects
Malformed JSON: Fallback to regex extraction and default rejection
- Error Recovery:
JSON parsing errors: Attempts regex extraction of key fields
Missing type field: Defaults to “reject” action
Invalid structure: Returns safe rejection response
Example
>>> response = '{"type": "propose", "proposal": {"server_room": 150}}' >>> parsed = game.parse_json_response(response) >>> print(parsed["decision"]["type"]) "propose" >>> print(parsed["decision"]["proposal"]) {"server_room": 150}
Note
Designed for robustness with AI model responses that may include explanatory text, formatting inconsistencies, or parsing errors.
- initialize_game(players: List[str]) Dict[str, Any][source]
Initialize the integrative negotiation game with randomized role assignments.
Sets up a multi-issue office space negotiation between IT and Marketing teams with randomized role assignment to minimize positional bias. Establishes complete game state including private information, utility functions, and BATNA parameters.
- Parameters:
players (List[str]) – List of exactly 2 player identifiers to participate in the bilateral negotiation.
- Returns:
- Complete initialized game state containing:
game_type (str): “integrative_negotiations”
players (List[str]): Player identifiers
role_assignments (Dict[str, str]): Mapping of roles to players
private_info (Dict[str, Dict]): Individual BATNAs and preferences
public_info (Dict[str, Any]): Shared issue descriptions
game_config (Dict[str, Any]): Configuration parameters
- Return type:
Dict[str, Any]
- Raises:
ValueError – If number of players is not exactly 2.
Example
>>> game = IntegrativeNegotiationsGame(config) >>> state = game.initialize_game(["alice", "bob"]) >>> state["role_assignments"] {"IT": "alice", "Marketing": "bob"}
Note
Role assignment is randomized to prevent systematic positional advantages. The first player is not always IT team.
- get_current_batna(player: str, round_num: int) float[source]
Calculate time-adjusted BATNA value accounting for negotiation urgency.
Computes the Best Alternative to a Negotiated Agreement with exponential decay to model increasing time pressure and opportunity costs as rounds progress. Different decay rates can be applied per team role.
- Parameters:
- Returns:
- Time-adjusted BATNA value for the specified player and round.
Always less than or equal to the initial BATNA value.
- Return type:
- Formula:
BATNA(t) = base_BATNA * (1 - decay_rate)^(round - 1)
Example
>>> game.get_current_batna("alice", 1) # Round 1 85.0 >>> game.get_current_batna("alice", 5) # Round 5 79.8 # Decreased due to time pressure
Note
Supports both uniform decay rates (float) and role-specific decay rates (dict) for asymmetric time pressure modeling.
- calculate_utility(player: str, proposal: Dict[str, Any]) float[source]
Calculate total weighted utility for a player given a specific proposal.
Computes utility by evaluating each negotiation issue against the player’s preferences and applying role-specific weights. Uses the additive utility model where total utility is the sum of weighted issue utilities.
- Parameters:
- Returns:
- Total weighted utility value for the player. Higher values
indicate more preferred outcomes.
- Return type:
- Utility Calculation:
For each issue: utility += issue_points * role_weight Where issue_points are determined by option selection and role_weights reflect strategic importance to the player’s team.
Example
>>> proposal = {"server_room": 150, "meeting_access": 4, ... "cleaning": "Shared", "branding": "Moderate"} >>> game.calculate_utility("alice", proposal) 87.5
Note
Returns 0.0 for invalid proposals or unrecognized players. Role assignment determines which weight set is applied.
- is_valid_proposal(proposal: Dict[str, Any]) bool[source]
Validate that a proposal contains valid selections for all negotiation issues.
Performs comprehensive validation to ensure proposals are complete and contain only valid options. Checks both completeness (all issues addressed) and validity (selections exist in defined option sets).
- Parameters:
proposal (Dict[str, Any]) – Proposal dictionary to validate containing issue names as keys and selected options as values.
- Returns:
- True if proposal is complete and contains only valid selections,
False if missing issues or invalid options detected.
- Return type:
- Validation Requirements:
Must be non-empty dictionary
Must contain all required issues (server_room, meeting_access, etc.)
All selections must exist in the corresponding issue option sets
Example
>>> valid = {"server_room": 150, "meeting_access": 4, ... "cleaning": "Shared", "branding": "Moderate"} >>> game.is_valid_proposal(valid) True >>> invalid = {"server_room": 200} # Missing issues, invalid size >>> game.is_valid_proposal(invalid) False
Note
This method validates structure and options but not semantic reasonableness or strategic value of proposals.
- is_valid_action(player: str, action: Dict[str, Any], game_state: Dict[str, Any]) bool[source]
Validate player action with enhanced structured format support and proposal limits.
- validate_response(response: Dict[str, Any]) bool[source]
Validate AI model response structure for required negotiation components.
Performs comprehensive validation of parsed responses to ensure they contain all necessary fields and valid action types for multi-issue negotiation processing.
- Parameters:
response (Dict[str, Any]) – Parsed response dictionary to validate containing action type and associated parameters.
- Returns:
- True if response contains valid action structure,
False if missing required fields or invalid action types.
- Return type:
- Validation Rules:
Must contain “type” and “proposal” keys
Action type must be “propose”, “accept”, or “reject”
Proposal validation handled separately by is_valid_proposal()
Example
>>> response = {"type": "propose", "proposal": {"server_room": 150}} >>> game.validate_response(response) True >>> invalid = {"type": "invalid_action"} >>> game.validate_response(invalid) False
Note
This method validates response structure but not semantic validity of proposals. Content validation occurs in downstream processing.
- process_actions(actions: Dict[str, Dict[str, Any]], game_state: Dict[str, Any]) Dict[str, Any][source]
Process player actions with proposal limits and enhanced JSON validation.
- is_game_over(game_state: Dict[str, Any]) bool[source]
Determine if the negotiation has reached a terminal state.
Checks multiple termination conditions to determine if the game should end. Used by the game engine to control round progression and trigger final result calculations.
- Parameters:
game_state (Dict[str, Any]) – Current game state containing round information and agreement status.
- Returns:
- True if game should terminate, False if negotiation
should continue with additional rounds.
- Return type:
- Termination Conditions:
Agreement reached between players
Game explicitly marked as ended
Maximum rounds exceeded
Example
>>> game.is_game_over({"agreement_reached": True}) True >>> game.is_game_over({"current_round": 10}) # max_rounds=8 True >>> game.is_game_over({"current_round": 3}) False
Note
Multiple termination conditions ensure robust game state management across different negotiation scenarios.
- get_winner(game_state: Dict[str, Any]) str | None[source]
Determine negotiation winner based on utility surplus over BATNA.
Identifies the player who achieved the greatest benefit from the negotiation by comparing their utility gain above their Best Alternative to a Negotiated Agreement (BATNA). No winner is declared for failed negotiations.
- Parameters:
game_state (Dict[str, Any]) – Final game state containing agreement details and utility calculations.
- Returns:
- Player identifier of the winner, or None if:
No agreement was reached
Both players have equal utility surplus
- Return type:
Optional[str]
- Winner Criteria:
Winner = max(utility - time_adjusted_BATNA) for each player Only positive surpluses indicate successful negotiation outcomes.
Example
>>> # Player utilities: alice=85, bob=78; BATNAs: alice=75, bob=80 >>> game.get_winner(final_state) "alice" # Surplus: alice=10, bob=-2 >>> game.get_winner(no_agreement_state) None # No agreement reached
Note
Winner determination encourages value-creating negotiations rather than purely competitive zero-sum outcomes.
- get_game_summary(game_state: Dict[str, Any]) Dict[str, Any][source]
Generate comprehensive summary of negotiation results for analysis.
Creates a structured summary containing all key negotiation outcomes, player roles, agreement details, and utility calculations. Used for research analysis, reporting, and comparative studies.
- Parameters:
game_state (Dict[str, Any]) – Final game state containing complete negotiation history and outcomes.
- Returns:
- Comprehensive summary containing:
game_type: “Integrative Negotiations”
players: Mapping of player IDs to role names
agreement_reached: Boolean success indicator
agreement_details: Final proposal if successful
utilities: Final utility values for each player
failure_reason: Explanation if negotiation failed
- Return type:
Dict[str, Any]
- Summary Structure:
Successful negotiations include agreement round, final proposal, utilities, and detailed breakdowns. Failed negotiations include failure reasons and context.
Example
>>> summary = game.get_game_summary(final_state) >>> summary["agreement_reached"] True >>> summary["final_agreement"] {"server_room": 150, "cleaning": "Shared"}
Note
Provides standardized output format for research data collection and comparative analysis across different negotiation scenarios.
- process_action(action) Dict[str, Any][source]
Process a single player action (required by BaseGame interface).
Implements the abstract BaseGame method for single-action processing. In integrative negotiations, multi-player action processing via process_actions() is preferred. This method serves as a compatibility interface for the base class contract.
- Parameters:
action – Single player action to process (format varies).
- Returns:
Processing result indicating successful handling.
- Return type:
Dict[str, Any]
- Implementation Note:
This game uses simultaneous bilateral action processing through process_actions() rather than sequential single-action processing. This method provides base class compatibility.
Example
>>> result = game.process_action({"type": "propose"}) >>> result["processed"] True
Note
For actual negotiation processing, use process_actions() which handles simultaneous bilateral actions appropriately.
- check_end_conditions() bool[source]
Check if the game should end (required by BaseGame interface).
Implements the abstract BaseGame method for termination checking. Delegates to the state-based is_game_over() method when game data is available, providing base class compatibility.
- Returns:
- True if game should terminate, False otherwise.
Delegates to is_game_over() when game state exists.
- Return type:
- Implementation Note:
This game uses state-based termination checking through is_game_over() which analyzes current game state for termination conditions.
Example
>>> game.check_end_conditions() True # If agreement reached or rounds exceeded
Note
Primary termination logic resides in is_game_over() which requires game state for proper condition evaluation.
- calculate_scores() Dict[str, float][source]
Calculate final scores for all players (required by BaseGame interface).
Implements the abstract BaseGame method for score calculation. Returns final utilities from completed negotiations or zero scores for failed negotiations, providing base class compatibility.
- Returns:
- Dictionary mapping player IDs to final scores:
Successful negotiations: actual utility values
Failed negotiations: 0.0 for all players
No game data: 0.0 for all players
- Return type:
- Score Calculation:
Scores are the final utility values calculated during agreement creation, representing negotiated value for each player.
Example
>>> scores = game.calculate_scores() >>> scores {"alice": 87.5, "bob": 78.3} # After successful negotiation
Note
Scores represent utility values rather than competitive rankings. Both players can achieve positive scores in value-creating negotiations.
- get_game_prompt(player_id: str, game_state: Dict[str, Any] | None = None) str[source]
Generate structured negotiation prompt for AI model interaction.
Creates comprehensive, role-specific prompts that provide players with all necessary context for strategic decision-making. Includes current situation, available options, utility guidance, and proper JSON response formatting requirements.
- Parameters:
- Returns:
- Complete formatted prompt string containing:
Current round and role information
Available options and point values
Role-specific priorities and preferences
Proposal history and opponent actions
Response format requirements and examples
- Return type:
- Prompt Components:
Header with round/role identification
BATNA and remaining proposal information
Option descriptions with utility values
Strategic guidance based on current situation
JSON format requirements and examples
Example
>>> prompt = game.get_game_prompt("alice", game_state) >>> "OFFICE SPACE NEGOTIATION" in prompt True >>> "RESPONSE FORMAT" in prompt True
Note
Prompts use neutral role labels to minimize cognitive bias while providing complete strategic context for informed decisions.
Game Implementations
Submodules
Abstract base class defining the interface for all negotiation games.
Base Game Interface
Defines the abstract interface and data structures for all negotiation games within the platform. This module provides the foundation for plug-and-play game integration with consistent APIs and type safety.
- Key Components:
BaseGame: Abstract base class for all negotiation game implementations
GameState: Enumeration of possible game states throughout lifecycle
PlayerAction: Data structure representing individual player actions
GameResult: Data structure containing complete game outcomes
- Architecture:
The base game interface follows the Template Method pattern, where concrete games implement specific game logic while inheriting common infrastructure. This enables consistent behavior across different negotiation scenarios.
- Game Lifecycle:
WAITING: Game created but not yet initialized with players
ACTIVE: Game in progress with players taking turns
COMPLETED: Game finished successfully with final results
FAILED: Game terminated due to errors or violations
- Design Patterns:
Template Method: Common game infrastructure with specific implementations
State Machine: Clear state transitions and lifecycle management
Data Transfer Objects: Structured data containers for actions and results
- class negotiation_platform.games.base_game.GameState(value)[source]
Bases:
EnumEnumeration of possible game states throughout the negotiation lifecycle.
Defines the complete state machine for negotiation games, enabling proper state tracking and transition validation throughout the game lifecycle.
- States:
- WAITING: Game instance created but not yet initialized with players.
No actions can be taken in this state.
- ACTIVE: Game is in progress with players actively negotiating.
Actions are validated and processed in this state.
- COMPLETED: Game finished successfully with valid final results.
No further actions are accepted.
- FAILED: Game terminated abnormally due to errors or rule violations.
Game data may be incomplete or invalid.
- State Transitions:
WAITING → ACTIVE: When game is initialized with valid players ACTIVE → COMPLETED: When end conditions are met successfully ACTIVE → FAILED: When unrecoverable errors occur Any state → FAILED: When critical failures are detected
Example
>>> game = ConcreteGame("game_1", {}) >>> print(game.state) GameState.WAITING >>> game.initialize_game(["player1", "player2"]) >>> print(game.state) GameState.ACTIVE
- WAITING = 'waiting'
- ACTIVE = 'active'
- COMPLETED = 'completed'
- FAILED = 'failed'
- class negotiation_platform.games.base_game.PlayerAction(player_id: str, action_type: str, action_data: Dict[str, Any], timestamp: float, round_number: int)[source]
Bases:
objectData structure representing a single player action within a negotiation game.
PlayerAction serves as a standardized container for all player interactions during negotiations. It captures both the action content and contextual metadata necessary for game processing and analysis.
- player_id
Unique identifier of the player who took this action. Must match one of the registered players in the game.
- Type:
- action_type
Category or type of action taken. Common types include: - “proposal”: Player making an offer or suggestion - “acceptance”: Player accepting a previous proposal - “rejection”: Player rejecting a previous proposal - “counter_proposal”: Player modifying a previous proposal - “message”: General communication or clarification
- Type:
- action_data
Action-specific data and parameters. Structure varies by action_type and game type. Examples: - Proposal: {“price”: 45000, “terms”: “cash_payment”} - Acceptance: {“accepted_proposal_id”: “prop_123”} - Message: {“text”: “I need to consider this offer”}
- Type:
Dict[str, Any]
- timestamp
Unix timestamp when the action was taken. Used for timing analysis and action ordering.
- Type:
- round_number
The negotiation round when this action occurred. Enables round-based analysis and game flow tracking.
- Type:
Example
>>> action = PlayerAction( ... player_id="player_1", ... action_type="proposal", ... action_data={"price": 42000, "warranty": True}, ... timestamp=1609459200.0, ... round_number=3 ... ) >>> print(action.action_data["price"]) 42000
Note
PlayerAction objects are immutable once created (dataclass with frozen=False by default, but should be treated as read-only after creation).
- class negotiation_platform.games.base_game.GameResult(game_id: str, players: List[str], winner: str | None, final_scores: Dict[str, float], total_rounds: int, game_data: Dict[str, Any], success: bool)[source]
Bases:
objectData structure containing the complete outcome and results of a negotiation game.
GameResult serves as the authoritative record of a completed negotiation, containing all final state information needed for analysis, metrics calculation, and reporting. It provides a standardized format for game outcomes across different negotiation types.
- game_id
Unique identifier for this specific game session. Used to correlate results with logs and analysis data.
- Type:
- players
Complete list of all players who participated. Maintains original player order for consistent analysis.
- Type:
List[str]
- winner
Identifier of the winning player, if applicable. None for games without clear winners (mutual agreements, ties, etc.). Some games may have multiple winners or no winner concept.
- Type:
Optional[str]
- final_scores
Final scores for each player. Maps player_id to their game-specific score. Score interpretation varies by game type (utility values, points, satisfaction ratings, etc.).
- total_rounds
Total number of negotiation rounds completed. Includes all rounds from game start to termination.
- Type:
- game_data
Complete final game state and additional data. Contains game-specific information such as: - final_agreement: Terms of any reached agreement - batna_values: Best alternatives for each player - resource_allocations: Final resource distributions - negotiation_history: Detailed interaction log
- Type:
Dict[str, Any]
- success
Whether the game completed successfully. True for normal completion (agreement or deadline reached). False for abnormal termination (errors, rule violations, crashes).
- Type:
Example
>>> result = GameResult( ... game_id="session_2023_001", ... players=["model_a", "model_b"], ... winner="model_a", ... final_scores={"model_a": 8.5, "model_b": 6.2}, ... total_rounds=4, ... game_data={"agreement_reached": True, "final_price": 43500}, ... success=True ... ) >>> print(result.final_scores["model_a"]) 8.5
- Usage:
GameResult objects are created by game implementations upon completion and consumed by the metrics system, reporting tools, and analysis scripts. They provide the primary interface between game execution and result analysis.
- class negotiation_platform.games.base_game.BaseGame(game_id: str, config: Dict[str, Any])[source]
Bases:
ABCAbstract base class defining the interface and common infrastructure for all negotiation games.
BaseGame provides the template and shared functionality for all negotiation game types within the platform. It implements common game management features while defining abstract methods that concrete games must implement for game-specific logic.
- Design Pattern:
Implements the Template Method pattern where this base class handles common game lifecycle management (state tracking, action history, round counting) while delegating game-specific logic to concrete implementations.
- Key Responsibilities:
Game state management and lifecycle tracking
Player registration and management
Action history maintenance and validation
Round counting and termination detection
Common configuration parameter handling
Abstract interface definition for game-specific logic
- actions_history
Complete chronological action log.
- Type:
List[PlayerAction]
- Abstract Methods:
Concrete games must implement: - initialize_game(): Set up initial game state with players - is_valid_action(): Validate player actions against game rules - process_action(): Update game state based on valid actions - check_end_conditions(): Determine if game should terminate - calculate_scores(): Compute final player scores - get_game_prompt(): Generate player-specific prompts
- Game Lifecycle:
Construction: Game created with configuration
Initialization: Players assigned and game state prepared
Active Phase: Players take turns, actions processed
Termination: End conditions met or max rounds reached
Scoring: Final scores calculated and results prepared
Example
>>> class CustomGame(BaseGame): ... def initialize_game(self, players): ... self.players = players ... self.state = GameState.ACTIVE ... return True ... # ... implement other abstract methods >>> game = CustomGame("game_1", {"max_rounds": 5}) >>> success = game.initialize_game(["player1", "player2"])
Note
This is an abstract class and cannot be instantiated directly. Use concrete implementations like CompanyCarGame, ResourceAllocationGame, etc.
- __init__(game_id: str, config: Dict[str, Any])[source]
Initialize a new game instance with identifier and configuration.
Sets up the basic game infrastructure including state tracking, player management, and configuration storage. The game starts in WAITING state until players are assigned via initialize_game().
- Parameters:
game_id (str) – Unique identifier for this game instance. Used for logging, result tracking, and debugging. Should be unique across all concurrent game sessions.
config (Dict[str, Any]) –
Configuration dictionary containing game parameters. Common parameters include:
max_rounds (int): Maximum negotiation rounds (default: 10)
time_limit (int): Session time limit in seconds
game_specific_params: Parameters unique to the game type
- Initialization Process:
Stores game identifier and configuration
Sets initial state to WAITING
Initializes empty player list
Resets round counter to 0
Sets up empty action history
Prepares game data dictionary for game-specific state
Example
>>> config = {"max_rounds": 5, "time_limit": 1800} >>> game = ConcreteGame("negotiation_001", config) >>> print(game.state) GameState.WAITING >>> print(game.max_rounds) 5
Note
After construction, call initialize_game() to assign players and transition to ACTIVE state before game play can begin.
- abstract is_valid_action(player: str, action: Dict[str, Any], game_state: Dict[str, Any]) bool[source]
Check if an action is valid in current game state
- abstract process_action(action: PlayerAction) Dict[str, Any][source]
Process a player action and update game state
- abstract get_game_prompt(player_id: str) str[source]
Get the current game prompt for a specific player
- add_action(action: PlayerAction)[source]
Add a player action to the game’s chronological history log.
Maintains a complete record of all player actions taken during the negotiation session for analysis, metrics calculation, and debugging.
- Parameters:
action (PlayerAction) – The player action to add to history. Must contain player_id, action_type, action_data, timestamp, and round_number for complete action tracking.
Example
>>> action = PlayerAction( ... player_id="player1", ... action_type="offer", ... action_data={"price": 42000}, ... timestamp=1609459200.0, ... round_number=3 ... ) >>> game.add_action(action)
- get_game_info() Dict[str, Any][source]
Retrieve comprehensive information about the current game state.
Provides a structured overview of the game’s current status including identifiers, state information, player list, round tracking, and configuration parameters. Useful for debugging, logging, and analysis.
- Returns:
- Dictionary containing:
game_id (str): Unique game identifier
state (str): Current game state (waiting/active/completed/failed)
players (List[str]): List of participating player identifiers
current_round (int): Current round number (0-based)
max_rounds (int): Maximum rounds before forced termination
config (Dict[str, Any]): Complete game configuration parameters
- Return type:
Dict[str, Any]
Example
>>> info = game.get_game_info() >>> print(f"Game {info['game_id']} is in {info['state']} state") >>> print(f"Round {info['current_round']}/{info['max_rounds']}") Game negotiation_001 is in active state Round 3/5
Company Car Game (Price Bargaining)
Bilateral car price negotiation with time-adjusted BATNAs and strategic guidance.
Company Car Price Bargaining Game
Bilateral negotiation game for vehicle price negotiations with realistic market dynamics.
This module implements a structured car buying/selling negotiation where a buyer and seller negotiate the price of a company vehicle. The game includes realistic elements such as:
Time-decaying BATNAs (Best Alternatives to Negotiated Agreement)
Strategic guidance and structured prompts
Market-realistic price ranges and constraints
JSON-based proposal system for structured interactions
- Key Features:
Bilateral negotiation between buyer and seller roles
Dynamic BATNA values that decay over time to simulate urgency
Structured proposal system with validation
Strategic prompts to guide realistic negotiation behavior
Win-win outcome detection and scoring
- Game Parameters:
Starting Price: Initial vehicle price for negotiation
Buyer Budget: Maximum amount buyer can afford
Seller Cost: Minimum acceptable amount for seller
BATNA Values: Alternative options for both parties
Time Decay: How BATNAs change over negotiation rounds
Example
>>> config = {
... "starting_price": 42000,
... "buyer_budget": 45000,
... "seller_cost": 38000,
... "buyer_batna": 41000,
... "seller_batna": 39000,
... "rounds": 5,
... "batna_decay": 0.02
... }
>>> game = CompanyCarGame(config)
>>> game.initialize_game(["buyer_agent", "seller_agent"])
- class negotiation_platform.games.price_bargaining.CompanyCarGame(config: Dict[str, Any])[source]
Bases:
BaseGameCompany car price negotiation game implementing realistic bilateral bargaining dynamics.
This class manages structured price negotiations between a buyer and seller for a company vehicle purchase. The game incorporates realistic market dynamics, time pressure through BATNA decay, and strategic guidance to create authentic negotiation experiences.
The game simulates a common business scenario where organizations must negotiate vehicle purchases, balancing budget constraints with value optimization. Both parties have alternatives (BATNAs) that become less attractive over time, encouraging timely agreement.
- Key Features:
Realistic price negotiation with market constraints
Dynamic BATNA values that decay to simulate time pressure
Strategic guidance system with contextual prompts
JSON-based structured proposal system for clear communication
Win-win outcome detection based on surplus above BATNA values
Comprehensive utility tracking and performance analysis
- Game Mechanics:
Buyer and seller alternate making price proposals
Each proposal is validated against budget and cost constraints
Players can make offers, counteroffers, or accept/reject proposals
BATNA values decay each round to encourage timely resolution
Game ends on acceptance or maximum rounds reached
Final utilities calculated based on achieved price vs. BATNA
Example
>>> config = { ... "starting_price": 42000, ... "buyer_budget": 45000, ... "seller_cost": 38000, ... "buyer_batna": 41000, ... "seller_batna": 39000, ... "rounds": 5, ... "batna_decay": 0.02 ... } >>> game = CompanyCarGame(config) >>> game.initialize_game(["buyer_agent", "seller_agent"]) >>> >>> # Buyer makes initial offer >>> action = {"type": "offer", "price": 40000} >>> valid = game.is_valid_action("buyer_agent", action) >>> if valid: ... game.process_actions({"buyer_agent": action}, game_state)
- Strategic Considerations:
Buyer Strategy: Start low but stay above seller’s likely cost
Seller Strategy: Start high but consider buyer’s budget limits
Both: Monitor BATNA decay and time pressure effects
Optimal: Find price range where both parties gain vs. their BATNAs
- Outcome Analysis:
Game success measured by: - Agreement reached within round limit - Both parties achieve positive surplus above BATNA - Efficient price discovery within feasible range - Balanced utility distribution between parties
- __init__(config: Dict[str, Any])[source]
Initialize company car negotiation game with configuration parameters.
Sets up the bilateral price negotiation environment with buyer and seller roles, BATNA values, budget constraints, and time decay mechanisms. Validates that all required configuration parameters are provided.
- Parameters:
config (Dict[str, Any]) – Configuration dictionary containing: - starting_price (int): Initial vehicle price for reference - buyer_budget (int): Maximum amount buyer can afford - seller_cost (int): Minimum amount seller needs to receive - buyer_batna (float): Buyer’s best alternative value - seller_batna (float): Seller’s best alternative value - rounds (int): Maximum negotiation rounds allowed - batna_decay (float): Per-round BATNA decay rate (0.0-1.0)
- Raises:
ValueError – If any required configuration field is missing.
Example
>>> config = { ... "starting_price": 42000, ... "buyer_budget": 45000, ... "seller_cost": 38000, ... "buyer_batna": 41000, ... "seller_batna": 39000, ... "rounds": 5, ... "batna_decay": 0.02 ... } >>> game = CompanyCarGame(config)
- validate_json_response(response: str) bool[source]
Validate that a response string contains properly formatted JSON.
Checks if the provided response can be parsed as valid JSON and contains the required “type” field for action identification. Used for input validation before processing player responses.
- Parameters:
response (str) – Raw response string from player to validate.
- Returns:
- True if response is valid JSON with “type” field,
False otherwise.
- Return type:
Example
>>> valid_response = '{"type": "offer", "price": 42000}' >>> game.validate_json_response(valid_response) True >>> invalid_response = 'I want to offer 42000' >>> game.validate_json_response(invalid_response) False
- parse_json_response(response: str) Dict[str, Any][source]
Parse and normalize JSON response from players into standard format.
Extracts decision data from various JSON response formats, handling both direct action format and structured response format. Provides robust error recovery with fallback parsing for malformed responses.
- Parameters:
response (str) – Raw JSON response string from player.
- Returns:
- Parsed response containing:
decision (Dict[str, Any]): Extracted action data with “type” field
raw_response (str): Original response for debugging
- Return type:
Dict[str, Any]
Example
>>> response = '{"type": "offer", "price": 42000}' >>> parsed = game.parse_json_response(response) >>> print(parsed["decision"]["type"]) offer >>> print(parsed["decision"]["price"]) 42000
Note
Falls back to {“type”: “reject”} for unparseable responses to ensure graceful handling of malformed input.
- initialize_game(players: List[str]) Dict[str, Any][source]
Process a single player action and update the game state accordingly.
Handles individual player actions by adding them to history and delegating to the batch process_actions method. Required by BaseGame interface for single-action processing.
- Parameters:
action (PlayerAction) – Player action to process containing player_id, action_type, action_data, timestamp, and round_number.
- Returns:
Updated game state after processing the action.
- Return type:
Dict[str, Any]
Example
>>> action = PlayerAction( ... player_id="buyer", ... action_type="offer", ... action_data={"price": 42000}, ... timestamp=1609459200.0, ... round_number=3 ... ) >>> new_state = game.process_action(action)
- get_current_batna(player: str, round_num: int) float[source]
Calculate time-adjusted BATNA value for specified player and round.
Applies exponential decay to the player’s initial BATNA value based on the current round number, simulating decreasing value of alternatives over time. This creates time pressure encouraging earlier agreements.
- Parameters:
- Returns:
Time-adjusted BATNA value for the specified round.
- Return type:
Example
>>> # Initial buyer BATNA: 41000, decay rate: 0.02 >>> round_1_batna = game.get_current_batna("buyer", 1) >>> print(f"Round 1 BATNA: €{round_1_batna:,.0f}") Round 1 BATNA: €40,180 >>> round_3_batna = game.get_current_batna("buyer", 3) >>> print(f"Round 3 BATNA: €{round_3_batna:,.0f}") Round 3 BATNA: €39,572
Note
BATNA decay formula: initial_batna * (1 - decay_rate)^round_num
- is_valid_action(player: str, action: Dict[str, Any], game_state: Dict[str, Any]) bool[source]
Validate player action against game rules with flexible format support.
Validates negotiation actions including offers, acceptances, and rejections. Supports both direct action format and structured response format with “decision” wrapper. Ensures actions comply with game constraints including proposal limits and valid action types.
- Parameters:
player (str) – Identifier of the player taking the action. Must be registered buyer or seller.
action (Dict[str, Any]) – Action data to validate. Supported formats: - Direct: {“type”: “offer”, “price”: 42000} - Structured: {“decision”: {“type”: “offer”, “price”: 42000}}
game_state (Dict[str, Any]) – Current game state containing round information, proposal counts, and negotiation history.
- Returns:
True if action is valid and can be processed, False otherwise.
- Return type:
- Validation Rules:
Action must have valid “type” field (offer, accept, reject)
Offers must include numeric “price” field
Player must not exceed proposal limits
Price offers must be positive numeric values
Example
>>> # Valid offer action >>> action = {"type": "offer", "price": 42000} >>> is_valid = game.is_valid_action("buyer", action, game_state) >>> print(is_valid) True
>>> # Valid structured format >>> structured = {"decision": {"type": "accept"}} >>> is_valid = game.is_valid_action("seller", structured, game_state) >>> print(is_valid) True
Note
Invalid actions are logged but do not raise exceptions, allowing graceful handling of malformed AI model responses.
- process_actions(actions: Dict[str, Dict[str, Any]], game_state: Dict[str, Any]) Dict[str, Any][source]
Process simultaneous player actions with proposal tracking and validation.
Handles bilateral negotiation actions including offers, acceptances, and rejections. Enforces proposal limits, validates action compatibility, and determines negotiation outcomes. Updates game state with action results and manages agreement detection.
- Parameters:
actions (Dict[str, Dict[str, Any]]) – Mapping of player identifiers to their action data. Expected format: {“player_id”: {“type”: “offer”, “price”: 42000}}
game_state (Dict[str, Any]) – Current game state containing: - current_round: Round number for tracking - proposal counts: Limits for each player - negotiation history: Previous actions and offers
- Returns:
- Updated game state containing:
agreement_reached: Boolean indicating successful negotiation
final_price: Agreed price if agreement reached
final_utilities: Utility scores for each player
game_over: Boolean indicating termination
Updated proposal counts and action history
- Return type:
Dict[str, Any]
- Processing Logic:
Initialize and validate proposal counters
Process each player’s action with validation
Check for mutual acceptances (agreement)
Handle offers and update last offer tracking
Update proposal counts and game state
Determine if negotiation should continue
- Agreement Detection:
Mutual acceptance: Both players accept in same round
Offer acceptance: One player accepts other’s previous offer
Price agreement: Players converge on acceptable price
Example
>>> actions = { ... "buyer": {"type": "offer", "price": 41000}, ... "seller": {"type": "offer", "price": 43000} ... } >>> new_state = game.process_actions(actions, current_state) >>> print(new_state["agreement_reached"]) False # No agreement, continue negotiating
Note
Players exceeding proposal limits receive rejection responses. Invalid actions are logged and may result in negotiation failure.
- is_game_over(game_state: Dict[str, Any]) bool[source]
Determine if the negotiation game has reached a terminal state.
Checks for various end conditions including agreement reached, maximum rounds exceeded, or explicit rejections that end negotiation.
- Parameters:
game_state (Dict[str, Any]) – Current game state to evaluate.
- Returns:
True if game should terminate, False if negotiation continues.
- Return type:
Example
>>> # Agreement reached >>> game_state = {"agreement_reached": True} >>> game.is_game_over(game_state) True >>> # Maximum rounds exceeded >>> game_state = {"current_round": 6, "agreement_reached": False} >>> game.is_game_over(game_state) # max_rounds = 5 True
- get_winner(game_state: Dict[str, Any]) str | None[source]
Determine the winning player based on positive utility surplus analysis.
Evaluates final utilities and surplus values to identify the player who achieved the highest positive surplus above their BATNA. Only players with positive surplus can be considered winners.
- Parameters:
game_state (Dict[str, Any]) – Final game state with utility data.
- Returns:
- Player identifier of winner, or None if:
No agreement was reached
No player has positive surplus
Utilities are tied or unavailable
- Return type:
Optional[str]
Example
>>> # Buyer achieved higher surplus >>> game_state = { ... "agreement_reached": True, ... "utility_surplus": {"buyer": 1000, "seller": 500} ... } >>> winner = game.get_winner(game_state) >>> print(f"Winner: {winner}") Winner: buyer
- process_action(action: PlayerAction) Dict[str, Any][source]
Process a single player action and update the game state accordingly.
Handles individual player actions by adding them to history and delegating to the batch process_actions method. Required by BaseGame interface for single-action processing.
- Parameters:
action (PlayerAction) – Player action to process containing player_id, action_type, action_data, timestamp, and round_number.
- Returns:
Updated game state after processing the action.
- Return type:
Dict[str, Any]
Example
>>> action = PlayerAction( ... player_id="buyer", ... action_type="offer", ... action_data={"price": 42000}, ... timestamp=1609459200.0, ... round_number=3 ... ) >>> new_state = game.process_action(action)
- check_end_conditions() bool[source]
Check if the negotiation game should terminate based on current state.
Evaluates termination conditions by delegating to the is_game_over method. Required by BaseGame interface for consistent end condition checking across all game implementations.
- Returns:
True if game should end, False if negotiation continues.
- Return type:
Example
>>> game.check_end_conditions() True # If agreement reached or max rounds exceeded
- calculate_scores() Dict[str, float][source]
Calculate final utility scores for all participating players.
Computes final negotiation outcomes based on whether agreement was reached. If successful agreement occurred, returns actual utility values achieved by each player. If negotiation failed, returns zero scores for all players to reflect lack of value creation.
- Returns:
- Mapping of player identifiers to final utility scores.
For successful negotiations, contains actual utility values. For failed negotiations, contains 0.0 for all players.
- Return type:
Example
>>> # Successful price agreement at $42,000 >>> scores = game.calculate_scores() >>> print(scores) {'buyer_player': 7.5, 'seller_player': 8.2}
>>> # Failed negotiation (no agreement) >>> scores = game.calculate_scores() >>> print(scores) {'buyer_player': 0.0, 'seller_player': 0.0}
Note
Required by BaseGame interface for consistent scoring across all negotiation game implementations. Scores reflect relative success in achieving negotiation objectives.
- get_game_prompt(player_id: str) str[source]
Generate comprehensive negotiation prompt with neutral role terminology.
Creates detailed, contextual prompts for car price negotiations using neutral role labels (“Party A”/”Party B”) instead of loaded terms (“buyer”/”seller”) to reduce cognitive bias and role-based behavioral influences. Includes current game state, strategic guidance, and structured action formatting requirements.
- Parameters:
player_id (str) – Identifier of the player requesting the prompt. Must be one of the registered players (buyer or seller).
- Returns:
- Comprehensive negotiation prompt containing:
Neutral role context and scenario description
Current round and proposal count information
BATNA thresholds and acceptance criteria
Opponent’s latest offer (if available)
Available actions and JSON formatting requirements
Strategic guidance for decision making
- Return type:
- Prompt Elements:
Bias-reduced role terminology (Party A/B vs buyer/seller)
Current negotiation state and round tracking
BATNA-based decision criteria and thresholds
Offer history and opponent action visibility
Structured JSON response format requirements
Strategic guidance for proposal and acceptance decisions
Example
>>> prompt = game.get_game_prompt("player1") >>> print("Party A" in prompt) # Neutral terminology True >>> print("JSON" in prompt) # Format requirements True >>> print("BATNA" in prompt) # Strategic guidance True
Note
Returns error message if game is not properly initialized with buyer and seller assignments. Prompts adapt to current game state including round limits and proposal constraints.
Resource Allocation Game
Multi-resource distribution negotiation between development and marketing teams.
Resource Allocation Negotiation Game
Multi-resource distribution negotiation between development and marketing teams.
This module implements a complex resource allocation scenario where two teams (Development and Marketing) negotiate the distribution of limited resources including GPUs, developers, and budget allocation for a project.
The game simulates realistic organizational resource conflicts where: - Teams have different priorities and valuation of resources - Resources are limited and must be allocated efficiently - Teams must balance their needs against organizational constraints - Win-win solutions require creative resource sharing and trade-offs
- Key Features:
Multi-resource negotiation (GPUs, developers, budget)
Team-based roles with different resource priorities
Complex utility calculations based on resource combinations
BATNA values representing alternative resource sources
Structured JSON proposal system for resource requests
- Game Components:
Development Team: Focuses on GPU resources and technical staff
Marketing Team: Prioritizes budget and developer support
Resource Pool: Limited resources that must be allocated
Utility Functions: Team-specific valuation of resource combinations
Example
>>> config = {
... "max_rounds": 5,
... "total_gpus": 10,
... "total_developers": 8,
... "total_budget": 100000
... }
>>> game = ResourceAllocationGame(config)
>>> game.initialize_game(["dev_team", "marketing_team"])
- class negotiation_platform.games.resource_allocation.ResourceAllocationGame(config: Dict[str, Any])[source]
Bases:
BaseGameComplex multi-resource allocation negotiation between Development and Marketing teams.
This class implements a sophisticated resource distribution scenario where two organizational teams must negotiate the allocation of limited computational and human resources for a shared project. The game simulates realistic organizational resource conflicts with complex interdependencies and trade-offs.
The negotiation involves multiple resource types with different valuations for each team, requiring creative resource sharing and package deals to achieve mutually beneficial outcomes. Teams must balance their specific needs against organizational constraints and counterpart requirements.
- Key Features:
Multi-resource negotiation (GPUs, developers, budget allocation)
Team-specific utility functions with different resource valuations
Complex resource interdependencies and constraints
BATNA values representing alternative resource sources
Structured JSON proposal system for resource requests
Win-win solution detection based on efficient resource utilization
- Resource Types:
GPU Hours: Computational resources for processing tasks - Development Team: High priority for model training and testing - Marketing Team: Moderate priority for data analysis
Developer Hours: Human resource allocation - Development Team: Critical for implementation work - Marketing Team: Needed for integration and campaign development
Budget Allocation: Financial resources for project components - Development Team: Infrastructure and tool costs - Marketing Team: Campaign execution and market research
- Game Mechanics:
Teams propose resource allocation packages
Proposals validated against total resource constraints
Utility calculated based on team-specific resource valuations
Teams can negotiate, trade, or share resources creatively
BATNA decay encourages timely resolution
Success measured by total utility maximization
- team_utilities
Team-specific utility functions defining how each team values different resource combinations.
- Type:
Dict[str, Dict]
- resource_weights
Team-specific importance weights for each resource type in utility calculations.
Example
>>> config = { ... "max_rounds": 5, ... "total_gpus": 10, ... "total_developers": 8, ... "total_budget": 100000, ... "batna_decay": 0.02 ... } >>> game = ResourceAllocationGame(config) >>> game.initialize_game(["dev_team", "marketing_team"]) >>> >>> # Development team proposes resource allocation >>> proposal = { ... "gpu_hours": 7, # High GPU need for development ... "developer_hours": 5, # Core development team ... "budget": 60000 # Infrastructure costs ... } >>> action = {"type": "proposal", "allocation": proposal} >>> valid = game.is_valid_action("dev_team", action)
- Strategic Considerations:
Development Team: Prioritize GPU and developer resources
Marketing Team: Focus on budget and developer support
Both teams: Identify resource trades that create mutual value
Optimal: Find allocations where total utility exceeds individual BATNAs
- Resource Efficiency:
Game encourages: - Creative resource sharing arrangements - Time-based resource allocation (sequential usage) - Hybrid solutions combining different resource types - Recognition of complementary resource needs between teams
- __init__(config: Dict[str, Any])[source]
Initialize resource allocation negotiation game with team configurations.
Sets up multi-resource negotiation between Development and Marketing teams with utility functions, BATNA values, resource constraints, and time decay mechanisms. Validates required configuration parameters.
- Parameters:
config (Dict[str, Any]) – Configuration dictionary containing: - batnas (Dict[str, float]): BATNA values for each team - rounds (int): Maximum negotiation rounds allowed - batna_decay (float): Per-round BATNA decay rate (0.0-1.0) - total_resources (Dict[str, int]): Available resource pools - constraints (Dict): Resource allocation constraints - utility_functions (Dict): Team-specific utility parameters - uncertainty (Dict, optional): Uncertainty parameters
- Raises:
ValueError – If any required configuration field is missing.
Example
>>> config = { ... "batnas": {"development": 50, "marketing": 45}, ... "rounds": 5, ... "batna_decay": 0.02, ... "total_resources": {"gpu": 100, "cpu": 100}, ... "constraints": {"max_gpu_per_team": 80}, ... "utility_functions": { ... "development": {"gpu_coefficient": 0.8, "cpu_coefficient": 0.2}, ... "marketing": {"gpu_coefficient": 0.3, "cpu_coefficient": 0.7} ... } ... } >>> game = ResourceAllocationGame(config)
- validate_json_response(response: str) bool[source]
Validate that a response string contains properly formatted JSON.
Checks if the provided response can be parsed as valid JSON and contains the required “type” field for action identification. Used for input validation before processing team responses.
- Parameters:
response (str) – Raw response string from team to validate.
- Returns:
- True if response is valid JSON with “type” field,
False otherwise.
- Return type:
Example
>>> valid_response = '{"type": "propose", "gpu": 60, "cpu": 40}' >>> game.validate_json_response(valid_response) True >>> invalid_response = 'We want 60 GPU hours' >>> game.validate_json_response(invalid_response) False
- parse_json_response(response: str) Dict[str, Any][source]
Parse and normalize JSON response from teams into standard format.
Extracts decision data from various JSON response formats, handling both direct action format and structured response format. Provides robust error recovery with fallback parsing for malformed responses.
- Parameters:
response (str) – Raw JSON response string from team.
- Returns:
- Parsed response containing:
decision (Dict[str, Any]): Extracted action data with “type” field
raw_response (str): Original response for debugging
- Return type:
Dict[str, Any]
Example
>>> response = '{"type": "propose", "gpu": 60, "cpu": 40}' >>> parsed = game.parse_json_response(response) >>> print(parsed["decision"]["type"]) propose >>> print(parsed["decision"]["gpu"]) 60
Note
Falls back to {“type”: “reject”} for unparseable responses to ensure graceful handling of malformed input.
- initialize_game(players: List[str]) Dict[str, Any][source]
Initialize multi-resource allocation negotiation between development and marketing teams.
Sets up the negotiation environment with randomized role assignments to minimize order bias, initializes team-specific utility functions, establishes resource constraints, and prepares the game state for active negotiation.
- Parameters:
players (List[str]) – List of exactly 2 player identifiers representing the negotiating teams. Order is randomized for role assignment.
- Returns:
- Initial game state containing:
players: List of player identifiers with assigned roles
current_round: Starting round number (1)
max_rounds: Maximum negotiation rounds allowed
total_gpu_hours: Total GPU resources available (10)
total_cpu_hours: Total CPU resources available (10)
private_info: Team-specific utility functions and preferences
resource_history: Empty history for tracking allocations
agreement_reached: False (negotiation not yet concluded)
- Return type:
Dict[str, Any]
- Initialization Process:
Validate exactly 2 players provided
Randomly assign development and marketing roles
Set up team-specific utility functions and preferences
Initialize resource constraints and tracking
Create private information for each team
Prepare negotiation state tracking
- Role Assignment:
Development Team: Higher GPU preference, moderate CPU needs
Marketing Team: Higher CPU preference, moderate GPU needs
Random assignment prevents order bias effects
- Resource Setup:
Total GPU Hours: 10 (must be allocated between teams)
Total CPU Hours: 10 (must be allocated between teams)
Team-specific utility functions for each resource type
Example
>>> players = ["model_a", "model_b"] >>> initial_state = game.initialize_game(players) >>> print(initial_state["total_gpu_hours"]) 10 >>> print("private_info" in initial_state) True
- Raises:
ValueError – If number of players is not exactly 2.
Note
Role assignments are logged for debugging but kept private from players to maintain negotiation authenticity.
- get_current_batna(player: str, round_num: int) float[source]
Calculate time-adjusted BATNA value for specified team and round.
Applies exponential decay to the team’s initial BATNA value based on the current round number, simulating decreasing value of alternative resource sources over time. Creates time pressure encouraging agreement.
- Parameters:
- Returns:
Time-adjusted BATNA value for the specified round.
- Return type:
Example
>>> # Initial development BATNA: 50, decay rate: 0.02 >>> round_1_batna = game.get_current_batna("development", 1) >>> print(f"Round 1 BATNA: {round_1_batna:.1f}") Round 1 BATNA: 49.0 >>> round_3_batna = game.get_current_batna("development", 3) >>> print(f"Round 3 BATNA: {round_3_batna:.1f}") Round 3 BATNA: 48.0
Note
BATNA decay formula: initial_batna * (1 - decay_rate)^round_num
- calculate_utility(player: str, gpu_hours: float, cpu_hours: float, round_num: int) float[source]
Calculate team-specific utility value for proposed resource allocation.
Computes utility scores using configurable team-specific coefficients and uncertainty factors. Each team has different preferences for GPU vs CPU resources based on their operational needs and strategic priorities.
- Parameters:
- Returns:
- Total utility value including base utility and uncertainty factor.
Higher values indicate more attractive proposals for the team.
- Return type:
- Utility Calculation:
Base utility = (gpu_coeff × gpu_hours) + (cpu_coeff × cpu_hours) Final utility = base_utility + random_uncertainty_factor
- Team Coefficients:
Development: Higher GPU coefficient, moderate CPU coefficient
Marketing: Higher CPU coefficient, moderate GPU coefficient
Configurable via utility_functions in game setup
- Uncertainty Factor:
Random value within team-specific bounds to model negotiation uncertainty and prevent deterministic outcomes.
Example
>>> # Development team evaluating GPU-heavy allocation >>> utility = game.calculate_utility("dev_team", 8.0, 2.0, 1) >>> print(f"Development utility: {utility:.1f}") Development utility: 28.3 # Including uncertainty
Note
Uncertainty factors add realism but may cause slight result variations between identical runs. Set narrow bounds for more predictable behavior.
- check_constraints_and_update(gpu_hours: float, cpu_hours: float) None[source]
Validate resource allocation against all constraints and update game state.
Performs comprehensive validation of proposed resource allocation against multiple constraint types including total resource limits, bandwidth constraints, and resource coupling requirements. Updates game state with detailed validation results and error messages.
- Parameters:
- Side Effects:
Updates self.game_data[‘constraint_check’] with detailed validation results including constraint status, violation messages, and resource utilization analysis.
- Constraint Validation:
Total Resource Limit: gpu_hours + cpu_hours ≤ total_resources
GPU Bandwidth: 4×gpu_hours + 4×cpu_hours ≤ gpu_bandwidth
Individual Resource Bounds: Non-negative allocations
Resource Coupling: Interdependency constraints
- Game State Updates:
- Creates or updates ‘constraint_check’ entry containing:
constraints_met: Boolean overall validation result
messages: List of specific constraint violation descriptions
gpu_hours: Validated GPU allocation
cpu_hours: Validated CPU allocation
total_usage: Combined resource utilization
Example
>>> # Valid allocation within all constraints >>> game.check_constraints_and_update(4.0, 6.0) >>> print(game.game_data['constraint_check']['constraints_met']) True
>>> # Invalid allocation exceeding total resources >>> game.check_constraints_and_update(8.0, 12.0) >>> print(game.game_data['constraint_check']['constraints_met']) False >>> print(game.game_data['constraint_check']['messages']) ['Total resources exceeded: 20.0 > 10.0']
Note
This method provides detailed constraint analysis for debugging and user feedback, supporting complex multi-constraint validation scenarios in resource allocation negotiations.
- is_valid_action(player: str, action: Dict[str, Any], game_state: Dict[str, Any]) bool[source]
Validate team action against resource allocation rules and constraints.
Comprehensive validation of negotiation actions including proposals and acceptances. Supports both direct action format and structured response format with “decision” wrapper. Ensures actions comply with resource constraints, proposal limits, and valid action types.
- Parameters:
player (str) – Identifier of the team taking the action. Must be registered development or marketing team.
action (Dict[str, Any]) – Action data to validate. Supported formats: - Direct: {“type”: “propose”, “gpu”: 6, “cpu”: 4} - Structured: {“decision”: {“type”: “propose”, “gpu”: 6, “cpu”: 4}}
game_state (Dict[str, Any]) – Current game state containing round information, proposal counts, and resource constraints.
- Returns:
True if action is valid and can be processed, False otherwise.
- Return type:
- Validation Rules:
Action must have valid “type” field (propose, accept)
Proposals must include numeric “gpu” and “cpu” fields
Resource allocations must respect total resource constraints
GPU + CPU allocations must not exceed available pools
Resource values must be non-negative numbers
Player must not exceed proposal limits
- Resource Constraints:
Total GPU hours available: 10
Total CPU hours available: 10
Individual allocations must be ≤ total resources
Combined team allocations must sum to ≤ totals
Example
>>> # Valid resource proposal >>> action = {"type": "propose", "gpu": 6, "cpu": 4} >>> is_valid = game.is_valid_action("dev_team", action, game_state) >>> print(is_valid) True
>>> # Invalid proposal exceeding resources >>> invalid = {"type": "propose", "gpu": 12, "cpu": 8} >>> is_valid = game.is_valid_action("mkt_team", invalid, game_state) >>> print(is_valid) False
Note
Invalid actions are logged but do not raise exceptions, allowing graceful handling of malformed AI model responses and constraint violations.
- process_actions(actions: Dict[str, Dict[str, Any]], game_state: Dict[str, Any]) Dict[str, Any][source]
Process player actions with proposal limits and enhanced validation.
- is_game_over(game_state: Dict[str, Any]) bool[source]
Determine if the resource allocation negotiation has reached a terminal state.
Checks for various end conditions including resource agreement reached, maximum rounds exceeded, or explicit rejections that end negotiation.
- Parameters:
game_state (Dict[str, Any]) – Current game state to evaluate.
- Returns:
True if game should terminate, False if negotiation continues.
- Return type:
Example
>>> # Agreement reached >>> game_state = {"agreement_reached": True} >>> game.is_game_over(game_state) True >>> # Maximum rounds exceeded >>> game_state = {"current_round": 6, "agreement_reached": False} >>> game.is_game_over(game_state) # max_rounds = 5 True
- get_game_prompt(player_id: str) str[source]
Generate comprehensive resource allocation negotiation prompt for teams.
Creates detailed, contextual prompts for GPU and CPU resource negotiations between Development and Marketing teams. Includes current game state, resource constraints, utility calculations, and structured action formatting requirements. Uses neutral role terminology to minimize cognitive bias.
- Parameters:
player_id (str) – Identifier of the team requesting the prompt. Must be either development or marketing team identifier.
- Returns:
- Comprehensive negotiation prompt containing:
Team-specific role context and resource priorities
Current resource allocation status and constraints
BATNA thresholds and utility calculations
Opponent’s latest proposal (if available)
Available actions and JSON formatting requirements
Strategic guidance for resource optimization
Proposal limits and round tracking information
- Return type:
- Prompt Components:
Neutral role terminology (Team A/B vs development/marketing)
Resource constraint specifications (GPU/CPU limits)
Team-specific utility functions and preferences
Current negotiation state and round progression
BATNA-based acceptance criteria
Structured JSON response format requirements
Strategic recommendations for win-win solutions
- Resource Context:
Total GPU hours: 10 (split between teams)
Total CPU hours: 10 (split between teams)
Team-specific utility calculations
Time-decaying BATNA values
Example
>>> prompt = game.get_game_prompt("dev_team") >>> print("GPU hours" in prompt) # Resource context True >>> print("Team A" in prompt) # Neutral terminology True >>> print("JSON" in prompt) # Format requirements True
Note
Returns error message if game is not properly initialized with team assignments. Prompts adapt to current resource constraints and proposal limits.
- process_action(action: PlayerAction) Dict[str, Any][source]
Process a single team action and update the game state accordingly.
Handles individual team actions by converting to batch format and delegating to the process_actions method. Required by BaseGame interface for single-action processing compatibility.
- Parameters:
action (PlayerAction) – Team action to process containing player_id, action_type, action_data, timestamp, and round_number.
- Returns:
Updated game state after processing the action.
- Return type:
Dict[str, Any]
Example
>>> action = PlayerAction( ... player_id="development", ... action_type="propose", ... action_data={"gpu": 60, "cpu": 40}, ... timestamp=1609459200.0, ... round_number=2 ... ) >>> new_state = game.process_action(action)
- check_end_conditions() bool[source]
Check if the resource allocation negotiation should terminate.
Evaluates termination conditions by delegating to the is_game_over method. Required by BaseGame interface for consistent end condition checking across all game implementations.
- Returns:
True if game should end, False if negotiation continues.
- Return type:
Example
>>> game.check_end_conditions() True # If agreement reached or max rounds exceeded
- calculate_scores() Dict[str, float][source]
Calculate final utility scores for all participating teams.
Returns final utility values if resource agreement was reached, or BATNA values for both teams if negotiation failed. Required by BaseGame interface for consistent scoring across implementations.
- Returns:
- Mapping of team identifiers to final utility
scores. Positive values indicate successful resource allocation.
- Return type:
Example
>>> # Successful negotiation >>> scores = game.calculate_scores() >>> print(scores) {'development': 56.0, 'marketing': 44.0} >>> # Failed negotiation >>> scores = game.calculate_scores() >>> print(scores) {'development': 50.0, 'marketing': 45.0} # BATNA values
Integrative Negotiation Game
Multi-issue collaborative negotiation focusing on mutual value creation.
- class negotiation_platform.games.integrative_negotiation.IntegrativeNegotiationsGame(config: Dict[str, Any])[source]
Bases:
BaseGameIntegrative negotiations game between IT and Marketing teams with price bargaining logic.
Four issues with point values (unchanged from original): - Server Room Size: 50 sqm (10), 100 sqm (30), 150 sqm (60) - Meeting Room Access: 2 days/week (10), 4 days/week (30), 7 days/week (60) - Cleaning Responsibility: IT handles (10), Shared (30), Outsourced (60) - Branding Visibility: Minimal (10), Moderate (30), Prominent (60)
- __init__(config: Dict[str, Any])[source]
Initialize integrative negotiations game with configuration parameters.
Sets up multi-issue office space negotiation between IT and Marketing teams. Configures team preferences, BATNA values, issue structures, and decay rates based on provided configuration dictionary.
- Parameters:
config (Dict[str, Any]) – Game configuration containing: - batnas (Dict[str, float]): BATNA values for IT and Marketing teams - rounds (int): Maximum negotiation rounds allowed - batna_decay (float or Dict): Decay rate(s) for time pressure - issues (Dict, optional): Issue configurations and point values - weights (Dict, optional): Team preference weights by issue
- Raises:
ValueError – If required configuration fields are missing.
Example
>>> config = { ... "batnas": {"IT": 35, "Marketing": 30}, ... "rounds": 8, ... "batna_decay": 0.02 ... } >>> game = IntegrativeNegotiationsGame(config)
Note
Supports both hardcoded fallback values and flexible configuration for research customization and parameter sensitivity analysis.
- validate_json_response(response: str) bool[source]
Validate that AI model response is properly formatted JSON with required structure.
Performs structural validation of negotiation responses to ensure they contain the minimum required fields for processing. Used as a quick validation step before detailed parsing and action processing.
- Parameters:
response (str) – Raw response string from AI model to validate.
- Returns:
- True if response is valid JSON dict with “type” field,
False for malformed JSON or missing required structure.
- Return type:
- Validation Criteria:
Must be valid JSON syntax
Must parse to a dictionary object
Must contain “type” field for action identification
Example
>>> game.validate_json_response('{"type": "propose", "proposal": {}}') True >>> game.validate_json_response('invalid json') False >>> game.validate_json_response('{"proposal": {}}') False
Note
This is a lightweight validation step. Full semantic validation of proposals and action types occurs in subsequent processing steps.
- parse_json_response(response: str) Dict[str, Any][source]
Parse and extract decision data from AI model JSON responses with error recovery.
Handles multiple response formats and provides robust parsing with graceful error recovery for malformed responses. Extracts decision data and preserves raw response for debugging purposes.
- Parameters:
response (str) – Raw JSON response string from AI model containing negotiation decision and potentially surrounding text.
- Returns:
- Parsed response containing:
decision (Dict): Extracted action data with type and parameters
raw_response (str): Original unmodified response for debugging
- Return type:
Dict[str, Any]
- Response Format Handling:
Pure JSON: {“type”: “propose”, “proposal”: {…}}
Embedded JSON: Text containing JSON objects
Malformed JSON: Fallback to regex extraction and default rejection
- Error Recovery:
JSON parsing errors: Attempts regex extraction of key fields
Missing type field: Defaults to “reject” action
Invalid structure: Returns safe rejection response
Example
>>> response = '{"type": "propose", "proposal": {"server_room": 150}}' >>> parsed = game.parse_json_response(response) >>> print(parsed["decision"]["type"]) "propose" >>> print(parsed["decision"]["proposal"]) {"server_room": 150}
Note
Designed for robustness with AI model responses that may include explanatory text, formatting inconsistencies, or parsing errors.
- initialize_game(players: List[str]) Dict[str, Any][source]
Initialize the integrative negotiation game with randomized role assignments.
Sets up a multi-issue office space negotiation between IT and Marketing teams with randomized role assignment to minimize positional bias. Establishes complete game state including private information, utility functions, and BATNA parameters.
- Parameters:
players (List[str]) – List of exactly 2 player identifiers to participate in the bilateral negotiation.
- Returns:
- Complete initialized game state containing:
game_type (str): “integrative_negotiations”
players (List[str]): Player identifiers
role_assignments (Dict[str, str]): Mapping of roles to players
private_info (Dict[str, Dict]): Individual BATNAs and preferences
public_info (Dict[str, Any]): Shared issue descriptions
game_config (Dict[str, Any]): Configuration parameters
- Return type:
Dict[str, Any]
- Raises:
ValueError – If number of players is not exactly 2.
Example
>>> game = IntegrativeNegotiationsGame(config) >>> state = game.initialize_game(["alice", "bob"]) >>> state["role_assignments"] {"IT": "alice", "Marketing": "bob"}
Note
Role assignment is randomized to prevent systematic positional advantages. The first player is not always IT team.
- get_current_batna(player: str, round_num: int) float[source]
Calculate time-adjusted BATNA value accounting for negotiation urgency.
Computes the Best Alternative to a Negotiated Agreement with exponential decay to model increasing time pressure and opportunity costs as rounds progress. Different decay rates can be applied per team role.
- Parameters:
- Returns:
- Time-adjusted BATNA value for the specified player and round.
Always less than or equal to the initial BATNA value.
- Return type:
- Formula:
BATNA(t) = base_BATNA * (1 - decay_rate)^(round - 1)
Example
>>> game.get_current_batna("alice", 1) # Round 1 85.0 >>> game.get_current_batna("alice", 5) # Round 5 79.8 # Decreased due to time pressure
Note
Supports both uniform decay rates (float) and role-specific decay rates (dict) for asymmetric time pressure modeling.
- calculate_utility(player: str, proposal: Dict[str, Any]) float[source]
Calculate total weighted utility for a player given a specific proposal.
Computes utility by evaluating each negotiation issue against the player’s preferences and applying role-specific weights. Uses the additive utility model where total utility is the sum of weighted issue utilities.
- Parameters:
- Returns:
- Total weighted utility value for the player. Higher values
indicate more preferred outcomes.
- Return type:
- Utility Calculation:
For each issue: utility += issue_points * role_weight Where issue_points are determined by option selection and role_weights reflect strategic importance to the player’s team.
Example
>>> proposal = {"server_room": 150, "meeting_access": 4, ... "cleaning": "Shared", "branding": "Moderate"} >>> game.calculate_utility("alice", proposal) 87.5
Note
Returns 0.0 for invalid proposals or unrecognized players. Role assignment determines which weight set is applied.
- is_valid_proposal(proposal: Dict[str, Any]) bool[source]
Validate that a proposal contains valid selections for all negotiation issues.
Performs comprehensive validation to ensure proposals are complete and contain only valid options. Checks both completeness (all issues addressed) and validity (selections exist in defined option sets).
- Parameters:
proposal (Dict[str, Any]) – Proposal dictionary to validate containing issue names as keys and selected options as values.
- Returns:
- True if proposal is complete and contains only valid selections,
False if missing issues or invalid options detected.
- Return type:
- Validation Requirements:
Must be non-empty dictionary
Must contain all required issues (server_room, meeting_access, etc.)
All selections must exist in the corresponding issue option sets
Example
>>> valid = {"server_room": 150, "meeting_access": 4, ... "cleaning": "Shared", "branding": "Moderate"} >>> game.is_valid_proposal(valid) True >>> invalid = {"server_room": 200} # Missing issues, invalid size >>> game.is_valid_proposal(invalid) False
Note
This method validates structure and options but not semantic reasonableness or strategic value of proposals.
- is_valid_action(player: str, action: Dict[str, Any], game_state: Dict[str, Any]) bool[source]
Validate player action with enhanced structured format support and proposal limits.
- validate_response(response: Dict[str, Any]) bool[source]
Validate AI model response structure for required negotiation components.
Performs comprehensive validation of parsed responses to ensure they contain all necessary fields and valid action types for multi-issue negotiation processing.
- Parameters:
response (Dict[str, Any]) – Parsed response dictionary to validate containing action type and associated parameters.
- Returns:
- True if response contains valid action structure,
False if missing required fields or invalid action types.
- Return type:
- Validation Rules:
Must contain “type” and “proposal” keys
Action type must be “propose”, “accept”, or “reject”
Proposal validation handled separately by is_valid_proposal()
Example
>>> response = {"type": "propose", "proposal": {"server_room": 150}} >>> game.validate_response(response) True >>> invalid = {"type": "invalid_action"} >>> game.validate_response(invalid) False
Note
This method validates response structure but not semantic validity of proposals. Content validation occurs in downstream processing.
- process_actions(actions: Dict[str, Dict[str, Any]], game_state: Dict[str, Any]) Dict[str, Any][source]
Process player actions with proposal limits and enhanced JSON validation.
- is_game_over(game_state: Dict[str, Any]) bool[source]
Determine if the negotiation has reached a terminal state.
Checks multiple termination conditions to determine if the game should end. Used by the game engine to control round progression and trigger final result calculations.
- Parameters:
game_state (Dict[str, Any]) – Current game state containing round information and agreement status.
- Returns:
- True if game should terminate, False if negotiation
should continue with additional rounds.
- Return type:
- Termination Conditions:
Agreement reached between players
Game explicitly marked as ended
Maximum rounds exceeded
Example
>>> game.is_game_over({"agreement_reached": True}) True >>> game.is_game_over({"current_round": 10}) # max_rounds=8 True >>> game.is_game_over({"current_round": 3}) False
Note
Multiple termination conditions ensure robust game state management across different negotiation scenarios.
- get_winner(game_state: Dict[str, Any]) str | None[source]
Determine negotiation winner based on utility surplus over BATNA.
Identifies the player who achieved the greatest benefit from the negotiation by comparing their utility gain above their Best Alternative to a Negotiated Agreement (BATNA). No winner is declared for failed negotiations.
- Parameters:
game_state (Dict[str, Any]) – Final game state containing agreement details and utility calculations.
- Returns:
- Player identifier of the winner, or None if:
No agreement was reached
Both players have equal utility surplus
- Return type:
Optional[str]
- Winner Criteria:
Winner = max(utility - time_adjusted_BATNA) for each player Only positive surpluses indicate successful negotiation outcomes.
Example
>>> # Player utilities: alice=85, bob=78; BATNAs: alice=75, bob=80 >>> game.get_winner(final_state) "alice" # Surplus: alice=10, bob=-2 >>> game.get_winner(no_agreement_state) None # No agreement reached
Note
Winner determination encourages value-creating negotiations rather than purely competitive zero-sum outcomes.
- get_game_summary(game_state: Dict[str, Any]) Dict[str, Any][source]
Generate comprehensive summary of negotiation results for analysis.
Creates a structured summary containing all key negotiation outcomes, player roles, agreement details, and utility calculations. Used for research analysis, reporting, and comparative studies.
- Parameters:
game_state (Dict[str, Any]) – Final game state containing complete negotiation history and outcomes.
- Returns:
- Comprehensive summary containing:
game_type: “Integrative Negotiations”
players: Mapping of player IDs to role names
agreement_reached: Boolean success indicator
agreement_details: Final proposal if successful
utilities: Final utility values for each player
failure_reason: Explanation if negotiation failed
- Return type:
Dict[str, Any]
- Summary Structure:
Successful negotiations include agreement round, final proposal, utilities, and detailed breakdowns. Failed negotiations include failure reasons and context.
Example
>>> summary = game.get_game_summary(final_state) >>> summary["agreement_reached"] True >>> summary["final_agreement"] {"server_room": 150, "cleaning": "Shared"}
Note
Provides standardized output format for research data collection and comparative analysis across different negotiation scenarios.
- process_action(action) Dict[str, Any][source]
Process a single player action (required by BaseGame interface).
Implements the abstract BaseGame method for single-action processing. In integrative negotiations, multi-player action processing via process_actions() is preferred. This method serves as a compatibility interface for the base class contract.
- Parameters:
action – Single player action to process (format varies).
- Returns:
Processing result indicating successful handling.
- Return type:
Dict[str, Any]
- Implementation Note:
This game uses simultaneous bilateral action processing through process_actions() rather than sequential single-action processing. This method provides base class compatibility.
Example
>>> result = game.process_action({"type": "propose"}) >>> result["processed"] True
Note
For actual negotiation processing, use process_actions() which handles simultaneous bilateral actions appropriately.
- check_end_conditions() bool[source]
Check if the game should end (required by BaseGame interface).
Implements the abstract BaseGame method for termination checking. Delegates to the state-based is_game_over() method when game data is available, providing base class compatibility.
- Returns:
- True if game should terminate, False otherwise.
Delegates to is_game_over() when game state exists.
- Return type:
- Implementation Note:
This game uses state-based termination checking through is_game_over() which analyzes current game state for termination conditions.
Example
>>> game.check_end_conditions() True # If agreement reached or rounds exceeded
Note
Primary termination logic resides in is_game_over() which requires game state for proper condition evaluation.
- calculate_scores() Dict[str, float][source]
Calculate final scores for all players (required by BaseGame interface).
Implements the abstract BaseGame method for score calculation. Returns final utilities from completed negotiations or zero scores for failed negotiations, providing base class compatibility.
- Returns:
- Dictionary mapping player IDs to final scores:
Successful negotiations: actual utility values
Failed negotiations: 0.0 for all players
No game data: 0.0 for all players
- Return type:
- Score Calculation:
Scores are the final utility values calculated during agreement creation, representing negotiated value for each player.
Example
>>> scores = game.calculate_scores() >>> scores {"alice": 87.5, "bob": 78.3} # After successful negotiation
Note
Scores represent utility values rather than competitive rankings. Both players can achieve positive scores in value-creating negotiations.
- get_game_prompt(player_id: str, game_state: Dict[str, Any] | None = None) str[source]
Generate structured negotiation prompt for AI model interaction.
Creates comprehensive, role-specific prompts that provide players with all necessary context for strategic decision-making. Includes current situation, available options, utility guidance, and proper JSON response formatting requirements.
- Parameters:
- Returns:
- Complete formatted prompt string containing:
Current round and role information
Available options and point values
Role-specific priorities and preferences
Proposal history and opponent actions
Response format requirements and examples
- Return type:
- Prompt Components:
Header with round/role identification
BATNA and remaining proposal information
Option descriptions with utility values
Strategic guidance based on current situation
JSON format requirements and examples
Example
>>> prompt = game.get_game_prompt("alice", game_state) >>> "OFFICE SPACE NEGOTIATION" in prompt True >>> "RESPONSE FORMAT" in prompt True
Note
Prompts use neutral role labels to minimize cognitive bias while providing complete strategic context for informed decisions.
Negotiation Tools
Supporting utilities and tools for game implementations.
Negotiation Tools
Reusable utility functions and algorithms for all negotiation game types.
This module provides a comprehensive toolkit of mathematical functions, validation utilities, and strategic algorithms that support negotiation game implementations. These tools are designed to be game-agnostic and reusable across different negotiation scenarios and game types.
- Key Features:
Mathematical utility functions for offer analysis
BATNA comparison and evaluation tools
Strategic suggestion algorithms for offer generation
Constraint validation systems for proposal checking
Generic utility calculation for multi-issue negotiations
Percentage-based comparison utilities for fairness analysis
- Function Categories:
Comparison Functions: Mathematical analysis of offers and values
BATNA Tools: Best Alternative to Negotiated Agreement evaluation
Strategic Functions: Algorithmic suggestions for negotiation moves
Validation Tools: Constraint checking and proposal validation
Utility Calculators: Multi-issue scoring and preference evaluation
- Design Philosophy:
These tools follow functional programming principles with pure functions that have no side effects. Each function is self-contained and can be used independently or composed together for complex game logic.
- Example Usage:
>>> # Calculate offer attractiveness >>> offer_value = 42000 >>> batna_value = 40000 >>> is_attractive = is_offer_above_batna(offer_value, batna_value) >>> >>> # Suggest strategic next move >>> next_offer = suggest_next_offer(42000, 40000, True, 0.05) >>> >>> # Calculate multi-issue utility >>> proposal = {"server_room": 100, "meeting_access": 4} >>> weights = {"server_room": 0.6, "meeting_access": 0.4} >>> points = {"server_room": {100: 30}, "meeting_access": {4: 30}} >>> utility = calculate_utility(proposal, weights, points)
- negotiation_platform.games.negotiation_tools.calculate_percentage_difference(a: float, b: float) float[source]
Calculate the percentage difference between two values.
Computes the relative difference between two numeric values as a percentage of the second value. Useful for analyzing offer changes, price movements, and comparative value assessments in negotiations.
- Parameters:
- Returns:
- Percentage difference as a decimal (0.0 to infinity).
Returns 0.0 if the reference value b is zero to avoid division errors.
- Return type:
Example
>>> # 10% increase from base price >>> diff = calculate_percentage_difference(44000, 40000) >>> print(f"{diff:.1f}%") 10.0%
>>> # Price reduction analysis >>> old_price = 45000 >>> new_price = 42000 >>> reduction = calculate_percentage_difference(new_price, old_price) >>> print(f"Price reduced by {reduction:.1f}%") Price reduced by 6.7%
Note
The function returns the absolute percentage difference. Use additional logic to determine if the change is an increase or decrease.
- negotiation_platform.games.negotiation_tools.is_offer_above_batna(offer: float, batna: float) bool[source]
Check if an offer value is above or equal to the BATNA threshold.
Determines whether a proposed offer meets the minimum acceptable threshold defined by the Best Alternative to Negotiated Agreement (BATNA). This is a fundamental decision criterion in negotiation theory.
- Parameters:
- Returns:
True if offer is greater than or equal to BATNA, False otherwise.
- Return type:
Example
>>> # Buyer evaluating a seller's offer >>> seller_offer = 41000 >>> buyer_batna = 40000 # Best alternative option >>> should_consider = is_offer_above_batna(seller_offer, buyer_batna) >>> print(f"Offer worth considering: {should_consider}") Offer worth considering: True
>>> # Seller evaluating buyer's offer >>> buyer_offer = 38000 >>> seller_batna = 39000 # Alternative buyer option >>> should_accept = is_offer_above_batna(buyer_offer, seller_batna) >>> print(f"Offer acceptable: {should_accept}") Offer acceptable: False
- Strategic Usage:
Accept offers above BATNA (creates positive value)
Reject offers below BATNA (destroys value compared to alternatives)
Use BATNA as minimum threshold in counteroffers
- negotiation_platform.games.negotiation_tools.is_offer_below_batna(offer: float, batna: float) bool[source]
Check if an offer value is below or equal to the BATNA threshold.
Determines whether a proposed offer falls below the minimum acceptable threshold. This is useful for identifying offers that should typically be rejected as they provide less value than available alternatives.
- Parameters:
- Returns:
True if offer is less than or equal to BATNA, False otherwise.
- Return type:
Example
>>> # Quick rejection check >>> low_offer = 37000 >>> batna_threshold = 39000 >>> should_reject = is_offer_below_batna(low_offer, batna_threshold) >>> print(f"Offer below threshold: {should_reject}") Offer below threshold: True
Note
This is the logical inverse of is_offer_above_batna() but provides semantic clarity when checking for unacceptable offers.
- negotiation_platform.games.negotiation_tools.suggest_next_offer(current_offer: float, batna: float, last_rejected: bool, step: float = 0.05) float[source]
Generate strategic suggestion for next negotiation offer using BATNA-based algorithm.
Provides algorithmic guidance for offer adjustment based on previous negotiation outcomes and BATNA positioning. Implements a conservative strategy that moves offers closer to BATNA when faced with rejection, encouraging agreement while maintaining value protection.
- Parameters:
current_offer (float) – The most recent offer value made in negotiation.
batna (float) – Best Alternative to Negotiated Agreement value.
last_rejected (bool) – Whether the previous offer was rejected by counterpart.
step (float, optional) – Percentage adjustment toward BATNA when rejected. Defaults to 0.05 (5% movement).
- Returns:
- Suggested next offer value. If last offer was accepted or this is
initial offer, returns current_offer unchanged. If rejected, returns adjusted offer moved toward BATNA by the specified step percentage.
- Return type:
- Algorithm:
If last offer accepted: maintain current position
If last offer rejected: move toward BATNA by step percentage
Direction determined by BATNA position relative to current offer
Step size controls aggressiveness of concession
Example
>>> # Buyer's offer was rejected, move toward seller >>> current = 40000 # Buyer's last offer >>> batna = 41000 # Buyer's alternative option >>> rejected = True >>> next_offer = suggest_next_offer(current, batna, rejected, 0.05) >>> print(f"Next offer: ${next_offer:,.0f}") Next offer: $40,050
>>> # Seller's offer was rejected, move toward buyer >>> current = 44000 # Seller's last offer >>> batna = 42000 # Seller's alternative option >>> rejected = True >>> next_offer = suggest_next_offer(current, batna, rejected, 0.10) >>> print(f"Next offer: ${next_offer:,.0f}") Next offer: $43,800
- Strategic Considerations:
Smaller steps (0.01-0.03): Conservative, slow concessions
Moderate steps (0.05-0.10): Balanced concession strategy
Larger steps (0.15+): Aggressive movement toward agreement
- negotiation_platform.games.negotiation_tools.check_constraints(offer: Dict[str, Any], constraints: Dict[str, Any]) bool[source]
Validate offer against multiple constraint functions for proposal acceptance.
Evaluates a complex offer or proposal against a set of constraint validation functions. Each constraint represents a business rule, resource limit, or negotiation requirement that must be satisfied for the offer to be valid.
- Parameters:
offer (Dict[str, Any]) – Proposal dictionary containing offer terms and values. Structure varies by game type but typically includes resource allocations, prices, or multi-issue terms.
constraints (Dict[str, Any]) – Dictionary mapping constraint names to validation functions. Each function should accept the offer dict and return boolean.
- Returns:
True if offer satisfies ALL constraints, False if any constraint fails.
- Return type:
- Constraint Function Examples:
Resource limits: lambda offer: offer[“gpu_hours”] <= 10
Budget constraints: lambda offer: offer[“total_cost”] <= 50000
Logical requirements: lambda offer: offer[“start_date”] < offer[“end_date”]
Multi-field validation: lambda offer: offer[“gpus”] + offer[“cpus”] <= offer[“budget”]/1000
Example
>>> # Resource allocation validation >>> offer = {"gpu_hours": 6, "developer_hours": 4, "budget": 45000} >>> constraints = { ... "gpu_limit": lambda o: o["gpu_hours"] <= 8, ... "dev_limit": lambda o: o["developer_hours"] <= 5, ... "budget_limit": lambda o: o["budget"] <= 50000, ... "resource_balance": lambda o: o["gpu_hours"] * 1000 <= o["budget"] ... } >>> is_valid = check_constraints(offer, constraints) >>> print(f"Offer valid: {is_valid}") Offer valid: True
>>> # Price negotiation validation >>> car_offer = {"price": 43000, "warranty": True, "delivery_days": 14} >>> car_constraints = { ... "price_range": lambda o: 35000 <= o["price"] <= 50000, ... "delivery_time": lambda o: o["delivery_days"] <= 30 ... } >>> valid_car_deal = check_constraints(car_offer, car_constraints)
- Usage Patterns:
Game rule validation before processing actions
Resource constraint checking in allocation games
Business logic validation for complex proposals
Multi-criteria decision support systems
- Error Handling:
Function returns False if any constraint function raises an exception, providing graceful handling of invalid offer structures or constraint errors.
- negotiation_platform.games.negotiation_tools.calculate_utility(offer: Dict[str, Any], weights: Dict[str, float], points: Dict[str, Dict[Any, float]]) float[source]
Calculate total utility value for multi-issue negotiation proposals.
Computes weighted utility scores for complex proposals involving multiple negotiation issues. Each issue contributes to total utility based on its importance weight and the point value associated with the chosen option. This enables quantitative comparison of multi-dimensional offers.
- Parameters:
offer (Dict[str, Any]) – Proposal dictionary mapping issue names to chosen options/values for each negotiation issue.
weights (Dict[str, float]) – Importance weights for each issue, determining how much each issue contributes to total utility. Weights typically sum to 1.0 but not required.
points (Dict[str, Dict[Any, float]]) – Point value mappings for each issue. Structure: {issue: {option: point_value}}
- Returns:
- Total weighted utility score. Higher values indicate more
attractive proposals for the evaluating party.
- Return type:
- Calculation Formula:
utility = Σ(weight[issue] × points[issue][option]) for all issues
Example
>>> # IT team evaluating office space proposal >>> proposal = { ... "server_room": 150, # 150 sqm server space ... "meeting_access": 2, # 2 days per week ... "cleaning": "Shared", # Shared cleaning responsibility ... "branding": "Prominent" # Prominent marketing visibility ... } >>> >>> # IT team's importance weights >>> it_weights = { ... "server_room": 0.40, # Server space is top priority ... "meeting_access": 0.10, # Low meeting room needs ... "cleaning": 0.30, # Moderate cleaning concern ... "branding": 0.20 # Low branding priority ... } >>> >>> # Point values for each option from IT perspective >>> it_points = { ... "server_room": {50: 10, 100: 30, 150: 60}, ... "meeting_access": {2: 10, 4: 30, 7: 60}, ... "cleaning": {"IT": 30, "Shared": 50, "Outsourced": 10}, ... "branding": {"Minimal": 10, "Moderate": 30, "Prominent": 60} ... } >>> >>> utility = calculate_utility(proposal, it_weights, it_points) >>> print(f"IT team utility: {utility:.1f}") IT team utility: 53.0
>>> # Marketing team would have different weights and potentially points >>> marketing_weights = { ... "server_room": 0.10, # Low server space priority ... "meeting_access": 0.30, # High meeting room needs ... "cleaning": 0.20, # Moderate cleaning concern ... "branding": 0.40 # Branding is top priority ... } >>> marketing_utility = calculate_utility(proposal, marketing_weights, it_points) >>> print(f"Marketing team utility: {marketing_utility:.1f}") Marketing team utility: 42.0
- Usage Patterns:
Multi-issue negotiation evaluation
Proposal ranking and comparison
Win-win solution identification
Pareto efficiency analysis
Automated offer generation and optimization
Note
Missing issues in offer, weights, or points are treated as zero contribution. This provides graceful handling of partial proposals or incomplete data.