View difference between Paste ID: KHWnizqD and dBzrQh8u
SHOW: | | - or go back to the newest paste.
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
}