See also minimax with α/β pruning.
The term “principal variation search” is something of a misnomer because
We’ll be using the notation described on the page for α/β minimax and extending it slightly.
A node can be an inhabitant of multiple search trees in the sense that, if node
n
is an inhabitant of tree
S
and
S
is a subtree of tree
T, then
n
is an inhabitant of
T
as well.
(In other words, a tree is merely a collection of nodes with a particular propertynode.tree
to refer to the
U.root
to refer to the node that contains no predecessors
But besides thissearch(node.position, −1, 0)search(node.position, −1, +1)X.root
nor
Y.root
are
node;
the three are different, even though
X.root.position,
Y.root.position, and
node.position
are all identical.
The proof that the search of a successor will fail low
node.succ [ k ] .ret <node.alpha [ k ]
is established by a
zero-window search, which has the property of failing low exactly when the search of
node., a
full-window search, will fail low.
node. = node., and knowing node. allows the search of node. to begin.
The root of this zero-window search is
Z(node., defined like so:
Z(node.succ [ k ] ).positionnode.succ [ k ] .positionZ(node.succ [ k ] ).alpha [ 0 ] node.alpha [ k ] Z(node.succ [ k ] ).beta node.alpha [ k ] Recall that
node.succ [ k ] .alpha [ 0 ] node.beta node.succ [ k ] .beta node.alpha [ k ]
so for the zero-window search, we’ve effectively lowered
node.
to
node..
ret ≥ beta ret > beta node.beta
is effectively lowered to
node.alpha [ k ] + εnode.alpha [ k ] , where
ε
is the smallest nonzero difference in evaluation, and so you will almost exclusively see
Z(node.succ [ k ] ).alpha [ 0 ] node.alpha [ k ] − εThe property above can be stated more precisely as
Z(node.succ [ k ] ).ret <node.alpha [ k ] node.succ [ k ] .ret <node.alpha [ k ]
Typically, the same routine is used for full-window and zero-window searches, and this requires only a single bit of bookkeeping.
Zero-window pruning is not applicable in a zero-window search because full-window and zero-window searches are equivalent in zero-window trees.
Typically, zero-window pruning is not attempted for the first successor of a node because the first successor is expected to meet or exceed alpha, in which case the proof will fail and the full-window search will have to be performed anyway. Since the zero-window search is expected to be useless, implementations simply begin the full-window search right away.
Z(node.succ [ 1 ] )
is expected to be useless (just as a search of
Z(node.succ [ 0 ] )
was expected to be useless), which suggests that an implementation oughtn’t attempt zero-window pruning yet but instead simply begin the (full-window) search of
node.succ [ 1 ]
right away (and so on until a successor meets or exceeds alpha). As far as I’m aware, however, this does not improve playing strength in practice.