pub fn current_player(bb: &Bitboard) -> Option<u8>Expand description
Whose turn is it? Player 0 moves first; after that they alternate.
Returns None if the piece counts are inconsistent.
Examples found in repository?
examples/selfplay_export.rs (line 84)
71fn play_game(seed: u64, iterations: u32) -> (Vec<PendingRow>, WinStatus) {
72 let mut bb = Bitboard::EMPTY;
73 let mut rows = Vec::new();
74 let mut ply = 0u32;
75
76 loop {
77 if has_winning_line(&bb) {
78 return (rows, check_winner(&bb));
79 }
80 let legal = generate_legal_moves(&bb);
81 if legal.is_empty() {
82 // No legal moves: the side to move loses (see Global
83 // Constraints — this is a decisive result, never a draw).
84 let loser = current_player(&bb).unwrap();
85 let winner = if loser == 0 {
86 WinStatus::Player1Wins
87 } else {
88 WinStatus::Player0Wins
89 };
90 return (rows, winner);
91 }
92
93 let side_to_move = current_player(&bb).unwrap();
94 // use_transposition_table MUST be false here: with it on (the
95 // engine's actual default), root moves that canonicalize to the
96 // same child are merged onto one shared node and reported under a
97 // single arbitrary move, silently dropping every other legal move
98 // that led there — worst exactly at shallow plies (the empty
99 // board's 64 legal moves collapse to 3), which every self-play
100 // game passes through. Verified in mcts.rs's
101 // root_move_visits_default_config_collapses_symmetric_root_moves
102 // test — this is not a hypothetical concern, it was caught by
103 // Opus review of the PR that added root_move_visits and is
104 // exactly what this exporter must avoid to produce a faithful
105 // per-legal-move policy target.
106 let mut engine = MCTSEngine::new(MCTSConfig {
107 max_iterations: iterations,
108 seed: Some(seed.wrapping_add(ply as u64)),
109 use_transposition_table: false,
110 ..Default::default()
111 });
112 let (best_move, _) = engine.search(&bb).expect("legal moves exist");
113 let policy: Vec<(u8, u8, u32)> = engine
114 .root_move_visits()
115 .into_iter()
116 .map(|(mv, visits)| (mv.shape, mv.position, visits))
117 .collect();
118
119 rows.push(PendingRow {
120 ply,
121 qfen: State::new(bb).to_qfen(),
122 side_to_move,
123 policy,
124 });
125
126 bb = apply_move(&bb, &best_move);
127 ply += 1;
128 }
129}