TimeManager

dicechess.engine.search.TimeManager
See theTimeManager companion object
final class TimeManager(val policy: TimePolicy)

Pure, platform-agnostic time manager for the bots' game clock.

This is layer (a) of time control: it maps a ClockState to a per-turn budget in milliseconds. Layer (b) — the actual search under a deadline — stays untouched in TimeBudgetedSearch; the caller turns the budget returned here into a System.nanoTime deadline. Keeping the manager here (rather than in each consumer) means the JS API, the offline arena, and any future server share one tested implementation instead of re-inventing it.

The policy is pure Long/Double math — no clocks, no randomness — so it is identical on the JVM and Scala.js and is exhaustively unit-testable without wall-clock flakiness (see TimeBudgetedSearch for why the search path cannot be).

How the budget is derived

From the spendable time (remaining minus a safety reserve) and an estimated moves-to-go:

reserve = max(ReserveFloorMs, ReserveFraction * remaining)
spendable = max(0, remaining - reserve)
target = increment + spendable / movesToGo // increment == 0 ⇒ sudden death

then clamped to [MinThinkMs, hardCap] where hardCap = MaxFraction * remaining, with a tighter panic clamp once spendable falls below TimeManager.PanicThresholdMs.

Behaviour across controls

  • Sudden death (1+0): target ≈ spendable / movesToGo, decaying as the clock drains; near zero the bot blitzes legal moves (panic floor) instead of flagging.
  • Fischer (10+10): the increment term keeps per-turn time high while the clock is healthy. As remaining shrinks the hardCap (a fraction of the remaining clock) takes over and throttles the target below the increment — deliberately conservative, since the increment is only credited after the turn completes, so a bot that overspends now can still flag before it is refunded. This trades a little time-utilisation for a guarantee of never losing on time; the constants are tunable.

Value parameters

policy

the configurable moves-to-go policy; safety limits remain common to every policy

Attributes

Companion
object
Source
TimeManager.scala
Graph
Supertypes
class Object
trait Matchable
class Any

Members list

Value members

Concrete methods

Computes the ideal per-turn TimeBudget for the given clock. Pure; see the class docs for the formula.

Computes the ideal per-turn TimeBudget for the given clock. Pure; see the class docs for the formula.

Attributes

Source
TimeManager.scala
def budgetMs(clock: ClockState, overheadBufferMs: Long): Long

The conservative budget (in ms) the caller should turn into a search deadline.

The conservative budget (in ms) the caller should turn into a search deadline.

Subtracts the caller's transport/granularity overheadBufferMs from the target — the bot cannot interrupt an in-flight rollout, and a worker round-trip adds latency, so the deadline is set short of the real allocation. The buffer is caller knowledge (≈50 ms in-process, ≈150 ms across a Web Worker), hence a parameter rather than a constant. Never returns less than TimeManager.MinThinkMs.

Value parameters

clock

the moving side's clock snapshot

overheadBufferMs

slack subtracted from the target to absorb rollout-overrun and transport latency

Attributes

Source
TimeManager.scala
def movesToGo(clock: ClockState): Double

Estimated number of further turns the moving side must play, used to spread the spendable time.

Estimated number of further turns the moving side must play, used to spread the spendable time.

Honours an explicit ClockState.movesToGo when present (clamped to at least 1 to stay division-safe); otherwise delegates to policy.

Attributes

Source
TimeManager.scala

Concrete fields

Attributes

Source
TimeManager.scala