QCUGVSRDRUJD5TSKWFWQ7LTAXCXUQICTZ3CIA2LUCWZWJWNJD67QC
RKTDDNSGNOB7K27XI6ZBRXHCBOAMAKM667BUQIQSVZ2YJED2YCPQC
FPQHLV6PVRI3KCH3ILEZCT5VZHWO7R7B2VOP22RTSU4YVEXGT6SAC
U3JHTSEMJLXNKOMAQJQPZKRW6GRPHYLG6DK53XVADIETKVCS2MFQC
RSTSRBUU5TMYSKYBYC3APFOPDMCAPUG4ZLJS2TTAH4EBKAPKB5ZQC
BYRGC3FM5PQVL2VDTUYOIDOMIVNMUM6DKD5QUQS2CIIIZPZ2VSPAC
WN26XTZ7IZN4WNAVXZ4NKD4CXIU7IC6V7BFNVICNMQUVHBZXBNCAC
LT2L5OITYEO7I6P5WA35E5FJ5AUGPMLFYYKPVI4WLBIH65AINECQC
KN2H7F5CIEAKA5XQ4NQXAZV25VNZ6AV7Q3M2FVOX2JF7JKMDFDCAC
6X6DDDY32EIHOKTZ2PXAE4G7DJGRC46MCQGWNV2EATJUAHHUDSUAC
UXWFBRYCNZIYOGP6MGMT5KGY6NFS566QDDJJZIP3YJODQI5QIHDAC
FAYXGKV6PUEUYD7BKKV27RKCIJ44WD4L4ZO5ICC5XD5KK5I7X2LQC
O325HSUUTHFZ2BM23FLJVHZKYV742BLUWCDYSYDMIEAZRU6RRO7AC
if (config.backpropObserver != null) {
while (rootNode!.parent != null) {
rootNode = rootNode.parent;
}
config.backpropObserver!(winner, rootNode, currentNode);
}
currentNode.visits += 1;
currentNode = currentNode.parent;
}
}
rewardBackProp(Map<PlayerType, double> rewards) {
Node<MoveType, PlayerType?>? currentNode = this;
Node<MoveType, PlayerType?>? rootNode = this;
while (currentNode != null) {
rewards.forEach((player, reward) {
currentNode?.winsByPlayer
.update(player, (value) => value + reward, ifAbsent: () => reward);
});
var currentPlayerReward = rewards[currentPlayer()]!;
// Q[s][a] = (N[s][a]*Q[s][a] + v)/(N[s][a]+1)
currentNode.q =
(currentNode.visits * currentNode.q + currentPlayerReward) /
(currentNode.visits + 1.0);
PlayerType? winner = null;
if (config.nnpv == null &&
config.useRewards == true &&
gameState is RewardProvider) {
if (currentDepth >= config.useValueAfterDepth!) {
var rewards = (gameState as RewardProvider).rewards();
var sortedRewards = List.from(rewards.values);
sortedRewards.sort();
var highestReward = sortedRewards.last;
for (var player in rewards.keys) {
if (highestReward == rewards[player]) {
return player;
}
}
}
}