Better performance score
This commit is contained in:
@@ -51,8 +51,10 @@ class AiHttpGameHandler(GameHandler):
|
|||||||
STEPS_WEIGHT = 10
|
STEPS_WEIGHT = 10
|
||||||
DISTANCE_WEIGHT = 10
|
DISTANCE_WEIGHT = 10
|
||||||
WIN_WEIGHT = 45
|
WIN_WEIGHT = 45
|
||||||
PLAY_ALL_MAPS_PER_GENERATION = True # Are all maps played during a generation or only MAPS_PER_GENERATION maps per generation are played
|
PLAY_ALL_MAPS_PER_GENERATION = False # Are all maps played during a generation or only MAPS_PER_GENERATION maps per generation are played
|
||||||
MAPS_PER_GENERATION = 9 # Evaluate across x maps per generation. Only used if PLAY_ALL_MAPS_PER_GENERATION is False. If True, all maps are played in a generation.
|
MAPS_PER_GENERATION = 3 # Evaluate across x maps per generation. Only used if PLAY_ALL_MAPS_PER_GENERATION is False. If True, all maps are played in a generation.
|
||||||
|
MAP_PASS_MIN_SCORE = 20 # Minimum score to consider a map passed. Only used if PLAY_ALL_MAPS_PER_GENERATION is False. If True, all maps are played in a generation.
|
||||||
|
MAP_DIFFICULTY_VARIANCE = 5 # Lower value will make the map selection more variable, higher value will make the map selection more consistent with the current performance. Only used if PLAY_ALL_MAPS_PER_GENERATION is False. A value too high may produce no evolving behavior
|
||||||
CONSISTENCY_PENALTY_WEIGHT = 0.4 # Penalty for inconsistent map scores
|
CONSISTENCY_PENALTY_WEIGHT = 0.4 # Penalty for inconsistent map scores
|
||||||
|
|
||||||
def __init__(self, host: str | None = None, port: int | None = None,
|
def __init__(self, host: str | None = None, port: int | None = None,
|
||||||
@@ -155,11 +157,7 @@ class AiHttpGameHandler(GameHandler):
|
|||||||
if self.PLAY_ALL_MAPS_PER_GENERATION:
|
if self.PLAY_ALL_MAPS_PER_GENERATION:
|
||||||
session["generation_maps"] = self.map_set.get_maps()
|
session["generation_maps"] = self.map_set.get_maps()
|
||||||
else:
|
else:
|
||||||
available_maps = self.map_set.get_maps_by_difficulty(round(perf), self.MAPS_PER_GENERATION)
|
session["generation_maps"] = sorted(self.map_set.get_maps_by_difficulty(round(perf), self.MAPS_PER_GENERATION), key=lambda m: m.difficulty)
|
||||||
session["generation_maps"] = [
|
|
||||||
random.choice(available_maps)
|
|
||||||
for _ in range(self.MAPS_PER_GENERATION) # Randomly selects in case there is less maps available than MAPS_PER_GENERATION
|
|
||||||
]
|
|
||||||
|
|
||||||
# Fetch the pre-selected map for the current round
|
# Fetch the pre-selected map for the current round
|
||||||
map_round = session.get("map_round", 0)
|
map_round = session.get("map_round", 0)
|
||||||
@@ -233,14 +231,14 @@ class AiHttpGameHandler(GameHandler):
|
|||||||
session["map_scores"][model_id].append(res["score"])
|
session["map_scores"][model_id].append(res["score"])
|
||||||
|
|
||||||
# Check if we have completed all maps for this generation
|
# Check if we have completed all maps for this generation
|
||||||
if session["map_round"] < self.MAPS_PER_JSON_ROUND if hasattr(self, 'MAPS_PER_JSON_ROUND') else session["map_round"] < self.MAPS_PER_GENERATION:
|
if session["map_round"] < len(session["generation_maps"]):
|
||||||
# More maps remaining in this generation! Tell client to simulate next map.
|
# More maps remaining in this generation! Tell client to simulate next map.
|
||||||
response["type"] = "map_round_complete"
|
response["type"] = "map_round_complete"
|
||||||
response["map_round"] = session["map_round"]
|
response["map_round"] = session["map_round"]
|
||||||
session["simulations"] = None
|
session["simulations"] = None
|
||||||
session["last_result"] = response
|
session["last_result"] = response
|
||||||
else:
|
else:
|
||||||
# All 3 maps completed -> finalize generation results (Average scores)
|
# All maps completed -> finalize generation results (Average scores)
|
||||||
response["type"] = "simulation_result"
|
response["type"] = "simulation_result"
|
||||||
final_results = {}
|
final_results = {}
|
||||||
|
|
||||||
@@ -261,26 +259,18 @@ class AiHttpGameHandler(GameHandler):
|
|||||||
response["results"] = final_results
|
response["results"] = final_results
|
||||||
best_score = max(res["score"] for res in final_results.values())
|
best_score = max(res["score"] for res in final_results.values())
|
||||||
best_model_id = max(final_results, key=lambda mid: final_results[mid]["score"])
|
best_model_id = max(final_results, key=lambda mid: final_results[mid]["score"])
|
||||||
map_difficulty = session["map"].difficulty
|
|
||||||
response["score"] = best_score
|
response["score"] = best_score
|
||||||
|
|
||||||
# --- Curriculum Regression & Performance Update ---
|
# --- Performance Update ---
|
||||||
PASSING_THRESHOLD = 85.0
|
# Average score of the 90th percentile of the final results
|
||||||
if best_score < PASSING_THRESHOLD:
|
sorted_scores = sorted((res["score"] for res in final_results.values()), reverse=True)
|
||||||
session["stagnant_generations"] = session.get("stagnant_generations", 0) + 1
|
top_90_count = max(1, round(len(sorted_scores) * 0.9))
|
||||||
else:
|
top_90_scores = sorted_scores[:top_90_count]
|
||||||
session["stagnant_generations"] = 0
|
avg_top_90_score = sum(top_90_scores) / len(top_90_scores)
|
||||||
|
performance_delta = (avg_top_90_score - self.MAP_PASS_MIN_SCORE) / self.MAP_DIFFICULTY_VARIANCE
|
||||||
if session["stagnant_generations"] >= 15:
|
performance_delta = max(0.05, performance_delta)
|
||||||
current_perf = session.get("performance", 10 * math.sqrt(map_difficulty))
|
session["performance"] = max(0.0, session.get("performance", 0.0) + performance_delta)
|
||||||
regression_drop = 15.0
|
# print(f"Generation {session['generation']} completed on difficulty {map_difficulty}. Best score: {best_score:.2f}. Updated performance: {session['performance']:.2f}", flush=True)
|
||||||
session["performance"] = max(0.0, current_perf - regression_drop)
|
|
||||||
print(f"Stuck on difficulty {map_difficulty} for {session['stagnant_generations']} generations. Dropping target performance to {session['performance']:.1f}", flush=True)
|
|
||||||
session["stagnant_generations"] = 0
|
|
||||||
else:
|
|
||||||
difficulty_scaling = 10 * math.sqrt(map_difficulty)
|
|
||||||
normalized_score = best_score / 100.0
|
|
||||||
session["performance"] = difficulty_scaling * (0.5 + 0.5 * normalized_score)
|
|
||||||
|
|
||||||
# Reset round counter for the next generation
|
# Reset round counter for the next generation
|
||||||
session["map_round"] = 0
|
session["map_round"] = 0
|
||||||
@@ -290,6 +280,7 @@ class AiHttpGameHandler(GameHandler):
|
|||||||
deepcopy(session["map"]),
|
deepcopy(session["map"]),
|
||||||
list(simulations[best_model_id].moves),
|
list(simulations[best_model_id].moves),
|
||||||
session["generation"],
|
session["generation"],
|
||||||
|
session["performance"],
|
||||||
)
|
)
|
||||||
replay_condition = session.get("replay_condition")
|
replay_condition = session.get("replay_condition")
|
||||||
if replay_condition is not None:
|
if replay_condition is not None:
|
||||||
@@ -311,7 +302,7 @@ class AiHttpGameHandler(GameHandler):
|
|||||||
return
|
return
|
||||||
self._show_best_run(*best_run)
|
self._show_best_run(*best_run)
|
||||||
|
|
||||||
def _show_best_run(self, game_map, moves: list[str], generation: int) -> None:
|
def _show_best_run(self, game_map, moves: list[str], generation: int, performance: float) -> None:
|
||||||
game = Game(game_map, GameState())
|
game = Game(game_map, GameState())
|
||||||
remaining_moves = iter(moves)
|
remaining_moves = iter(moves)
|
||||||
|
|
||||||
@@ -327,12 +318,14 @@ class AiHttpGameHandler(GameHandler):
|
|||||||
frame_lines = []
|
frame_lines = []
|
||||||
for index, line in enumerate(game_string.splitlines()):
|
for index, line in enumerate(game_string.splitlines()):
|
||||||
if index == 0:
|
if index == 0:
|
||||||
line += "\tCoins: " + str(game.gameState.coins)
|
|
||||||
elif index == 1:
|
|
||||||
line += "\tMoves: " + str(game.gameState.movesCount)
|
|
||||||
elif index == 2:
|
|
||||||
line += "\tGeneration: " + str(generation)
|
line += "\tGeneration: " + str(generation)
|
||||||
|
elif index == 1:
|
||||||
|
line += "\tPerformance: " + str(round(performance, 2))
|
||||||
|
elif index == 2:
|
||||||
|
line += "\tCoins: " + str(game.gameState.coins)
|
||||||
elif index == 3:
|
elif index == 3:
|
||||||
|
line += "\tMoves: " + str(game.gameState.movesCount)
|
||||||
|
elif index == 4:
|
||||||
line += "\tMove: " + moves[game.gameState.movesCount - 1] if len(moves) >= game.gameState.movesCount else "\tMove: None"
|
line += "\tMove: " + moves[game.gameState.movesCount - 1] if len(moves) >= game.gameState.movesCount else "\tMove: None"
|
||||||
frame_lines.append(line)
|
frame_lines.append(line)
|
||||||
print("\033[2J\033[H\n\n\n" + "\n".join(frame_lines), flush=True)
|
print("\033[2J\033[H\n\n\n" + "\n".join(frame_lines), flush=True)
|
||||||
|
|||||||
Reference in New Issue
Block a user