Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #include <bits/stdc++.h>
- using namespace std;
- // Constants
- const int MAX_VALUE = 10000;
- const int DEPTH = 13;
- const int N = 4; // 4x4 board size
- // Player constants
- const int WHITE = 0;
- const int BLACK = 1;
- // Function for adaptive DELTA calculation based on current depth
- inline int getDelta(int depth) {
- return (depth * depth); // Faster increase for high depths
- }
- // Precomputed move directions for each index
- // Direction 0: up, 1: down, 2: left, 3: right
- const int MOVES[16][4] = {
- {-1, 4, -1, 1}, // 0 (a1)
- {-1, 5, 0, 2}, // 1 (a2)
- {-1, 6, 1, 3}, // 2 (a3)
- {-1, 7, 2, -1}, // 3 (a4)
- {0, 8, -1, 5}, // 4 (b1)
- {1, 9, 4, 6}, // 5 (b2)
- {2, 10, 5, 7}, // 6 (b3)
- {3, 11, 6, -1}, // 7 (b4)
- {4, 12, -1, 9}, // 8 (c1)
- {5, 13, 8, 10}, // 9 (c2)
- {6, 14, 9, 11}, // 10 (c3)
- {7, 15, 10, -1}, // 11 (c4)
- {8, -1, -1, 13}, // 12 (d1)
- {9, -1, 12, 14}, // 13 (d2)
- {10, -1, 13, 15}, // 14 (d3)
- {11, -1, 14, -1} // 15 (d4)
- };
- // Precomputed masks for shift limits
- const uint16_t NOT_A_FILE = 0xEEEE; // Exclude column A (0, 4, 8, 12)
- const uint16_t NOT_H_FILE = 0x7777; // Exclude column H (3, 7, 11, 15)
- const uint16_t NOT_1_RANK = 0xFFF0; // Exclude row 1 (0-3)
- const uint16_t NOT_8_RANK = 0x0FFF; // Exclude row 8 (12-15)
- // Structure for moves
- struct Move {
- bool drop;
- int src, dst; // Square indices (0-15) instead of coordinates
- // Constructor for easier move creation
- Move(bool is_drop = false, int source = -1, int dest = -1)
- : drop(is_drop), src(source), dst(dest) {}
- };
- // Optimized game representation with 16-bit bitboards
- struct Game {
- // Bitboards - each represents one type of square (16 bits, one for each square)
- uint16_t empty_board; // 1 at positions where the square is empty
- uint16_t piece_boards[2]; // piece_boards[WHITE] = white pieces, piece_boards[BLACK] = black pieces
- uint16_t wall_board; // 1 at positions with walls
- // Other data
- uint8_t hand[2]; // hand[WHITE] = white pieces in hand, hand[BLACK] = black pieces
- uint8_t count[2]; // count[WHITE] = white piece count, count[BLACK] = black piece count
- uint8_t side; // WHITE = white to move, BLACK = black to move
- // Hash value for quick comparison in transposition table
- uint64_t hash;
- // Convert index (0-15) to coordinates (a1, a2, ..., d4) - for display only
- inline pair<char, int> indexToCoord(int idx) const {
- return {static_cast<char>('a' + (idx % N)), (idx / N) + 1};
- }
- // Set bit in given bitboard at given index
- inline void setBit(uint16_t &board, int idx) {
- board |= (1u << idx);
- }
- // Clear bit in given bitboard at given index
- inline void clearBit(uint16_t &board, int idx) {
- board &= ~(1u << idx);
- }
- // Check if bit is set
- inline bool isBitSet(uint16_t board, int idx) const {
- return (board & (1u << idx)) != 0;
- }
- // Set only one bitboard at given index (clear others)
- inline void setOnlyOneBitboard(int idx, uint16_t &target_board) {
- uint16_t mask = 1u << idx;
- empty_board &= ~mask;
- piece_boards[WHITE] &= ~mask;
- piece_boards[BLACK] &= ~mask;
- wall_board &= ~mask;
- target_board |= mask;
- }
- // Get opponent side
- inline int opponent() const {
- return 1 - side;
- }
- // Initialize game
- void reset() {
- hand[WHITE] = hand[BLACK] = 0;
- count[WHITE] = count[BLACK] = 8;
- side = WHITE; // White starts
- // Initialize bitboards
- empty_board = 0;
- piece_boards[WHITE] = 0;
- piece_boards[BLACK] = 0;
- wall_board = 0;
- // Set up the board - alternating black and white squares
- for (int idx = 0; idx < 16; idx++) {
- int row = idx / N;
- int col = idx % N;
- if ((row + col) % 2 == 0) {
- setBit(piece_boards[BLACK], idx);
- } else {
- setBit(piece_boards[WHITE], idx);
- }
- }
- // Set hash value
- updateHash();
- }
- // Update hash value
- inline void updateHash() {
- hash = 0;
- hash ^= empty_board;
- hash ^= ((uint64_t)piece_boards[WHITE] << 16);
- hash ^= ((uint64_t)piece_boards[BLACK] << 32);
- hash ^= ((uint64_t)wall_board << 48);
- hash ^= ((uint64_t)hand[WHITE] << 56);
- hash ^= ((uint64_t)hand[BLACK] << 58);
- hash ^= ((uint64_t)side << 60);
- }
- // Enhanced evaluation function
- int eval() const {
- int opp = opponent();
- // Basic components
- int empty = hand[WHITE] + hand[BLACK];
- int piece_diff = count[WHITE] - count[BLACK];
- int hand_diff = hand[WHITE] - hand[BLACK];
- // Functions for bitboard shifts
- auto shift_up = [](uint16_t b) { return (b & NOT_1_RANK) >> 4; };
- auto shift_down = [](uint16_t b) { return (b & NOT_8_RANK) << 4; };
- auto shift_left = [](uint16_t b) { return (b & NOT_A_FILE) >> 1; };
- auto shift_right = [](uint16_t b) { return (b & NOT_H_FILE) << 1; };
- // Calculate target squares for each player's pieces
- uint16_t targets[2];
- for (int player = 0; player < 2; player++) {
- targets[player] =
- (shift_up(piece_boards[player]) |
- shift_down(piece_boards[player]) |
- shift_left(piece_boards[player]) |
- shift_right(piece_boards[player])) & ~wall_board;
- }
- // Count bits in target squares = number of possible moves
- int mobility[2];
- mobility[WHITE] = __builtin_popcount(targets[WHITE]);
- mobility[BLACK] = __builtin_popcount(targets[BLACK]);
- int mobility_diff = mobility[WHITE] - mobility[BLACK];
- // Final position score with weights for different components
- int score = 31 * mobility_diff + 25 * hand_diff + 19 * empty + 13 * piece_diff + 7;
- return score * (side == WHITE ? 1 : -1);
- }
- // Optimized move implementation with bitboards
- void do_move(const Move &m) {
- int me = side, opp = opponent();
- if (m.drop) {
- // Placing a piece from hand
- uint16_t dst_mask = 1u << m.dst;
- empty_board &= ~dst_mask;
- piece_boards[me] |= dst_mask;
- hand[me]--;
- count[me]++;
- } else {
- // Moving a piece
- uint16_t src_mask = 1u << m.src;
- uint16_t dst_mask = 1u << m.dst;
- // Remove original piece
- piece_boards[me] &= ~src_mask;
- // Set empty square at original position
- empty_board |= src_mask;
- // Process target square
- if (empty_board & dst_mask) {
- // Target square is empty
- empty_board &= ~dst_mask;
- piece_boards[me] |= dst_mask;
- } else {
- // Set wall and adjust piece counts
- if (piece_boards[me] & dst_mask) {
- // Piece collided with own piece
- count[me] -= 2;
- } else {
- // Piece collided with opponent's piece
- count[me]--;
- count[opp]--;
- }
- // Clear all bitboards at target position
- empty_board &= ~dst_mask;
- piece_boards[WHITE] &= ~dst_mask;
- piece_boards[BLACK] &= ~dst_mask;
- // Set wall
- wall_board |= dst_mask;
- hand[me]++;
- }
- }
- side = opp; // Change side to move
- updateHash(); // Update hash value
- }
- // Compare two game states - optimized using hash
- bool equals(const Game &other) const {
- return hash == other.hash;
- }
- // For debugging - display board state
- void print() const {
- cout << "Side: " << (side == WHITE ? "WHITE" : "BLACK") << endl;
- cout << "Hand: W=" << (int)hand[WHITE] << ", B=" << (int)hand[BLACK] << endl;
- cout << "Count: W=" << (int)count[WHITE] << ", B=" << (int)count[BLACK] << endl;
- cout << "Board:" << endl;
- cout << " a b c d" << endl;
- cout << " +---+---+---+---+" << endl;
- // Prechod riadkami od 3 po 0 (štvrtý riadok ako prvý)
- for (int row = N-1; row >= 0; row--) {
- cout << row+1 << " |";
- for (int col = 0; col < N; col++) {
- int idx = row * N + col;
- uint16_t mask = 1u << idx;
- if (piece_boards[WHITE] & mask) cout << " W |";
- else if (piece_boards[BLACK] & mask) cout << " B |";
- else if (empty_board & mask) cout << " |";
- else if (wall_board & mask) cout << " X |";
- else cout << "?|"; // Error - no bitboard is set
- }
- cout << " " << row+1 << endl;
- cout << " +---+---+---+---+" << endl;
- }
- cout << " a b c d" << endl;
- }
- };
- // Enum for transposition table entry type
- enum TTFlag {
- TT_EXACT = 0,
- TT_UPPER = 1,
- TT_LOWER = 2
- };
- // Optimized transposition table with hash collision handling
- struct TTEntry {
- uint64_t hash;
- int16_t depth;
- int16_t score;
- uint8_t flag; // 0 = exact value, 1 = upper bound, 2 = lower bound
- Move best_move;
- };
- // Increased transposition table size and using mask instead of modulo
- const int TT_BITS = 28; // 2^28 = 16777216 entries
- const int TT_SIZE = 1 << TT_BITS;
- const int TT_MASK = TT_SIZE - 1;
- vector<TTEntry> transposition_table(TT_SIZE);
- // Precomputed move history for ordering
- int history_table[2][17][16]; // [side][from][to], where from=16 represents moves from hand
- // Global counter of visited positions for statistics
- uint64_t nodes_visited = 0;
- // Serial history of states for draw detection
- Game state[128]; // Maximum moves in game
- // Forward declarations
- pair<int, Move> alphabeta(int ply, int depth, int alpha, int beta);
- void generate_all_moves(const Game& game, vector<Move>& moves);
- // Optimized bitboard shift operations
- inline uint16_t shift_up(uint16_t b) { return (b & NOT_1_RANK) >> 4; }
- inline uint16_t shift_down(uint16_t b) { return (b & NOT_8_RANK) << 4; }
- inline uint16_t shift_left(uint16_t b) { return (b & NOT_A_FILE) >> 1; }
- inline uint16_t shift_right(uint16_t b) { return (b & NOT_H_FILE) << 1; }
- // Function to generate all possible moves for a player
- void generate_all_moves(const Game& game, vector<Move>& moves) {
- int me = game.side;
- int opp = game.opponent();
- // Get bitboards for current player and opponent
- uint16_t my_pieces = game.piece_boards[me];
- uint16_t opp_pieces = game.piece_boards[opp];
- // Bitmaps for possible moves in each direction
- uint16_t up_moves = shift_up(my_pieces) & ~(game.wall_board);
- uint16_t down_moves = shift_down(my_pieces) & ~(game.wall_board);
- uint16_t left_moves = shift_left(my_pieces) & ~(game.wall_board);
- uint16_t right_moves = shift_right(my_pieces) & ~(game.wall_board);
- // Process each direction to generate moves
- auto process_moves = [&](uint16_t moves_bitmap, int direction) {
- // Split moves into different types
- uint16_t captures_opp = moves_bitmap & opp_pieces;
- uint16_t captures_self = moves_bitmap & my_pieces;
- uint16_t to_empty = moves_bitmap & game.empty_board;
- // Process each type
- auto add_moves = [&](uint16_t bitmap, int offset) {
- uint16_t temp = bitmap;
- while (temp) {
- int dst_idx = __builtin_ctz(temp);
- temp &= ~(1u << dst_idx);
- int src_idx;
- switch (direction) {
- case 0: src_idx = dst_idx + 4; break; // Up
- case 1: src_idx = dst_idx - 4; break; // Down
- case 2: src_idx = dst_idx + 1; break; // Left
- case 3: src_idx = dst_idx - 1; break; // Right
- default: src_idx = -1;
- }
- if (src_idx >= 0 && src_idx < 16) {
- moves.push_back(Move(false, src_idx, dst_idx));
- }
- }
- };
- // Add all types of moves
- add_moves(captures_opp, 0);
- add_moves(captures_self, 0);
- add_moves(to_empty, 0);
- };
- // Process moves in all directions
- process_moves(up_moves, 0);
- process_moves(down_moves, 1);
- process_moves(left_moves, 2);
- process_moves(right_moves, 3);
- // Placing a piece from hand
- if (game.hand[me] > 0) {
- uint16_t empty_positions = game.empty_board;
- while (empty_positions) {
- int empty_idx = __builtin_ctz(empty_positions);
- empty_positions &= ~(1u << empty_idx);
- moves.push_back(Move(true, -1, empty_idx));
- }
- }
- }
- // Optimized alpha-beta search with move ordering
- pair<int, Move> alphabeta(int ply, int depth, int alpha, int beta) {
- nodes_visited++;
- int me = state[ply].side;
- int opp = state[ply].opponent();
- // Base condition
- if (depth <= 0) return {state[ply].eval(), Move()};
- // Use transposition table
- uint64_t hash = state[ply].hash;
- int tt_index = hash & TT_MASK;
- TTEntry &entry = transposition_table[tt_index];
- // If we find a match and depth is sufficient, use value from table
- if (entry.hash == hash && entry.depth >= depth) {
- if (entry.flag == TT_EXACT) {
- return {entry.score, entry.best_move};
- } else if (entry.flag == TT_LOWER && entry.score >= beta) {
- return {entry.score, entry.best_move};
- } else if (entry.flag == TT_UPPER && entry.score <= alpha) {
- return {entry.score, entry.best_move};
- }
- // Otherwise just use best_move for ordering
- }
- // Get bitboards for current player and opponent
- uint16_t my_pieces = state[ply].piece_boards[me];
- uint16_t opp_pieces = state[ply].piece_boards[opp];
- // Prepare list of all possible moves using bit operations
- vector<pair<Move, int>> moves;
- // Add move from transposition table, if it exists
- if (entry.hash == hash && entry.best_move.dst != -1) {
- if (entry.best_move.drop) {
- // Verify if drop move is valid
- if (state[ply].hand[me] > 0 && state[ply].isBitSet(state[ply].empty_board, entry.best_move.dst)) {
- moves.push_back({entry.best_move, 10000});
- }
- } else {
- // Verify if move is valid
- if (state[ply].isBitSet(my_pieces, entry.best_move.src) &&
- !state[ply].isBitSet(state[ply].wall_board, entry.best_move.dst)) {
- moves.push_back({entry.best_move, 10000});
- }
- }
- }
- // Bitmaps for possible moves in each direction
- uint16_t up_moves = shift_up(my_pieces) & ~(state[ply].wall_board);
- uint16_t down_moves = shift_down(my_pieces) & ~(state[ply].wall_board);
- uint16_t left_moves = shift_left(my_pieces) & ~(state[ply].wall_board);
- uint16_t right_moves = shift_right(my_pieces) & ~(state[ply].wall_board);
- // Function to process moves in a given direction
- auto process_moves = [&](uint16_t moves_bitmap, int dir) {
- // Different types of target squares
- uint16_t captures_opp = moves_bitmap & opp_pieces;
- uint16_t captures_self = moves_bitmap & my_pieces;
- uint16_t to_empty = moves_bitmap & state[ply].empty_board;
- // Process captures of opponent's pieces (highest priority)
- uint16_t temp = captures_opp;
- while (temp) {
- int dst_idx = __builtin_ctz(temp);
- temp &= ~(1u << dst_idx);
- int src_idx;
- switch (dir) {
- case 0: src_idx = dst_idx + 4; break; // Up
- case 1: src_idx = dst_idx - 4; break; // Down
- case 2: src_idx = dst_idx + 1; break; // Left
- case 3: src_idx = dst_idx - 1; break; // Right
- default: src_idx = -1;
- }
- // Check if it's capturing opponent's last piece
- bool winning_move = (state[ply].count[opp] == 1 && state[ply].hand[opp] == 0);
- int move_score = winning_move ? 5000 + history_table[me][src_idx][dst_idx] :
- 2500 + history_table[me][src_idx][dst_idx];
- moves.push_back({Move(false, src_idx, dst_idx), move_score});
- }
- // Process captures of own pieces (lowest priority)
- temp = captures_self;
- while (temp) {
- int dst_idx = __builtin_ctz(temp);
- temp &= ~(1u << dst_idx);
- int src_idx;
- switch (dir) {
- case 0: src_idx = dst_idx + 4; break;
- case 1: src_idx = dst_idx - 4; break;
- case 2: src_idx = dst_idx + 1; break;
- case 3: src_idx = dst_idx - 1; break;
- default: src_idx = -1;
- }
- int move_score = history_table[me][src_idx][dst_idx];
- moves.push_back({Move(false, src_idx, dst_idx), move_score});
- }
- // Process moves to empty squares (medium priority)
- temp = to_empty;
- while (temp) {
- int dst_idx = __builtin_ctz(temp);
- temp &= ~(1u << dst_idx);
- int src_idx;
- switch (dir) {
- case 0: src_idx = dst_idx + 4; break;
- case 1: src_idx = dst_idx - 4; break;
- case 2: src_idx = dst_idx + 1; break;
- case 3: src_idx = dst_idx - 1; break;
- default: src_idx = -1;
- }
- int move_score = 1500 + history_table[me][src_idx][dst_idx];
- moves.push_back({Move(false, src_idx, dst_idx), move_score});
- }
- };
- // Process moves in all directions
- process_moves(up_moves, 0);
- process_moves(down_moves, 1);
- process_moves(left_moves, 2);
- process_moves(right_moves, 3);
- // Moves by placing a piece from hand
- if (state[ply].hand[me] > 0) {
- uint16_t empty_positions = state[ply].empty_board;
- while (empty_positions) {
- int empty_idx = __builtin_ctz(empty_positions);
- empty_positions &= ~(1u << empty_idx);
- int move_score = 1000 + history_table[me][16][empty_idx];
- moves.push_back({Move(true, -1, empty_idx), move_score});
- }
- }
- // Sort moves by priority (descending)
- sort(moves.begin(), moves.end(), [](const auto &a, const auto &b) {
- return a.second > b.second;
- });
- int best_score = -MAX_VALUE;
- Move best_move;
- bool found = false;
- uint8_t flag = TT_UPPER;
- // Iterate through sorted moves
- for (const auto &move_pair : moves) {
- const Move &m = move_pair.first;
- found = true;
- state[ply+1] = state[ply];
- state[ply+1].do_move(m);
- // Principal Variation Search - NegaScout version
- int child_score;
- if (best_score == -MAX_VALUE) {
- auto result = alphabeta(ply+1, depth-1, -beta, -alpha);
- child_score = -result.first;
- } else {
- // Null-window search with reduced depth for later moves (Late Move Reduction)
- int reduction = 0;
- if (depth >= 3 && !m.drop && !state[ply].isBitSet(opp_pieces, m.dst)) {
- reduction = 1;
- }
- auto result = alphabeta(ply+1, depth-1-reduction, -alpha-1, -alpha);
- child_score = -result.first;
- // Re-search on null-window search failure
- if (child_score > alpha && child_score < beta) {
- auto result = alphabeta(ply+1, depth-1, -beta, -alpha);
- child_score = -result.first;
- }
- }
- if (child_score > best_score) {
- best_score = child_score;
- best_move = m;
- if (best_score > alpha) {
- alpha = best_score;
- flag = TT_EXACT;
- // Update move history for successful move
- if (!m.drop) {
- history_table[me][m.src][m.dst] += depth * depth;
- } else {
- history_table[me][16][m.dst] += depth * depth;
- }
- // Use adaptive DELTA value for more aggressive pruning at greater depths
- int currentDelta = getDelta(depth);
- if (alpha >= beta - currentDelta) {
- flag = TT_LOWER;
- break;
- }
- }
- }
- }
- if (!found || moves.empty()) {
- // No valid move or empty moves were generated
- // Return a value close to but not equal to MAX_VALUE
- return {-MAX_VALUE + 1000, Move()};
- }
- // Check if we just captured the opponent's last piece
- // But don't end the search - let selfplay function handle game termination
- if (state[ply].count[opp] == 1) {
- for (const auto &move_pair : moves) {
- const Move &m = move_pair.first;
- if (!m.drop) {
- uint16_t dst_mask = 1u << m.dst;
- if (state[ply].piece_boards[opp] & dst_mask) {
- // We found a move that captures the last opponent's piece
- // Return a high but not maximum value
- return {MAX_VALUE - 1000, m};
- }
- }
- }
- }
- // Save result to transposition table
- entry.hash = hash;
- entry.depth = depth;
- entry.score = best_score;
- entry.flag = flag;
- entry.best_move = best_move;
- return {best_score, best_move};
- }
- void selfplay(int maxmoves = 64) {
- // Initialize transposition table
- for (int i = 0; i < TT_SIZE; i++) {
- transposition_table[i].hash = 0;
- transposition_table[i].depth = 0;
- transposition_table[i].score = 0;
- transposition_table[i].flag = TT_EXACT;
- transposition_table[i].best_move = Move();
- }
- // Initialize move history
- memset(history_table, 0, sizeof(history_table));
- // Initialize node counter
- nodes_visited = 0;
- srand(time(NULL));
- state[0].reset();
- bool game_over = false;
- string game_result = "";
- int current_ply = 0;
- // Vypíš informáciu o začiatku hry
- cout << "Starting the game. Displaying moves:\n";
- for (int ply = 0; ply < maxmoves; ply++) {
- current_ply = ply;
- int me = state[ply].side;
- int opp = state[ply].opponent();
- auto start_time = chrono::high_resolution_clock::now();
- auto [score, move] = alphabeta(ply, DEPTH + ply, -MAX_VALUE, MAX_VALUE);
- auto end_time = chrono::high_resolution_clock::now();
- auto duration = chrono::duration_cast<chrono::milliseconds>(end_time - start_time).count();
- // Check if a valid move was found
- bool valid_move = (move.src != -1 || move.drop);
- if (!valid_move) {
- // Check if there are really no valid moves
- vector<Move> possible_moves;
- generate_all_moves(state[ply], possible_moves);
- if (!possible_moves.empty()) {
- cout << "(ERROR: algorithm returned no move, but there are " << possible_moves.size() << " possible moves)";
- // Use first available move
- move = possible_moves[0];
- valid_move = true;
- } else {
- cout << (me == WHITE ? "W" : "B") << " " << (ply+1) << ": (no move) - no legal moves available";
- cout << " eval=" << score << " time=" << duration << "ms";
- cout << " nodes=" << nodes_visited << " nps=" << (nodes_visited * 1000 / (duration + 1)) << "\n";
- // Set game over - player with no moves loses
- game_over = true;
- game_result = (me == WHITE ? "B" : "W") + string(" wins the game (opponent has no valid moves).");
- break;
- }
- }
- state[ply+1] = state[ply];
- state[ply+1].do_move(move);
- // Vypíš informácie o ťahu kompaktne
- cout << (me == WHITE ? "W" : "B") << " " << (ply+1) << ": ";
- if (move.drop) {
- auto [col, row] = state[ply].indexToCoord(move.dst);
- cout << "drop " << col << row;
- }
- else {
- auto [src_col, src_row] = state[ply].indexToCoord(move.src);
- auto [dst_col, dst_row] = state[ply].indexToCoord(move.dst);
- cout << src_col << src_row << "->" << dst_col << dst_row;
- }
- cout << " eval=" << score << " time=" << duration << "ms";
- cout << " nodes=" << nodes_visited << " nps=" << (nodes_visited * 1000 / (duration + 1)) << endl;
- // Reset counter for next move
- nodes_visited = 0;
- // Check if game is over - did the move capture opponent's last piece?
- // Record opponent's state before and after move
- uint8_t opp_pieces_before = state[ply].count[opp];
- uint8_t opp_pieces_after = state[ply+1].count[opp];
- if (opp_pieces_before == 1 && opp_pieces_after == 0) {
- game_over = true;
- game_result = (me == WHITE ? "W" : "B") + string(" wins the game by capturing all opponent's pieces.");
- break;
- }
- // Check for draw
- for (int i = 0; i < ply; i++) {
- if (state[i].equals(state[ply+1])) {
- game_over = true;
- game_result = "Game ends in a draw by repetition with position after move " + to_string(i+1) + ".";
- break;
- }
- }
- if (game_over) break;
- }
- // Print final result
- if (game_over) {
- cout << "\n" << game_result << "\n";
- } else {
- cout << "\nGame ended after maximum number of moves (" << maxmoves << ").\n";
- }
- // Print final position
- cout << "\nFinal position:\n";
- // If game ended early, use the correct variable for display
- int final_ply = (game_over && current_ply < maxmoves) ? current_ply + 1 : (maxmoves > 0 ? maxmoves : 0);
- state[final_ply].print();
- }
- int main() {
- ios_base::sync_with_stdio(false);
- cin.tie(nullptr);
- cout << "Cloister - Optimized Version" << endl;
- cout << "Search depth: " << DEPTH << endl;
- cout << "TT table size: " << TT_SIZE << " entries" << endl << endl;
- selfplay();
- return 0;
- }
Add Comment
Please, Sign In to add comment