ChanceCollapse

dicechess.engine.search.ChanceCollapse
final case class ChanceCollapse(evalBatch: (Array[GameState], Color) => Array[Int], lossGuard: Boolean = ...)

Learned replacement for a root candidate's chance node: instead of expanding the opponent's 56 weighted dice outcomes and their replies, one batched call scores the position each candidate reaches and returns that expectation directly.

This is the search's most expensive layer by orders of magnitude — every candidate pays hundreds to thousands of leaf evaluations for one number — so a model that predicts that number turns a chance node into a single row of a batch. It is off by default and has to be enabled explicitly, because it trades an exact expectation for an estimate: what comes back is what the model thinks the expansion would have produced.

What it keeps: the immediate-king-capture shortcut and the forced pass above it, pre-ranking and its deterministic order, the random tie-break, the deadline's meaning (the batch is indivisible and is not started once the deadline has passed, so the anytime fallback to the pre-ranker's pick is unchanged), and root rescoring.

What it gives up: Star1/Star2 pruning and the transposition table have nothing to prune or store — a collapsed value is a different quantity from the exact expectation the table holds at that depth, so the table is left untouched rather than mixed — and ExpectimaxConfig.searchDepth has no effect, since no tree is built below the root.

It also gives up per-roll knowledge, and with it the one rule RootRescore treats as inviolable: a candidate that loses our king on some roll must never be rescued by a rescorer. lossGuard restores exactly that rule without expanding anything — KingCaptureProbability answers "can the opponent capture our king on their next roll" over the same 56 multisets, exactly (king-capture paths ignore the maximum micro-moves rule, so its depth-first search is not an estimate), at one 216-outcome search per root candidate. Leave it off for a pure collapse; turn it on when a rescorer is configured at a positive weight, or the rescorer can outvote a lost line.

Value parameters

evalBatch

scores the positions the candidates reach, from the root mover's perspective, on the same Int axis as the search's own evalBatch — the value means "the expectation of this chance node", not "the value of this leaf"

lossGuard

whether to re-establish the loss-taint rule described above. It only has an effect while a RootRescore is active at a positive weight — that rule is about what a rescorer may not rescue, and with no rescorer the collapsed value is returned unchanged — so the search skips the work entirely in that case rather than paying a 216-outcome search per candidate for a value nothing reads.

Attributes

Source
ExpectimaxSearch.scala
Graph
Supertypes
trait Serializable
trait Product
trait Equals
class Object
trait Matchable
class Any
Show all

Members list

Value members

Inherited methods

def productElementNames: Iterator[String]

An iterator over the names of all the elements of this product.

An iterator over the names of all the elements of this product.

Attributes

Inherited from:
Product
def productIterator: Iterator[Any]

An iterator over all the elements of this product.

An iterator over all the elements of this product.

Attributes

Returns

in the default implementation, an Iterator[Any]

Inherited from:
Product