A small point: in "PlayoutOutTree", just after "if (!played.AlreadyPlayed(move)) {", there should have a "played.Play(move)". I believe it does not change the final result (as the check is also done in the backup, and the move played in the backup), but I simply forgot that line (that should make moves_played_out_tree smaller).
To avoid confusion, I repost the pseudo code with that correction (and hoping the indentation is not broken by the email editor once again). Sylvain class AmafBoard { AmafBoard() { offset = 0 fill(alread_played, 0) } Clear() { offset++; } Play(move) { already_played[move] = offset } AlreadyPlayed(move) { return already_played[move] == offset } vector already_played int offset } ChooseMove(node, board) { bias = 0.015 // I put a random number here, to be tuned b = bias * bias / 0.25 best_value = -1 best_move = PASSMOVE for (move in board.allmoves) { c = node.child(move).counts w = node.child(move).wins rc = node.rave_counts[move] rw = node.rave_wins[move] coefficient = 1 - rc * (rc + c + rc * c * b) value = w / c * coef + rw / rc * (1 - coef) // please here take care of the c==0 and rc == 0 cases if (value > best_value) { best_value = value best_move = move } } return best_move } PlayoutInTree(board, moves_played_in_tree, nodes_seen_in_tree) { node = tree.root while (node) { move = ChooseMove(node, board) moves_played_in_tree.append(move) nodes_seen_in_tree.append(node) board.PlayMove(move) node = node.child(move) } } PlayoutOutTree(board, AmafBoard played, moves) { int pass = 0 while (pass < 2) { move = board.ChooseMoveAndPlay() if (move == PASSMOVE) { pass ++ continue } else { pass = 0 } if (!played.AlreadyPlayed(move)) { played.Play(move) if (!board.last_move_was_black()) { move = -move } moves.append(move) } } return board.black_wins() } BackupNode(node, index, black_wins, moves_played_in_tree, moves_played_out_tree, already_played) { already_played.Clear() win = node.is_black_to_play ? black_wins : !black_wins // normal backup node.wins += win node.counts ++ // rave backup for (j = index; j < moves_played_in_tree.size(); j += 2) { move = moves_played_in_tree[j] if (already_played.AlreadyPlayed(move)) continue already_played.Play(move) node.rave_wins[move] += win node.rave_counts[move] ++ } for (j = 0; j < moves_played_out_tree.size(); ++j) { move = moves_played_out_tree[j] if (!node.is_black_to_play) move = -move // If it is either not the right color or the intersection is // already played we ignore that move for that node if (move < 0 || already_played.AlreadyPlayed(move)) continue already_played.Play(move) node.rave_wins[move] += win node.rave_counts[move] ++ } } Backup(black_wins, nodes_seen_in_tree, moves_played_in_tree, moves_played_out_tree, already_played) { for (i = 0; i < nodes_seen_in_tree.size(); ++i) { node = nodes_seen_in_tree[i] BackupNode(node, i, black_wins, moves_played_in_tree, moves_played_out_tree, already_played) } } OneIteration(board_position, AmafBoard already_played) { board = board_position.Copy() already_played.Clear() vector moves_played_in_tree vector nodes_seen_in_tree PlayoutInTree(&board, &moves_played_in_tree, &nodes_seen_in_tree) vector moves_played_out_tree bool black_wins = PlayoutOutTree(&board, &already_played, &moves_played_out_tree) Backup(black_wins, nodes_seen_in_tree, moves_played_in_tree, moves_played_out_tree, &already_played) } 2009/1/17 Sylvain Gelly <sylvain.ge...@m4x.org>: > Hi, > > Sorry for the slow reply. > After those discussions, I figured out that pseudo code was the > fastest clear and not ambiguous way to describe how to precisely > implement the algorithm. I needed to find some time to write it. > Note that I did not write only the backup phase because to clearly > describe the backup you have to see the playouts as well. I also > describe the choice of the best move. > Note also the fact that the backup from the moves in the tree and from > the moves out of the tree is different. That is the somehow more > tricky part to check that a move has not been already played (that is > different for each node in the tree and we obviously don't want to > keep a vector of already played moves for each node). > > Please forgive the mistakes that are in that code, of course I did not > make any test, and it has been quite a long time I am not in the > computer go trip anymore :). Please correct any mistake, > I hope it makes things clearer. > > Best, > Sylvain > > class AmafBoard { > AmafBoard() { > offset = 0 > fill(alread_played, 0) > } > Clear() { > offset++; > } > Play(move) { > already_played[move] = offset > } > AlreadyPlayed(move) { > return already_played[move] == offset > } > vector already_played > int offset > } > > ChooseMove(node, board) { > bias = 0.015 // I put a random number here, to be tuned > b = bias * bias / 0.25 > best_value = -1 > best_move = PASSMOVE > for (move in board.allmoves) { > c = node.child(move).counts > w = node.child(move).wins > rc = node.rave_counts[move] > rw = node.rave_wins[move] > coefficient = 1 - rc * (rc + c + rc * c * b) > value = w / c * coef + rw / rc * (1 - coef) // please here take > care of the c==0 and rc == 0 cases > if (value > best_value) { > best_value = value > best_move = move > } > } > return best_move > } > PlayoutInTree(board, moves_played_in_tree, nodes_seen_in_tree) { > node = tree.root > while (node) { > move = ChooseMove(node, board) > moves_played_in_tree.append(move) > nodes_seen_in_tree.append(node) > board.PlayMove(move) > node = node.child(move) > } > } > PlayoutOutTree(board, AmafBoard played, moves) { > int pass = 0 > while (pass < 2) { > move = board.ChooseMoveAndPlay() > if (move == PASSMOVE) { > pass ++ > continue > } else { > pass = 0 > } > if (!played.AlreadyPlayed(move)) { > if (!board.last_move_was_black()) { > move = -move > } > moves.append(move) > } > } > return board.black_wins() > } > > BackupNode(node, index, black_wins, moves_played_in_tree, > moves_played_out_tree, already_played) { > already_played.Clear() > win = node.is_black_to_play ? black_wins : !black_wins > // normal backup > node.wins += win > node.counts ++ > // rave backup > for (j = index; j < moves_played_in_tree.size(); j += 2) { > move = moves_played_in_tree[j] > if (already_played.AlreadyPlayed(move)) continue > already_played.Play(move) > node.rave_wins[move] += win > node.rave_counts[move] ++ > } > for (j = 0; j < moves_played_out_tree.size(); ++j) { > move = moves_played_out_tree[j] > if (!node.is_black_to_play) move = -move > // If it is either not the right color or the intersection is > already played we ignore that move for that node > if (move < 0 || already_played.AlreadyPlayed(move)) continue > > already_played.Play(move) > node.rave_wins[move] += win > node.rave_counts[move] ++ > } > } > > Backup(black_wins, nodes_seen_in_tree, moves_played_in_tree, > moves_played_out_tree, already_played) { > for (i = 0; i < nodes_seen_in_tree.size(); ++i) { > node = nodes_seen_in_tree[i] > BackupNode(node, i, black_wins, moves_played_in_tree, > moves_played_out_tree, already_played) > } > } > OneIteration(board_position, AmafBoard already_played) { > board = board_position.Copy() > already_played.Clear() > > vector moves_played_in_tree > vector nodes_seen_in_tree > PlayoutInTree(&board, &moves_played_in_tree, &nodes_seen_in_tree) > > vector moves_played_out_tree > bool black_wins = PlayoutOutTree(&board, &already_played, > &moves_played_out_tree) > Backup(black_wins, nodes_seen_in_tree, moves_played_in_tree, > moves_played_out_tree, &already_played) > } >
_______________________________________________ computer-go mailing list computer-go@computer-go.org http://www.computer-go.org/mailman/listinfo/computer-go/