MoKy

Cloister.cpp

May 22nd, 2025
12
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. #include <bits/stdc++.h>
  2. using namespace std;
  3.  
  4. // Constants
  5. const int MAX_VALUE = 10000;
  6. const int DEPTH = 13;
  7. const int N = 4; // 4x4 board size
  8.  
  9. // Player constants
  10. const int WHITE = 0;
  11. const int BLACK = 1;
  12.  
  13. // Function for adaptive DELTA calculation based on current depth
  14. inline int getDelta(int depth) {
  15. return (depth * depth); // Faster increase for high depths
  16. }
  17.  
  18. // Precomputed move directions for each index
  19. // Direction 0: up, 1: down, 2: left, 3: right
  20. const int MOVES[16][4] = {
  21. {-1, 4, -1, 1}, // 0 (a1)
  22. {-1, 5, 0, 2}, // 1 (a2)
  23. {-1, 6, 1, 3}, // 2 (a3)
  24. {-1, 7, 2, -1}, // 3 (a4)
  25.  
  26. {0, 8, -1, 5}, // 4 (b1)
  27. {1, 9, 4, 6}, // 5 (b2)
  28. {2, 10, 5, 7}, // 6 (b3)
  29. {3, 11, 6, -1}, // 7 (b4)
  30.  
  31. {4, 12, -1, 9}, // 8 (c1)
  32. {5, 13, 8, 10}, // 9 (c2)
  33. {6, 14, 9, 11}, // 10 (c3)
  34. {7, 15, 10, -1}, // 11 (c4)
  35.  
  36. {8, -1, -1, 13}, // 12 (d1)
  37. {9, -1, 12, 14}, // 13 (d2)
  38. {10, -1, 13, 15}, // 14 (d3)
  39. {11, -1, 14, -1} // 15 (d4)
  40. };
  41.  
  42. // Precomputed masks for shift limits
  43. const uint16_t NOT_A_FILE = 0xEEEE; // Exclude column A (0, 4, 8, 12)
  44. const uint16_t NOT_H_FILE = 0x7777; // Exclude column H (3, 7, 11, 15)
  45. const uint16_t NOT_1_RANK = 0xFFF0; // Exclude row 1 (0-3)
  46. const uint16_t NOT_8_RANK = 0x0FFF; // Exclude row 8 (12-15)
  47.  
  48. // Structure for moves
  49. struct Move {
  50. bool drop;
  51. int src, dst; // Square indices (0-15) instead of coordinates
  52.  
  53. // Constructor for easier move creation
  54. Move(bool is_drop = false, int source = -1, int dest = -1)
  55. : drop(is_drop), src(source), dst(dest) {}
  56. };
  57.  
  58. // Optimized game representation with 16-bit bitboards
  59. struct Game {
  60. // Bitboards - each represents one type of square (16 bits, one for each square)
  61. uint16_t empty_board; // 1 at positions where the square is empty
  62. uint16_t piece_boards[2]; // piece_boards[WHITE] = white pieces, piece_boards[BLACK] = black pieces
  63. uint16_t wall_board; // 1 at positions with walls
  64.  
  65. // Other data
  66. uint8_t hand[2]; // hand[WHITE] = white pieces in hand, hand[BLACK] = black pieces
  67. uint8_t count[2]; // count[WHITE] = white piece count, count[BLACK] = black piece count
  68. uint8_t side; // WHITE = white to move, BLACK = black to move
  69.  
  70. // Hash value for quick comparison in transposition table
  71. uint64_t hash;
  72.  
  73. // Convert index (0-15) to coordinates (a1, a2, ..., d4) - for display only
  74. inline pair<char, int> indexToCoord(int idx) const {
  75. return {static_cast<char>('a' + (idx % N)), (idx / N) + 1};
  76. }
  77.  
  78. // Set bit in given bitboard at given index
  79. inline void setBit(uint16_t &board, int idx) {
  80. board |= (1u << idx);
  81. }
  82.  
  83. // Clear bit in given bitboard at given index
  84. inline void clearBit(uint16_t &board, int idx) {
  85. board &= ~(1u << idx);
  86. }
  87.  
  88. // Check if bit is set
  89. inline bool isBitSet(uint16_t board, int idx) const {
  90. return (board & (1u << idx)) != 0;
  91. }
  92.  
  93. // Set only one bitboard at given index (clear others)
  94. inline void setOnlyOneBitboard(int idx, uint16_t &target_board) {
  95. uint16_t mask = 1u << idx;
  96. empty_board &= ~mask;
  97. piece_boards[WHITE] &= ~mask;
  98. piece_boards[BLACK] &= ~mask;
  99. wall_board &= ~mask;
  100. target_board |= mask;
  101. }
  102.  
  103. // Get opponent side
  104. inline int opponent() const {
  105. return 1 - side;
  106. }
  107.  
  108. // Initialize game
  109. void reset() {
  110. hand[WHITE] = hand[BLACK] = 0;
  111. count[WHITE] = count[BLACK] = 8;
  112. side = WHITE; // White starts
  113.  
  114. // Initialize bitboards
  115. empty_board = 0;
  116. piece_boards[WHITE] = 0;
  117. piece_boards[BLACK] = 0;
  118. wall_board = 0;
  119.  
  120. // Set up the board - alternating black and white squares
  121. for (int idx = 0; idx < 16; idx++) {
  122. int row = idx / N;
  123. int col = idx % N;
  124. if ((row + col) % 2 == 0) {
  125. setBit(piece_boards[BLACK], idx);
  126. } else {
  127. setBit(piece_boards[WHITE], idx);
  128. }
  129. }
  130.  
  131. // Set hash value
  132. updateHash();
  133. }
  134.  
  135. // Update hash value
  136. inline void updateHash() {
  137. hash = 0;
  138. hash ^= empty_board;
  139. hash ^= ((uint64_t)piece_boards[WHITE] << 16);
  140. hash ^= ((uint64_t)piece_boards[BLACK] << 32);
  141. hash ^= ((uint64_t)wall_board << 48);
  142. hash ^= ((uint64_t)hand[WHITE] << 56);
  143. hash ^= ((uint64_t)hand[BLACK] << 58);
  144. hash ^= ((uint64_t)side << 60);
  145. }
  146.  
  147. // Enhanced evaluation function
  148. int eval() const {
  149. int opp = opponent();
  150.  
  151. // Basic components
  152. int empty = hand[WHITE] + hand[BLACK];
  153. int piece_diff = count[WHITE] - count[BLACK];
  154. int hand_diff = hand[WHITE] - hand[BLACK];
  155.  
  156. // Functions for bitboard shifts
  157. auto shift_up = [](uint16_t b) { return (b & NOT_1_RANK) >> 4; };
  158. auto shift_down = [](uint16_t b) { return (b & NOT_8_RANK) << 4; };
  159. auto shift_left = [](uint16_t b) { return (b & NOT_A_FILE) >> 1; };
  160. auto shift_right = [](uint16_t b) { return (b & NOT_H_FILE) << 1; };
  161.  
  162. // Calculate target squares for each player's pieces
  163. uint16_t targets[2];
  164.  
  165. for (int player = 0; player < 2; player++) {
  166. targets[player] =
  167. (shift_up(piece_boards[player]) |
  168. shift_down(piece_boards[player]) |
  169. shift_left(piece_boards[player]) |
  170. shift_right(piece_boards[player])) & ~wall_board;
  171. }
  172.  
  173. // Count bits in target squares = number of possible moves
  174. int mobility[2];
  175. mobility[WHITE] = __builtin_popcount(targets[WHITE]);
  176. mobility[BLACK] = __builtin_popcount(targets[BLACK]);
  177. int mobility_diff = mobility[WHITE] - mobility[BLACK];
  178.  
  179. // Final position score with weights for different components
  180. int score = 31 * mobility_diff + 25 * hand_diff + 19 * empty + 13 * piece_diff + 7;
  181. return score * (side == WHITE ? 1 : -1);
  182. }
  183.  
  184. // Optimized move implementation with bitboards
  185. void do_move(const Move &m) {
  186. int me = side, opp = opponent();
  187.  
  188. if (m.drop) {
  189. // Placing a piece from hand
  190. uint16_t dst_mask = 1u << m.dst;
  191. empty_board &= ~dst_mask;
  192.  
  193. piece_boards[me] |= dst_mask;
  194. hand[me]--;
  195. count[me]++;
  196. } else {
  197. // Moving a piece
  198. uint16_t src_mask = 1u << m.src;
  199. uint16_t dst_mask = 1u << m.dst;
  200.  
  201. // Remove original piece
  202. piece_boards[me] &= ~src_mask;
  203.  
  204. // Set empty square at original position
  205. empty_board |= src_mask;
  206.  
  207. // Process target square
  208. if (empty_board & dst_mask) {
  209. // Target square is empty
  210. empty_board &= ~dst_mask;
  211. piece_boards[me] |= dst_mask;
  212. } else {
  213. // Set wall and adjust piece counts
  214. if (piece_boards[me] & dst_mask) {
  215. // Piece collided with own piece
  216. count[me] -= 2;
  217. } else {
  218. // Piece collided with opponent's piece
  219. count[me]--;
  220. count[opp]--;
  221. }
  222.  
  223. // Clear all bitboards at target position
  224. empty_board &= ~dst_mask;
  225. piece_boards[WHITE] &= ~dst_mask;
  226. piece_boards[BLACK] &= ~dst_mask;
  227.  
  228. // Set wall
  229. wall_board |= dst_mask;
  230. hand[me]++;
  231. }
  232. }
  233.  
  234. side = opp; // Change side to move
  235. updateHash(); // Update hash value
  236. }
  237.  
  238. // Compare two game states - optimized using hash
  239. bool equals(const Game &other) const {
  240. return hash == other.hash;
  241. }
  242.  
  243. // For debugging - display board state
  244. void print() const {
  245. cout << "Side: " << (side == WHITE ? "WHITE" : "BLACK") << endl;
  246. cout << "Hand: W=" << (int)hand[WHITE] << ", B=" << (int)hand[BLACK] << endl;
  247. cout << "Count: W=" << (int)count[WHITE] << ", B=" << (int)count[BLACK] << endl;
  248. cout << "Board:" << endl;
  249.  
  250. cout << " a b c d" << endl;
  251. cout << " +---+---+---+---+" << endl;
  252. // Prechod riadkami od 3 po 0 (štvrtý riadok ako prvý)
  253. for (int row = N-1; row >= 0; row--) {
  254. cout << row+1 << " |";
  255. for (int col = 0; col < N; col++) {
  256. int idx = row * N + col;
  257. uint16_t mask = 1u << idx;
  258. if (piece_boards[WHITE] & mask) cout << " W |";
  259. else if (piece_boards[BLACK] & mask) cout << " B |";
  260. else if (empty_board & mask) cout << " |";
  261. else if (wall_board & mask) cout << " X |";
  262. else cout << "?|"; // Error - no bitboard is set
  263. }
  264. cout << " " << row+1 << endl;
  265. cout << " +---+---+---+---+" << endl;
  266. }
  267. cout << " a b c d" << endl;
  268. }
  269. };
  270.  
  271. // Enum for transposition table entry type
  272. enum TTFlag {
  273. TT_EXACT = 0,
  274. TT_UPPER = 1,
  275. TT_LOWER = 2
  276. };
  277.  
  278. // Optimized transposition table with hash collision handling
  279. struct TTEntry {
  280. uint64_t hash;
  281. int16_t depth;
  282. int16_t score;
  283. uint8_t flag; // 0 = exact value, 1 = upper bound, 2 = lower bound
  284. Move best_move;
  285. };
  286.  
  287. // Increased transposition table size and using mask instead of modulo
  288. const int TT_BITS = 28; // 2^28 = 16777216 entries
  289. const int TT_SIZE = 1 << TT_BITS;
  290. const int TT_MASK = TT_SIZE - 1;
  291. vector<TTEntry> transposition_table(TT_SIZE);
  292.  
  293. // Precomputed move history for ordering
  294. int history_table[2][17][16]; // [side][from][to], where from=16 represents moves from hand
  295.  
  296. // Global counter of visited positions for statistics
  297. uint64_t nodes_visited = 0;
  298.  
  299. // Serial history of states for draw detection
  300. Game state[128]; // Maximum moves in game
  301.  
  302. // Forward declarations
  303. pair<int, Move> alphabeta(int ply, int depth, int alpha, int beta);
  304. void generate_all_moves(const Game& game, vector<Move>& moves);
  305.  
  306. // Optimized bitboard shift operations
  307. inline uint16_t shift_up(uint16_t b) { return (b & NOT_1_RANK) >> 4; }
  308. inline uint16_t shift_down(uint16_t b) { return (b & NOT_8_RANK) << 4; }
  309. inline uint16_t shift_left(uint16_t b) { return (b & NOT_A_FILE) >> 1; }
  310. inline uint16_t shift_right(uint16_t b) { return (b & NOT_H_FILE) << 1; }
  311.  
  312. // Function to generate all possible moves for a player
  313. void generate_all_moves(const Game& game, vector<Move>& moves) {
  314. int me = game.side;
  315. int opp = game.opponent();
  316.  
  317. // Get bitboards for current player and opponent
  318. uint16_t my_pieces = game.piece_boards[me];
  319. uint16_t opp_pieces = game.piece_boards[opp];
  320.  
  321. // Bitmaps for possible moves in each direction
  322. uint16_t up_moves = shift_up(my_pieces) & ~(game.wall_board);
  323. uint16_t down_moves = shift_down(my_pieces) & ~(game.wall_board);
  324. uint16_t left_moves = shift_left(my_pieces) & ~(game.wall_board);
  325. uint16_t right_moves = shift_right(my_pieces) & ~(game.wall_board);
  326.  
  327. // Process each direction to generate moves
  328. auto process_moves = [&](uint16_t moves_bitmap, int direction) {
  329. // Split moves into different types
  330. uint16_t captures_opp = moves_bitmap & opp_pieces;
  331. uint16_t captures_self = moves_bitmap & my_pieces;
  332. uint16_t to_empty = moves_bitmap & game.empty_board;
  333.  
  334. // Process each type
  335. auto add_moves = [&](uint16_t bitmap, int offset) {
  336. uint16_t temp = bitmap;
  337. while (temp) {
  338. int dst_idx = __builtin_ctz(temp);
  339. temp &= ~(1u << dst_idx);
  340.  
  341. int src_idx;
  342. switch (direction) {
  343. case 0: src_idx = dst_idx + 4; break; // Up
  344. case 1: src_idx = dst_idx - 4; break; // Down
  345. case 2: src_idx = dst_idx + 1; break; // Left
  346. case 3: src_idx = dst_idx - 1; break; // Right
  347. default: src_idx = -1;
  348. }
  349.  
  350. if (src_idx >= 0 && src_idx < 16) {
  351. moves.push_back(Move(false, src_idx, dst_idx));
  352. }
  353. }
  354. };
  355.  
  356. // Add all types of moves
  357. add_moves(captures_opp, 0);
  358. add_moves(captures_self, 0);
  359. add_moves(to_empty, 0);
  360. };
  361.  
  362. // Process moves in all directions
  363. process_moves(up_moves, 0);
  364. process_moves(down_moves, 1);
  365. process_moves(left_moves, 2);
  366. process_moves(right_moves, 3);
  367.  
  368. // Placing a piece from hand
  369. if (game.hand[me] > 0) {
  370. uint16_t empty_positions = game.empty_board;
  371. while (empty_positions) {
  372. int empty_idx = __builtin_ctz(empty_positions);
  373. empty_positions &= ~(1u << empty_idx);
  374. moves.push_back(Move(true, -1, empty_idx));
  375. }
  376. }
  377. }
  378.  
  379. // Optimized alpha-beta search with move ordering
  380. pair<int, Move> alphabeta(int ply, int depth, int alpha, int beta) {
  381. nodes_visited++;
  382. int me = state[ply].side;
  383. int opp = state[ply].opponent();
  384.  
  385. // Base condition
  386. if (depth <= 0) return {state[ply].eval(), Move()};
  387.  
  388. // Use transposition table
  389. uint64_t hash = state[ply].hash;
  390. int tt_index = hash & TT_MASK;
  391. TTEntry &entry = transposition_table[tt_index];
  392.  
  393. // If we find a match and depth is sufficient, use value from table
  394. if (entry.hash == hash && entry.depth >= depth) {
  395. if (entry.flag == TT_EXACT) {
  396. return {entry.score, entry.best_move};
  397. } else if (entry.flag == TT_LOWER && entry.score >= beta) {
  398. return {entry.score, entry.best_move};
  399. } else if (entry.flag == TT_UPPER && entry.score <= alpha) {
  400. return {entry.score, entry.best_move};
  401. }
  402. // Otherwise just use best_move for ordering
  403. }
  404.  
  405. // Get bitboards for current player and opponent
  406. uint16_t my_pieces = state[ply].piece_boards[me];
  407. uint16_t opp_pieces = state[ply].piece_boards[opp];
  408.  
  409. // Prepare list of all possible moves using bit operations
  410. vector<pair<Move, int>> moves;
  411.  
  412. // Add move from transposition table, if it exists
  413. if (entry.hash == hash && entry.best_move.dst != -1) {
  414. if (entry.best_move.drop) {
  415. // Verify if drop move is valid
  416. if (state[ply].hand[me] > 0 && state[ply].isBitSet(state[ply].empty_board, entry.best_move.dst)) {
  417. moves.push_back({entry.best_move, 10000});
  418. }
  419. } else {
  420. // Verify if move is valid
  421. if (state[ply].isBitSet(my_pieces, entry.best_move.src) &&
  422. !state[ply].isBitSet(state[ply].wall_board, entry.best_move.dst)) {
  423. moves.push_back({entry.best_move, 10000});
  424. }
  425. }
  426. }
  427.  
  428. // Bitmaps for possible moves in each direction
  429. uint16_t up_moves = shift_up(my_pieces) & ~(state[ply].wall_board);
  430. uint16_t down_moves = shift_down(my_pieces) & ~(state[ply].wall_board);
  431. uint16_t left_moves = shift_left(my_pieces) & ~(state[ply].wall_board);
  432. uint16_t right_moves = shift_right(my_pieces) & ~(state[ply].wall_board);
  433.  
  434. // Function to process moves in a given direction
  435. auto process_moves = [&](uint16_t moves_bitmap, int dir) {
  436. // Different types of target squares
  437. uint16_t captures_opp = moves_bitmap & opp_pieces;
  438. uint16_t captures_self = moves_bitmap & my_pieces;
  439. uint16_t to_empty = moves_bitmap & state[ply].empty_board;
  440.  
  441. // Process captures of opponent's pieces (highest priority)
  442. uint16_t temp = captures_opp;
  443. while (temp) {
  444. int dst_idx = __builtin_ctz(temp);
  445. temp &= ~(1u << dst_idx);
  446.  
  447. int src_idx;
  448. switch (dir) {
  449. case 0: src_idx = dst_idx + 4; break; // Up
  450. case 1: src_idx = dst_idx - 4; break; // Down
  451. case 2: src_idx = dst_idx + 1; break; // Left
  452. case 3: src_idx = dst_idx - 1; break; // Right
  453. default: src_idx = -1;
  454. }
  455.  
  456. // Check if it's capturing opponent's last piece
  457. bool winning_move = (state[ply].count[opp] == 1 && state[ply].hand[opp] == 0);
  458. int move_score = winning_move ? 5000 + history_table[me][src_idx][dst_idx] :
  459. 2500 + history_table[me][src_idx][dst_idx];
  460.  
  461. moves.push_back({Move(false, src_idx, dst_idx), move_score});
  462. }
  463.  
  464. // Process captures of own pieces (lowest priority)
  465. temp = captures_self;
  466. while (temp) {
  467. int dst_idx = __builtin_ctz(temp);
  468. temp &= ~(1u << dst_idx);
  469.  
  470. int src_idx;
  471. switch (dir) {
  472. case 0: src_idx = dst_idx + 4; break;
  473. case 1: src_idx = dst_idx - 4; break;
  474. case 2: src_idx = dst_idx + 1; break;
  475. case 3: src_idx = dst_idx - 1; break;
  476. default: src_idx = -1;
  477. }
  478.  
  479. int move_score = history_table[me][src_idx][dst_idx];
  480. moves.push_back({Move(false, src_idx, dst_idx), move_score});
  481. }
  482.  
  483. // Process moves to empty squares (medium priority)
  484. temp = to_empty;
  485. while (temp) {
  486. int dst_idx = __builtin_ctz(temp);
  487. temp &= ~(1u << dst_idx);
  488.  
  489. int src_idx;
  490. switch (dir) {
  491. case 0: src_idx = dst_idx + 4; break;
  492. case 1: src_idx = dst_idx - 4; break;
  493. case 2: src_idx = dst_idx + 1; break;
  494. case 3: src_idx = dst_idx - 1; break;
  495. default: src_idx = -1;
  496. }
  497.  
  498. int move_score = 1500 + history_table[me][src_idx][dst_idx];
  499. moves.push_back({Move(false, src_idx, dst_idx), move_score});
  500. }
  501. };
  502.  
  503. // Process moves in all directions
  504. process_moves(up_moves, 0);
  505. process_moves(down_moves, 1);
  506. process_moves(left_moves, 2);
  507. process_moves(right_moves, 3);
  508.  
  509. // Moves by placing a piece from hand
  510. if (state[ply].hand[me] > 0) {
  511. uint16_t empty_positions = state[ply].empty_board;
  512. while (empty_positions) {
  513. int empty_idx = __builtin_ctz(empty_positions);
  514. empty_positions &= ~(1u << empty_idx);
  515.  
  516. int move_score = 1000 + history_table[me][16][empty_idx];
  517. moves.push_back({Move(true, -1, empty_idx), move_score});
  518. }
  519. }
  520.  
  521. // Sort moves by priority (descending)
  522. sort(moves.begin(), moves.end(), [](const auto &a, const auto &b) {
  523. return a.second > b.second;
  524. });
  525.  
  526. int best_score = -MAX_VALUE;
  527. Move best_move;
  528. bool found = false;
  529. uint8_t flag = TT_UPPER;
  530.  
  531. // Iterate through sorted moves
  532. for (const auto &move_pair : moves) {
  533. const Move &m = move_pair.first;
  534.  
  535. found = true;
  536. state[ply+1] = state[ply];
  537. state[ply+1].do_move(m);
  538.  
  539. // Principal Variation Search - NegaScout version
  540. int child_score;
  541. if (best_score == -MAX_VALUE) {
  542. auto result = alphabeta(ply+1, depth-1, -beta, -alpha);
  543. child_score = -result.first;
  544. } else {
  545. // Null-window search with reduced depth for later moves (Late Move Reduction)
  546. int reduction = 0;
  547. if (depth >= 3 && !m.drop && !state[ply].isBitSet(opp_pieces, m.dst)) {
  548. reduction = 1;
  549. }
  550.  
  551. auto result = alphabeta(ply+1, depth-1-reduction, -alpha-1, -alpha);
  552. child_score = -result.first;
  553.  
  554. // Re-search on null-window search failure
  555. if (child_score > alpha && child_score < beta) {
  556. auto result = alphabeta(ply+1, depth-1, -beta, -alpha);
  557. child_score = -result.first;
  558. }
  559. }
  560.  
  561. if (child_score > best_score) {
  562. best_score = child_score;
  563. best_move = m;
  564.  
  565. if (best_score > alpha) {
  566. alpha = best_score;
  567. flag = TT_EXACT;
  568.  
  569. // Update move history for successful move
  570. if (!m.drop) {
  571. history_table[me][m.src][m.dst] += depth * depth;
  572. } else {
  573. history_table[me][16][m.dst] += depth * depth;
  574. }
  575.  
  576. // Use adaptive DELTA value for more aggressive pruning at greater depths
  577. int currentDelta = getDelta(depth);
  578. if (alpha >= beta - currentDelta) {
  579. flag = TT_LOWER;
  580. break;
  581. }
  582. }
  583. }
  584. }
  585.  
  586. if (!found || moves.empty()) {
  587. // No valid move or empty moves were generated
  588. // Return a value close to but not equal to MAX_VALUE
  589. return {-MAX_VALUE + 1000, Move()};
  590. }
  591.  
  592. // Check if we just captured the opponent's last piece
  593. // But don't end the search - let selfplay function handle game termination
  594. if (state[ply].count[opp] == 1) {
  595. for (const auto &move_pair : moves) {
  596. const Move &m = move_pair.first;
  597. if (!m.drop) {
  598. uint16_t dst_mask = 1u << m.dst;
  599. if (state[ply].piece_boards[opp] & dst_mask) {
  600. // We found a move that captures the last opponent's piece
  601. // Return a high but not maximum value
  602. return {MAX_VALUE - 1000, m};
  603. }
  604. }
  605. }
  606. }
  607.  
  608. // Save result to transposition table
  609. entry.hash = hash;
  610. entry.depth = depth;
  611. entry.score = best_score;
  612. entry.flag = flag;
  613. entry.best_move = best_move;
  614.  
  615. return {best_score, best_move};
  616. }
  617.  
  618. void selfplay(int maxmoves = 64) {
  619. // Initialize transposition table
  620. for (int i = 0; i < TT_SIZE; i++) {
  621. transposition_table[i].hash = 0;
  622. transposition_table[i].depth = 0;
  623. transposition_table[i].score = 0;
  624. transposition_table[i].flag = TT_EXACT;
  625. transposition_table[i].best_move = Move();
  626. }
  627.  
  628. // Initialize move history
  629. memset(history_table, 0, sizeof(history_table));
  630.  
  631. // Initialize node counter
  632. nodes_visited = 0;
  633.  
  634. srand(time(NULL));
  635. state[0].reset();
  636.  
  637. bool game_over = false;
  638. string game_result = "";
  639. int current_ply = 0;
  640.  
  641. // Vypíš informáciu o začiatku hry
  642. cout << "Starting the game. Displaying moves:\n";
  643.  
  644. for (int ply = 0; ply < maxmoves; ply++) {
  645. current_ply = ply;
  646. int me = state[ply].side;
  647. int opp = state[ply].opponent();
  648.  
  649. auto start_time = chrono::high_resolution_clock::now();
  650. auto [score, move] = alphabeta(ply, DEPTH + ply, -MAX_VALUE, MAX_VALUE);
  651. auto end_time = chrono::high_resolution_clock::now();
  652.  
  653. auto duration = chrono::duration_cast<chrono::milliseconds>(end_time - start_time).count();
  654.  
  655. // Check if a valid move was found
  656. bool valid_move = (move.src != -1 || move.drop);
  657.  
  658. if (!valid_move) {
  659. // Check if there are really no valid moves
  660. vector<Move> possible_moves;
  661. generate_all_moves(state[ply], possible_moves);
  662.  
  663. if (!possible_moves.empty()) {
  664. cout << "(ERROR: algorithm returned no move, but there are " << possible_moves.size() << " possible moves)";
  665. // Use first available move
  666. move = possible_moves[0];
  667. valid_move = true;
  668. } else {
  669. cout << (me == WHITE ? "W" : "B") << " " << (ply+1) << ": (no move) - no legal moves available";
  670. cout << " eval=" << score << " time=" << duration << "ms";
  671. cout << " nodes=" << nodes_visited << " nps=" << (nodes_visited * 1000 / (duration + 1)) << "\n";
  672.  
  673. // Set game over - player with no moves loses
  674. game_over = true;
  675. game_result = (me == WHITE ? "B" : "W") + string(" wins the game (opponent has no valid moves).");
  676. break;
  677. }
  678. }
  679.  
  680. state[ply+1] = state[ply];
  681. state[ply+1].do_move(move);
  682.  
  683. // Vypíš informácie o ťahu kompaktne
  684. cout << (me == WHITE ? "W" : "B") << " " << (ply+1) << ": ";
  685. if (move.drop) {
  686. auto [col, row] = state[ply].indexToCoord(move.dst);
  687. cout << "drop " << col << row;
  688. }
  689. else {
  690. auto [src_col, src_row] = state[ply].indexToCoord(move.src);
  691. auto [dst_col, dst_row] = state[ply].indexToCoord(move.dst);
  692. cout << src_col << src_row << "->" << dst_col << dst_row;
  693. }
  694. cout << " eval=" << score << " time=" << duration << "ms";
  695. cout << " nodes=" << nodes_visited << " nps=" << (nodes_visited * 1000 / (duration + 1)) << endl;
  696.  
  697. // Reset counter for next move
  698. nodes_visited = 0;
  699.  
  700. // Check if game is over - did the move capture opponent's last piece?
  701. // Record opponent's state before and after move
  702. uint8_t opp_pieces_before = state[ply].count[opp];
  703. uint8_t opp_pieces_after = state[ply+1].count[opp];
  704.  
  705. if (opp_pieces_before == 1 && opp_pieces_after == 0) {
  706. game_over = true;
  707. game_result = (me == WHITE ? "W" : "B") + string(" wins the game by capturing all opponent's pieces.");
  708. break;
  709. }
  710.  
  711. // Check for draw
  712. for (int i = 0; i < ply; i++) {
  713. if (state[i].equals(state[ply+1])) {
  714. game_over = true;
  715. game_result = "Game ends in a draw by repetition with position after move " + to_string(i+1) + ".";
  716. break;
  717. }
  718. }
  719.  
  720. if (game_over) break;
  721. }
  722.  
  723. // Print final result
  724. if (game_over) {
  725. cout << "\n" << game_result << "\n";
  726. } else {
  727. cout << "\nGame ended after maximum number of moves (" << maxmoves << ").\n";
  728. }
  729.  
  730. // Print final position
  731. cout << "\nFinal position:\n";
  732.  
  733. // If game ended early, use the correct variable for display
  734. int final_ply = (game_over && current_ply < maxmoves) ? current_ply + 1 : (maxmoves > 0 ? maxmoves : 0);
  735. state[final_ply].print();
  736. }
  737.  
  738. int main() {
  739. ios_base::sync_with_stdio(false);
  740. cin.tie(nullptr);
  741.  
  742. cout << "Cloister - Optimized Version" << endl;
  743. cout << "Search depth: " << DEPTH << endl;
  744. cout << "TT table size: " << TT_SIZE << " entries" << endl << endl;
  745.  
  746. selfplay();
  747. return 0;
  748. }
Add Comment
Please, Sign In to add comment