43 #include <dumbo/core/game_state.h>
44 #include <dumbo/tic_tac_toe/board.h>
45 #include <dumbo/tic_tac_toe/square.h>
47 #include <glog/logging.h>
51 #include <unordered_set>
57 const std::vector<Board::SquareTuple> Board::winning_sets_ = {
59 std::make_tuple(Square(0, 0,
true), Square(0, 1,
true), Square(0, 2,
true)),
60 std::make_tuple(Square(1, 0,
true), Square(1, 1,
true), Square(1, 2,
true)),
61 std::make_tuple(Square(2, 0,
true), Square(2, 1,
true), Square(2, 2,
true)),
64 std::make_tuple(Square(0, 0,
true), Square(1, 0,
true), Square(2, 0,
true)),
65 std::make_tuple(Square(0, 1,
true), Square(1, 1,
true), Square(2, 1,
true)),
66 std::make_tuple(Square(0, 2,
true), Square(1, 2,
true), Square(2, 2,
true)),
69 std::make_tuple(Square(0, 0,
true), Square(1, 1,
true), Square(2, 2,
true)),
70 std::make_tuple(Square(0, 2,
true), Square(1, 1,
true),
73 const std::vector<Board::SquareTuple> Board::losing_sets_ = {
75 std::make_tuple(Square(0, 0,
false), Square(0, 1,
false),
77 std::make_tuple(Square(1, 0,
false), Square(1, 1,
false),
79 std::make_tuple(Square(2, 0,
false), Square(2, 1,
false),
83 std::make_tuple(Square(0, 0,
false), Square(1, 0,
false),
85 std::make_tuple(Square(0, 1,
false), Square(1, 1,
false),
87 std::make_tuple(Square(0, 2,
false), Square(1, 2,
false),
91 std::make_tuple(Square(0, 0,
false), Square(1, 1,
false),
93 std::make_tuple(Square(0, 2,
false), Square(1, 1,
false),
94 Square(2, 0,
false))};
96 Board::Board(
const std::unordered_set<Square, Square::Hasher>& occupied_squares,
98 : GameState<Square>(my_turn), occupied_squares_(occupied_squares) {
100 constexpr uint8_t kGridSize = 3;
101 for (
const auto& sq : occupied_squares_)
102 CHECK(sq.row < kGridSize && sq.col < kGridSize);
105 for (uint8_t ii = 0; ii < kGridSize; ii++) {
106 for (uint8_t jj = 0; jj < kGridSize; jj++) {
107 const Square sq{ii, jj, my_turn_};
108 const Square sq_other_player{ii, jj, !my_turn_};
110 if (!occupied_squares_.count(sq) &&
111 !occupied_squares_.count(sq_other_player))
112 empty_squares_.emplace_back(sq);
121 Square Board::RandomMove()
const {
122 CHECK(!empty_squares_.empty());
123 std::uniform_int_distribution<size_t> unif(0, empty_squares_.size() - 1);
124 return empty_squares_[unif(rng_)];
129 bool Board::NextState(
const Square& move, GameState* next_state)
const {
130 CHECK_NOTNULL(next_state);
131 CHECK_EQ(move.my_square, my_turn_);
134 Square move_other_player = move;
135 move_other_player.my_square = !move.my_square;
136 if (occupied_squares_.count(move) ||
137 occupied_squares_.count(move_other_player))
141 Board* next_board =
static_cast<Board*
>(next_state);
142 CHECK_NOTNULL(next_board);
145 next_board->my_turn_ = !my_turn_;
148 next_board->occupied_squares_.clear();
149 next_board->occupied_squares_.insert(occupied_squares_.begin(),
150 occupied_squares_.end());
151 next_board->occupied_squares_.emplace(move);
154 next_board->empty_squares_.clear();
155 bool found_matching_empty =
false;
156 for (
const auto& empty : empty_squares_) {
158 found_matching_empty =
true;
162 next_board->empty_squares_.emplace_back(empty);
163 next_board->empty_squares_.back().my_square = next_board->my_turn_;
166 CHECK(found_matching_empty);
167 CHECK_EQ(next_board->empty_squares_.size(), empty_squares_.size() - 1);
177 bool Board::IsTerminal(
double* win)
const {
181 auto has_set = [
this](
const SquareTuple& s) {
182 return this->occupied_squares_.count(std::get<0>(s)) &&
183 this->occupied_squares_.count(std::get<1>(s)) &&
184 this->occupied_squares_.count(std::get<2>(s));
188 for (
const auto& ws : winning_sets_) {
196 for (
const auto& ls : losing_sets_) {
204 if (empty_squares_.empty()) {
213 bool Board::operator==(
const GameState<Square>& rhs)
const {
214 const Board& rhs_board =
static_cast<const Board&
>(rhs);
218 for (
const auto& sq : rhs_board.occupied_squares_) {
219 if (!occupied_squares_.count(sq))
return false;
226 void Board::Render()
const {
227 constexpr uint8_t kNumRows = 3;
229 Square sq{0, 0,
true};
230 for (uint8_t ii = 0; ii < kNumRows; ii++) {
233 for (uint8_t jj = 0; jj < kNumRows; jj++) {
236 if (occupied_squares_.count(sq))
239 sq.my_square =
false;
240 if (occupied_squares_.count(sq))
247 std::cout << std::endl;
252 void Board::Check()
const {
254 constexpr
size_t kNumRows = 3;
255 CHECK_EQ(occupied_squares_.size() + empty_squares_.size(),
256 kNumRows * kNumRows);
259 auto is_occupied = [
this](uint8_t ii, uint8_t jj) {
260 Square sq{ii, jj,
true};
261 if (this->occupied_squares_.count(sq))
return true;
263 sq.my_square =
false;
264 return this->occupied_squares_.count(sq) > 0;
267 auto is_empty = [
this](uint8_t ii, uint8_t jj) {
268 const Square sq{ii, jj, this->my_turn_};
269 for (
const auto& empty : this->empty_squares_) {
270 if (empty == sq)
return true;
276 for (uint8_t ii = 0; ii < 3; ii++) {
277 for (uint8_t jj = 0; jj < 3; jj++) {
278 if (!is_occupied(ii, jj)) CHECK(is_empty(ii, jj));
283 for (
const auto& empty : empty_squares_) CHECK_EQ(empty.my_square, my_turn_);