| """ |
| 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 |
| |
| 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_reserve = min(2.0, time_left * 0.1) |
| available_time = max(0, time_left - emergency_reserve - self.move_overhead) |
| |
| |
| if moves_to_go: |
| expected_moves = moves_to_go |
| else: |
| |
| expected_moves = max(20, 40 - move_number // 2) |
| |
| |
| base_time = available_time / expected_moves |
| increment_bonus = increment * 0.8 |
| |
| |
| soft_limit = base_time + increment_bonus |
| |
| |
| hard_limit = min( |
| soft_limit * 3.0, |
| available_time * 0.5 |
| ) |
| |
| |
| soft_limit = max(soft_limit, 0.1) |
| 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 |
| |
| |
| if elapsed >= self.hard_limit: |
| return True |
| |
| |
| 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()) |