File: PrecompMoves.h

package info (click to toggle)
pentobi 29.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 3,892 kB
  • sloc: cpp: 25,719; javascript: 875; xml: 40; makefile: 13; sh: 6
file content (135 lines) | stat: -rw-r--r-- 5,048 bytes parent folder | download
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
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
//-----------------------------------------------------------------------------
/** @file libpentobi_base/PrecompMoves.h
    @author Markus Enzenberger
    @copyright GNU General Public License version 3 or later */
//-----------------------------------------------------------------------------

#ifndef LIBPENTOBI_BASE_PRECOMP_MOVES_H
#define LIBPENTOBI_BASE_PRECOMP_MOVES_H

#include <array>
#include "Grid.h"
#include "Move.h"
#include "PieceMap.h"
#include "libboardgame_base/Range.h"

namespace libpentobi_base {

using namespace std;

//-----------------------------------------------------------------------------

/** Precomputed moves for fast move generation.
    Compact storage of precomputed lists with local moves. Each list contains
    all moves that include a given point constrained by the piece type and the
    forbidden status of adjacent points. This drastically reduces the number of
    moves that need to be checked for legality during move generation.
    @see Board::get_adj_status() */
class PrecompMoves
{
public:
    /** The number of neighbors used for computing the adjacent status.
        The adjacent status is a single number that encodes the forbidden
        status of the first adj_status_nu_adj neighbors (from the list
        Geometry::get_adj() concatenated with Geometry::get_diag()). It is used
        for speeding up the matching of moves at a given point. Increasing this
        number will make the precomputed lists shorter but exponentially
        increase the number of lists and the total memory used for all lists.
        Therefore, the optimal value for speeding up the matching depends on
        the CPU cache size. */
#ifdef PENTOBI_LOW_RESOURCES
    static constexpr unsigned adj_status_nu_adj = 5;
#else
    static constexpr unsigned adj_status_nu_adj = 6;
#endif

    /** The maximum sum of the sizes of all precomputed move lists in any
        game variant. */
    static constexpr unsigned max_move_lists_sum_length =
            adj_status_nu_adj == 5 ? 2356736 : 2628840;
    static_assert(adj_status_nu_adj == 5 || adj_status_nu_adj == 6);

    /** The range of values for the adjacent status. */
    static constexpr unsigned nu_adj_status = 1 << adj_status_nu_adj;

    /** Begin/end range for lists with moves at a given point. */
    using Range = libboardgame_base::Range<const Move>;


    /** Add a move to list during construction. */
    void set_move(unsigned i, Move mv)
    {
        LIBBOARDGAME_ASSERT(i < max_move_lists_sum_length);
        m_move_lists[i] = mv;
    }

    /** Store beginning and end of a local move list duing construction. */
    void set_list_range(Point p, unsigned adj_status, Piece piece,
                        unsigned begin, unsigned size)
    {
        m_moves_range[p][adj_status][piece] = CompressedRange(begin, size);
    }

    /** Get all moves of a piece at a point constrained by the forbidden
        status of adjacent points. */
    Range get_moves(Piece piece, Point p, unsigned adj_status = 0) const
    {
        auto& range = m_moves_range[p][adj_status][piece];
        auto begin = move_lists_begin() + range.begin();
        return {begin, begin + range.size()};
    }

    bool has_moves(Piece piece, Point p, unsigned adj_status) const
    {
        return ! m_moves_range[p][adj_status][piece].empty();
    }

    /** Begin of storage for move lists.
        Only needed for special use cases like during an in-place construction
        of PrecompMoves for follow-up positions when we need to compare the
        index of old iterators with the current get_size() to ensure that
        we don't overwrite any old content that we still need to read
        during the construction. */
    const Move* move_lists_begin() const { return &(*m_move_lists.begin()); }

private:
    class CompressedRange
    {
    public:
        CompressedRange() = default;

        CompressedRange(unsigned begin, unsigned size)
        {
            LIBBOARDGAME_ASSERT(begin + size <= max_move_lists_sum_length);
            static_assert(max_move_lists_sum_length < (1 << 24));
            LIBBOARDGAME_ASSERT(size < (1 << 8));
            m_val = size;
            if (size != 0)
                m_val |= (begin << 8);
        }

        bool empty() const { return m_val == 0; }

        unsigned begin() const { return m_val >> 8; }

        unsigned size() const { return m_val & 0xff; }

    private:
        uint_least32_t m_val;
    };

    /** See m_move_lists. */
    Grid<array<PieceMap<CompressedRange>, nu_adj_status>> m_moves_range;

    /** Compact representation of lists of moves of a piece at a point
        constrained by the forbidden status of adjacent points.
        All lists are stored in a single array; m_moves_range contains
        information about the actual begin/end indices. */
    array<Move, max_move_lists_sum_length> m_move_lists;
};

//-----------------------------------------------------------------------------

} // namespace libpentobi_base

#endif // LIBPENTOBI_BASE_PRECOMP_MOVES_H