add timeout budget when exeaded use quick save move before timeout

This commit is contained in:
2026-04-03 21:17:08 +02:00
parent f124ce6f96
commit 8f6bc3cfdd
2 changed files with 43 additions and 3 deletions
+6 -1
View File
@@ -16,12 +16,13 @@ class GameBoard:
self.ruleset = ruleset
self.map = map
self.url = self._get_game_url(True if ruleset["version"] == "cli" else False)
self.timeout = 500
# Setter Functions
def _set_snakes(self, snakes:list[dict]):
self.other_snakes = [ x for x in snakes if x["id"] != self.my_snake["id"] ]
def _set_my_snake(self, my_snake:str):
def _set_my_snake(self, my_snake:dict):
self.my_snake = my_snake
def _set_food(self, food:list[dict]):
@@ -67,6 +68,9 @@ class GameBoard:
def get_ruleset(self):
return self.ruleset
def get_timeout(self):
return self.timeout
def get_my_snake_head(self):
return self.my_snake["head"]
@@ -97,6 +101,7 @@ class GameBoard:
self._set_snakes(game_data['board']['snakes'])
self._set_turn(game_data["turn"])
self.timeout = int(game_data.get('game', {}).get('timeout', 500))
async def start_game(self, game_data:dict):
self.init_snakes = len(game_data['board']['snakes'])
+37 -2
View File
@@ -2,6 +2,7 @@ from collections.abc import Iterator
from collections import deque
from typing import Any, cast
import random, os
from time import perf_counter
from snakes.TemplateSnake import TemplateSnake
@@ -76,6 +77,8 @@ class BestBattleSnake(TemplateSnake):
self.game_board = game_data
self.calculations = []
self.duel_style = self._get_duel_style()
timeout_ms = (game_data.get_timeout() if hasattr(game_data, "get_timeout") else 500)
deadline = perf_counter() + (max(50, int(timeout_ms) - 120) / 1000.0)
game_id = getattr(game_data, "id", None)
turn = game_data.get_turn()
@@ -161,6 +164,7 @@ class BestBattleSnake(TemplateSnake):
enemy_can_grow_cache=enemy_can_grow_cache,
width=width,
height=height,
deadline=deadline,
)
self.recent_heads.append(current_head_point)
self.last_move = best_move
@@ -183,6 +187,7 @@ class BestBattleSnake(TemplateSnake):
hazard_damage=hazard_damage,
width=width,
height=height,
deadline=deadline,
)
self.recent_heads.append(current_head_point)
self.last_move = best_move
@@ -193,6 +198,8 @@ class BestBattleSnake(TemplateSnake):
scores:dict[str, float] = {}
move_safety:dict[str, dict[str, Any]] = {}
for move, pos in safe_moves.items():
if self._time_exceeded(deadline):
break
point = (pos["x"], pos["y"])
ate_food = point in food_set
@@ -307,6 +314,18 @@ class BestBattleSnake(TemplateSnake):
scores[move] = round(score, 5)
if not scores:
quick_move = (
self.last_move
if self.last_move in safe_moves
else random.choice(list(safe_moves.keys()))
)
self.recent_heads.append(current_head_point)
self.last_move = quick_move
self.add_to_history({"turn": turn, "move": quick_move, "reason": "timeout_budget"})
self.previous_hazards = set(hazard_set)
return quick_move
survivable_moves = [
move for move, data in move_safety.items() if data["is_survivable"]
]
@@ -341,7 +360,7 @@ class BestBattleSnake(TemplateSnake):
self.previous_hazards = set(hazard_set)
return best_move
def _choose_duel_move(self, safe_moves:MoveMap, my_body:list[Coord], my_len:int, my_health:int, food_set:set[Point], hazard_set:set[Point], other_snakes:list[SnakeState],enemy_attack_map:AttackMap, enemy_can_grow_cache:dict[Any, bool], previous_hazard_set:set[Point], hazard_damage:int, width:int, height:int) -> tuple[str, dict[str, float]]:
def _choose_duel_move(self, safe_moves:MoveMap, my_body:list[Coord], my_len:int, my_health:int, food_set:set[Point], hazard_set:set[Point], other_snakes:list[SnakeState], enemy_attack_map:AttackMap, enemy_can_grow_cache:dict[Any, bool], previous_hazard_set:set[Point], hazard_damage:int, width:int, height:int, deadline:float|None=None) -> tuple[str, dict[str, float]]:
"""Score and select a move for one-vs-one games."""
duel_weights = self._duel_weights(self.duel_style)
enemy = other_snakes[0]
@@ -354,6 +373,8 @@ class BestBattleSnake(TemplateSnake):
move_safety:dict[str, dict[str, Any]] = {}
for move, pos in safe_moves.items():
if self._time_exceeded(deadline):
break
point = (pos["x"], pos["y"])
ate_food = point in food_set
@@ -509,18 +530,23 @@ class BestBattleSnake(TemplateSnake):
else:
considered_moves = list(scores.keys())
if not scores:
return random.choice(list(safe_moves.keys())), {}
best_score = max(scores[move] for move in considered_moves)
top_moves = [
move for move in considered_moves if best_score - scores[move] <= 1.5
]
return random.choice(top_moves), scores
def _choose_constrictor_move(self, safe_moves:MoveMap, my_body:list[Coord], my_len:int, other_snakes:list[SnakeState], food_set:set[Point], enemy_attack_map:AttackMap, enemy_heads:list[Point], enemy_can_grow_cache:dict[Any, bool], width:int, height:int) -> tuple[str, dict[str, float]]:
def _choose_constrictor_move(self, safe_moves:MoveMap, my_body:list[Coord], my_len:int, other_snakes:list[SnakeState], food_set:set[Point], enemy_attack_map:AttackMap, enemy_heads:list[Point], enemy_can_grow_cache:dict[Any, bool], width:int, height:int, deadline:float|None=None) -> tuple[str, dict[str, float]]:
"""Score and select a move for constrictor games."""
scores:dict[str, float] = {}
move_safety:dict[str, dict[str, Any]] = {}
for move, pos in safe_moves.items():
if self._time_exceeded(deadline):
break
point = (pos["x"], pos["y"])
future_body = self._future_body(
my_body, pos, ate_food=False, is_constrictor=True
@@ -604,6 +630,9 @@ class BestBattleSnake(TemplateSnake):
else:
considered_moves = list(scores.keys())
if not scores:
return random.choice(list(safe_moves.keys())), {}
best_score = max(scores[move] for move in considered_moves)
top_moves = [
move for move in considered_moves if best_score - scores[move] <= 2.0
@@ -779,6 +808,12 @@ class BestBattleSnake(TemplateSnake):
return False
return point in previous_hazard_set
def _time_exceeded(self, deadline:float|None) -> bool:
"""Return True when the move-calculation time budget is exhausted."""
if deadline is None:
return False
return perf_counter() >= deadline
def _nearest_food_distance(self, start:Point, food_set:set[Point], blocked:set[Point], width:int, height:int) -> int|None:
"""Compute shortest reachable distance to any food using BFS."""
if not food_set: