feat(management): add symmetric difficulty per-player support

Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
This commit is contained in:
Natalie 2026-04-18 21:25:26 -07:00
parent 3ec49e76e7
commit 6ac6b0b967
3 changed files with 38 additions and 23 deletions

View file

@ -197,8 +197,13 @@ case "${MODE}" in
SEEDS="${2:-10}"; TURNS="${3:-300}"
GPU_ENV="AI_GPU_ROLLOUT=${AI_GPU_ROLLOUT:-false}"
echo "[$(date +%H:%M:%S)] difficulty=${DIFF_TIER} batch: ${SEEDS} seeds T${TURNS} PARALLEL=${PARALLEL} ${GPU_ENV}"
# AI_DIFFICULTY_P0 + AI_DIFFICULTY_P1 apply the modifier to BOTH players
# (including the human-slot player 0 which is_human=true). This is
# required for symmetric Easy-vs-Easy / Hard-vs-Hard tier_peak differentiation.
ssh "${APRICOT}" "set -euo pipefail; cd '${SCRATCH_ABS}' && \
AI_USE_MCTS=true AI_DIFFICULTY='${DIFF_TIER}' ${GPU_ENV} PARALLEL=${PARALLEL} \
AI_USE_MCTS=true AI_DIFFICULTY='${DIFF_TIER}' \
AI_DIFFICULTY_P0='${DIFF_TIER}' AI_DIFFICULTY_P1='${DIFF_TIER}' \
${GPU_ENV} PARALLEL=${PARALLEL} \
bash tools/autoplay-batch.sh ${SEEDS} ${TURNS} ${RESULTS_ABS}/difficulty-${DIFF_TIER} 2>&1 | tail -30"
;;
difficulty-asym)

View file

@ -52,13 +52,16 @@ func _process_production(player: RefCounted) -> void: # Player
if game_map == null:
return
# Apply difficulty modifier for AI players (per-player override takes precedence)
# Per-player override applies to any player (including human in difficulty batches).
# Global ai_difficulty_modifier only applies to non-human players.
var prod_modifier: float = 1.0
if player is PlayerScript and not player.is_human:
var per_player: float = float(
GameState.ai_per_player_production_mult.get(player.index, 0.0)
)
prod_modifier = per_player if per_player > 0.0 else GameState.ai_difficulty_modifier
var _per_player_prod: float = float(
GameState.ai_per_player_production_mult.get(player.index, 0.0)
)
if _per_player_prod > 0.0:
prod_modifier = _per_player_prod
elif player is PlayerScript and not player.is_human:
prod_modifier = GameState.ai_difficulty_modifier
# Unhappy penalty: -25% production when happiness < 0
if player.happiness < 0:
prod_modifier *= 0.75
@ -158,13 +161,16 @@ func _process_research(player: RefCounted) -> void: # Player
if EnvConfig.get_bool("FORCE_UNLIMITED_RESEARCH"):
player.research_progress = 999999
# Apply difficulty modifier for AI players (per-player override takes precedence)
# Per-player override applies to any player (including human in difficulty batches).
# Global ai_research_modifier only applies to non-human players.
var sci_modifier: float = 1.0
if player is PlayerScript and not player.is_human:
var per_player: float = float(
GameState.ai_per_player_research_mult.get(player.index, 0.0)
)
sci_modifier = per_player if per_player > 0.0 else GameState.ai_research_modifier
var _per_player_sci: float = float(
GameState.ai_per_player_research_mult.get(player.index, 0.0)
)
if _per_player_sci > 0.0:
sci_modifier = _per_player_sci
elif player is PlayerScript and not player.is_human:
sci_modifier = GameState.ai_research_modifier
player.research_progress += int(player.science_per_turn * sci_modifier)

View file

@ -22,11 +22,13 @@ static func process_production(
if game_map == null:
return
var prod_modifier: float = 1.0
if player is PlayerScript and not player.is_human:
var per_player: float = float(
GameState.ai_per_player_production_mult.get(player.index, 0.0)
)
prod_modifier = per_player if per_player > 0.0 else GameState.ai_difficulty_modifier
var _per_player_prod: float = float(
GameState.ai_per_player_production_mult.get(player.index, 0.0)
)
if _per_player_prod > 0.0:
prod_modifier = _per_player_prod
elif player is PlayerScript and not player.is_human:
prod_modifier = GameState.ai_difficulty_modifier
for city: RefCounted in player.cities:
if not city is CityScript:
continue
@ -358,11 +360,13 @@ static func build_tile_yields_json(
static func _calculate_science_income(player: RefCounted) -> int:
var sci_modifier: float = 1.0
if player is PlayerScript and not player.is_human:
var per_player: float = float(
GameState.ai_per_player_research_mult.get(player.index, 0.0)
)
sci_modifier = per_player if per_player > 0.0 else GameState.ai_research_modifier
var _per_player_sci: float = float(
GameState.ai_per_player_research_mult.get(player.index, 0.0)
)
if _per_player_sci > 0.0:
sci_modifier = _per_player_sci
elif player is PlayerScript and not player.is_human:
sci_modifier = GameState.ai_research_modifier
var science: int = int(player.science_per_turn * sci_modifier)
var game_map: RefCounted = GameState.get_game_map()
if game_map == null: