File size: 3,473 Bytes
2cf2375
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
#pragma once
#include "BestemsheCore.h"

namespace Bestemshe {

class StateIndex {
private:
    static uint64_t NCR_TABLE[60][10];

public:
    static void InitCombinatorics() {
        for (int n = 0; n < 60; ++n) {
            for (int k = 0; k < 10; ++k) {
                if (k == 0 || n == k) NCR_TABLE[n][k] = 1;
                else if (k > n)       NCR_TABLE[n][k] = 0;
                else                  NCR_TABLE[n][k] = NCR_TABLE[n-1][k-1] + NCR_TABLE[n-1][k];
            }
        }
    }

    static inline uint64_t nCr(int n, int k) {
        // NCR_TABLE is [60][10]: valid rows 0..59, valid cols 0..9.
        // Guard every dimension so k==10 / n>=60 can never index out of bounds.
        if (n < 0 || k < 0 || k >= 10 || n >= 60 || n < k) return 0;
        return NCR_TABLE[n][k];
    }

    static inline int GetMinK(uint8_t M) { 
        return std::max(0, (int)M - 24); 
    }
    
    static inline int GetKCount(uint8_t M) { 
        return (std::min(24, (int)M) - GetMinK(M)) / 2 + 1; 
    }

    static uint64_t GetLayerSize(uint8_t M) {
        uint64_t k_count = GetKCount(M);
        int R = 50 - M;
        uint64_t b_count = nCr(R + 9, 9);
        return k_count * b_count;
    }

    static uint64_t IndexState(const State& s) {
        int min_K = GetMinK(s.M);
        uint64_t I_K = (s.K_self - min_K) / 2;

        uint64_t I_B = 0;
        int sum = 0;
        for (int i = 0; i < 9; ++i) {
            sum += s.board[i];
            int p_i = i + sum;
            I_B += nCr(p_i, i + 1);
        }

        int R = 50 - s.M;
        return I_K * nCr(R + 9, 9) + I_B;
    }

    static State UnindexState(uint64_t index, uint8_t M) {
        State s;
        s.M = M;
        int R = 50 - M;
        uint64_t b_count = nCr(R + 9, 9);

        uint64_t I_K = index / b_count;
        uint64_t I_B = index % b_count;

        s.K_self = GetMinK(M) + I_K * 2;
        s.K_opp = M - s.K_self;

        int current_p = 0;
        for (int i = 8; i >= 0; --i) {
            int p = i;
            while (nCr(p + 1, i + 1) <= I_B) p++;
            I_B -= nCr(p, i + 1);
            if (i == 8) s.board[9] = R + 8 - p;
            else s.board[i + 1] = current_p - p - 1;
            current_p = p;
        }
        s.board[0] = current_p;
        return s;
    }

    // O(1)-amortized "board odometer": advances a board (sum == R) to the next composition
    // in IndexState (colex combinatorial-number-system) order. Moving a stone leftward is the
    // CNS-rank successor; the inner clear loop is the odometer "carry", amortized O(1) over a
    // full enumeration. Returns false on wrap-around from the last composition (R,0,...,0).
    // Bijection-equivalent to iterating UnindexState(i) for i = 0,1,2,... within a fixed K.
    static inline bool AdvanceBoard(uint8_t b[10]) {
        int S = b[0];                               // running prefix sum b[0..j-1]
        for (int j = 1; j <= 9; ++j) {
            if (b[j] > 0) {
                b[j] -= 1;
                for (int t = 0; t <= j - 2; ++t) b[t] = 0;
                b[j - 1] = static_cast<uint8_t>(S + 1);
                return true;
            }
            S += b[j];                              // b[j] == 0 here; keeps S == b[0..j]
        }
        return false;                               // wrapped (last composition reached)
    }
};

// Allocate the static table definition
inline uint64_t StateIndex::NCR_TABLE[60][10] = {};

} // namespace Bestemshe