Rafs-an09002's picture
Create engine/time_manager.py
5950ee5 verified
"""
Adaptive Time Management
Research: Stockfish time management algorithm
Key Concepts:
- Time allocation based on game phase
- Emergency time reserve
- Increment handling
- Move overhead compensation
"""
import time
from typing import Optional
class TimeManager:
"""
Smart time allocation for searches
Adapts based on position complexity and game phase
"""
def __init__(self):
self.start_time = 0.0
self.allocated_time = 0.0
self.hard_limit = 0.0
self.move_overhead = 0.050 # 50ms network/processing overhead
def allocate_time(
self,
time_left: float,
increment: float = 0.0,
moves_to_go: Optional[int] = None,
move_number: int = 1
) -> tuple[float, float]:
"""
Calculate time allocation for this move
Args:
time_left: Remaining time in seconds
increment: Time increment per move
moves_to_go: Expected moves until time control
move_number: Current move number
Returns:
(soft_limit, hard_limit) in seconds
"""
# Emergency time reserve (never go below 2 seconds)
emergency_reserve = min(2.0, time_left * 0.1)
available_time = max(0, time_left - emergency_reserve - self.move_overhead)
# Estimate moves remaining
if moves_to_go:
expected_moves = moves_to_go
else:
# Typical game length: 40 moves per side
expected_moves = max(20, 40 - move_number // 2)
# Base allocation with increment
base_time = available_time / expected_moves
increment_bonus = increment * 0.8 # Use 80% of increment
# Soft limit (normal search time)
soft_limit = base_time + increment_bonus
# Hard limit (absolute maximum, emergency situations)
hard_limit = min(
soft_limit * 3.0, # Up to 3x soft limit
available_time * 0.5 # But never more than 50% of remaining time
)
# Ensure minimums
soft_limit = max(soft_limit, 0.1) # At least 100ms
hard_limit = max(hard_limit, soft_limit)
return soft_limit, hard_limit
def start_search(self, allocated_time: float, hard_limit: float):
"""Initialize search timer"""
self.start_time = time.time()
self.allocated_time = allocated_time
self.hard_limit = hard_limit
def should_stop(self, depth: int, best_move_stable: bool = False) -> bool:
"""
Check if search should stop
Args:
depth: Current search depth
best_move_stable: True if best move hasn't changed recently
Returns:
True if should stop search
"""
elapsed = time.time() - self.start_time
# Hard limit exceeded
if elapsed >= self.hard_limit:
return True
# Soft limit exceeded with stable best move
if elapsed >= self.allocated_time:
if best_move_stable or depth >= 4:
return True
return False
def elapsed(self) -> float:
"""Get elapsed search time"""
return time.time() - self.start_time
def remaining(self) -> float:
"""Get remaining time in soft limit"""
return max(0, self.allocated_time - self.elapsed())