BitBully 0.0.39
Loading...
Searching...
No Matches
Position.hpp
1/*
2 * This file is part of Connect4 Game Solver <http://connect4.gamesolver.org>
3 * Copyright (C) 2017-2019 Pascal Pons <contact@gamesolver.org>
4 *
5 * Connect4 Game Solver is free software: you can redistribute it and/or
6 * modify it under the terms of the GNU Affero General Public License as
7 * published by the Free Software Foundation, either version 3 of the
8 * License, or (at your option) any later version.
9 *
10 * Connect4 Game Solver is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU Affero General Public License for more details.
14 *
15 * You should have received a copy of the GNU Affero General Public License
16 * along with Connect4 Game Solver. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19#ifndef POSITION_HPP
20#define POSITION_HPP
21
22#include <cassert>
23#include <cstdint>
24#include <string>
25
26namespace GameSolver {
27namespace Connect4 {
77
83
84class Position {
85public:
86 static constexpr int WIDTH = 7; // width of the board
87 static constexpr int HEIGHT = 6; // height of the board
88
89#ifdef __GNUC__
90 // Board size is 64bits or 128 bits depending on WIDTH and HEIGHT
91 using position_t = typename std::conditional<WIDTH *(HEIGHT + 1) <= 64,
92 uint64_t, __int128>::type;
93#else
94 // __int128 is a g++ non portable type. Use the following line limited to
95 // 64bits board for C++ compatibility
96 using position_t = uint64_t;
97#endif
98
99 static constexpr int MIN_SCORE = -(WIDTH * HEIGHT) / 2 + 3;
100 static constexpr int MAX_SCORE = (WIDTH * HEIGHT + 1) / 2 - 3;
101
102 static_assert(WIDTH < 10, "Board's width must be less than 10");
103 static_assert(WIDTH * (HEIGHT + 1) <= sizeof(position_t) * 8,
104 "Board does not fit into position_t bitmask");
105
113 void play(position_t move) {
114 current_position ^= mask;
115 mask |= move;
116 moves++;
117 }
118
119 /*
120 * Plays a sequence of successive played columns, mainly used to initilize a
121 * board.
122 * @param seq: a sequence of digits corresponding to the 1-based index of the
123 * column played.
124 *
125 * @return number of played moves. Processing will stop at first invalid move
126 * that can be:
127 * - invalid character (non digit, or digit >= WIDTH)
128 * - playing a colum the is already full
129 * - playing a column that makes an alignment (we only solve non).
130 * Caller can check if the move sequence was valid by comparing the
131 * number of processed moves to the length of the sequence.
132 */
133 unsigned int play(const std::string &seq) {
134 for (unsigned int i = 0; i < seq.size(); i++) {
135 int col = seq[i] - '1';
136 if (col < 0 || col >= Position::WIDTH || !canPlay(col) ||
137 isWinningMove(col))
138 return i; // invalid move
139 playCol(col);
140 }
141 return seq.size();
142 }
143
147 bool canWinNext() const { return winning_position() & possible(); }
148
152 int nbMoves() const { return moves; }
153
157 position_t key() const { return current_position + mask; }
158
176 uint64_t key3() const {
177 uint64_t key_forward = 0;
178 for (int i = 0; i < Position::WIDTH; i++)
179 partialKey3(key_forward, i); // compute key in increasing order of columns
180
181 uint64_t key_reverse = 0;
182 for (int i = Position::WIDTH; i--;)
183 partialKey3(key_reverse, i); // compute key in decreasing order of columns
184
185 return key_forward < key_reverse
186 ? key_forward / 3
187 : key_reverse / 3; // take the smallest key and divide per 3 as
188 // the last base3 digit is always 0
189 }
190
200 position_t possibleNonLosingMoves() const {
201 assert(!canWinNext());
202 position_t possible_mask = possible();
203 position_t opponent_win = opponent_winning_position();
204 position_t forced_moves = possible_mask & opponent_win;
205 if (forced_moves) {
206 if (forced_moves &
207 (forced_moves - 1)) // check if there is more than one forced move
208 return 0; // the opponnent has two winning moves and you cannot stop him
209 else
210 possible_mask = forced_moves; // enforce to play the single forced move
211 }
212 return possible_mask &
213 ~(opponent_win >> 1); // avoid to play below an opponent winning spot
214 }
215
224 int moveScore(position_t move) const {
225 return popcount(compute_winning_position(current_position | move, mask));
226 }
227
231 Position() : current_position{0}, mask{0}, moves{0} {}
232
239 bool canPlay(int col) const { return (mask & top_mask_col(col)) == 0; }
240
248 void playCol(int col) {
249 play((mask + bottom_mask_col(col)) & column_mask(col));
250 }
251
259 bool isWinningMove(int col) const {
260 return winning_position() & possible() & column_mask(col);
261 }
262
263private:
264 position_t current_position; // bitmap of the current_player stones
265 position_t mask; // bitmap of all the already palyed spots
266 unsigned int moves; // number of moves played since the beinning of the game.
267
271 void partialKey3(uint64_t &key, int col) const {
272 for (position_t pos = UINT64_C(1) << (col * (Position::HEIGHT + 1));
273 pos & mask; pos <<= 1) {
274 key *= 3;
275 if (pos & current_position)
276 key += 1;
277 else
278 key += 2;
279 }
280 key *= 3;
281 }
282
286 position_t winning_position() const {
287 return compute_winning_position(current_position, mask);
288 }
289
293 position_t opponent_winning_position() const {
294 return compute_winning_position(current_position ^ mask, mask);
295 }
296
301 position_t possible() const { return (mask + bottom_mask) & board_mask; }
302
306 static unsigned int popcount(position_t m) {
307 unsigned int c = 0;
308 for (c = 0; m; c++)
309 m &= m - 1;
310 return c;
311 }
312
319 static position_t compute_winning_position(position_t position,
320 position_t mask) {
321 // vertical;
322 position_t r = (position << 1) & (position << 2) & (position << 3);
323
324 // horizontal
325 position_t p = (position << (HEIGHT + 1)) & (position << 2 * (HEIGHT + 1));
326 r |= p & (position << 3 * (HEIGHT + 1));
327 r |= p & (position >> (HEIGHT + 1));
328 p = (position >> (HEIGHT + 1)) & (position >> 2 * (HEIGHT + 1));
329 r |= p & (position << (HEIGHT + 1));
330 r |= p & (position >> 3 * (HEIGHT + 1));
331
332 // diagonal 1
333 p = (position << HEIGHT) & (position << 2 * HEIGHT);
334 r |= p & (position << 3 * HEIGHT);
335 r |= p & (position >> HEIGHT);
336 p = (position >> HEIGHT) & (position >> 2 * HEIGHT);
337 r |= p & (position << HEIGHT);
338 r |= p & (position >> 3 * HEIGHT);
339
340 // diagonal 2
341 p = (position << (HEIGHT + 2)) & (position << 2 * (HEIGHT + 2));
342 r |= p & (position << 3 * (HEIGHT + 2));
343 r |= p & (position >> (HEIGHT + 2));
344 p = (position >> (HEIGHT + 2)) & (position >> 2 * (HEIGHT + 2));
345 r |= p & (position << (HEIGHT + 2));
346 r |= p & (position >> 3 * (HEIGHT + 2));
347
348 return r & (board_mask ^ mask);
349 }
350
351 // Static bitmaps
352 template <int width, int height> struct bottom {
353 static constexpr position_t mask =
354 bottom<width - 1, height>::mask | position_t(1)
355 << (width - 1) * (height + 1);
356 };
357 template <int height> struct bottom<0, height> {
358 static constexpr position_t mask = 0;
359 };
360
361 static constexpr position_t bottom_mask = bottom<WIDTH, HEIGHT>::mask;
362 static constexpr position_t board_mask = bottom_mask * ((1LL << HEIGHT) - 1);
363
364 // return a bitmask containg a single 1 corresponding to the top cel of a
365 // given column
366 static constexpr position_t top_mask_col(int col) {
367 return UINT64_C(1) << ((HEIGHT - 1) + col * (HEIGHT + 1));
368 }
369
370 // return a bitmask containg a single 1 corresponding to the bottom cell of a
371 // given column
372 static constexpr position_t bottom_mask_col(int col) {
373 return UINT64_C(1) << col * (HEIGHT + 1);
374 }
375
376public:
377 // return a bitmask 1 on all the cells of a given column
378 static constexpr position_t column_mask(int col) {
379 return ((UINT64_C(1) << HEIGHT) - 1) << col * (HEIGHT + 1);
380 }
381};
382
383} // namespace Connect4
384} // namespace GameSolver
385#endif
bool canPlay(int col) const
Definition Position.hpp:239
bool isWinningMove(int col) const
Definition Position.hpp:259
void play(position_t move)
Definition Position.hpp:113
position_t possibleNonLosingMoves() const
Definition Position.hpp:200
int moveScore(position_t move) const
Definition Position.hpp:224