|
|
@ -1,16 +1,16 @@ |
|
|
|
/// Klotski Engine by Dnomd343 @2024
|
|
|
|
|
|
|
|
/// Group is a concept in klotski. For any valid cases, moving all its blocks
|
|
|
|
/// any finite number of times can generate a limited number of layouts, they
|
|
|
|
/// are called a `group`. Of course, there are some special groups whose size
|
|
|
|
/// is only 1, that is, only itself. (all blocks can no longer be moved)
|
|
|
|
/// Group is a concept in klotski. For any valid cases, moving any block in any
|
|
|
|
/// finite number of times can generate a limited number of cases, we call them
|
|
|
|
/// `Group`. Of course, there are some special groups whose size is only one,
|
|
|
|
/// that is, only itself. (all blocks can no longer be moved)
|
|
|
|
|
|
|
|
/// For a case, by definition, it must have a `2x2` block, at least two spaces,
|
|
|
|
/// and the others are filled by any number of `1x1`, `1x2` and `2x1`, so their
|
|
|
|
/// numbers satisfy the following inequality.
|
|
|
|
///
|
|
|
|
/// => n_1x1 + (n_1x2 + n_2x1) * 2 + n_2x2 * 4 < (20 - 2)
|
|
|
|
/// => n_1x1 + (n_1x2 + n_2x1) * 2 < 14
|
|
|
|
/// => n_1x1 + (n_1x2 + n_2x1) * 2 + n_2x2 * 4 <= (20 - 2)
|
|
|
|
/// => n_1x1 + (n_1x2 + n_2x1) * 2 <= 14
|
|
|
|
///
|
|
|
|
/// Through calculation, it can be known that these three independent variables
|
|
|
|
/// can get 204 combinations. However, on a 5x4 chessboard, it's never possible
|
|
|
@ -19,15 +19,16 @@ |
|
|
|
|
|
|
|
/// According to the number of blocks in the layout, you can use the following
|
|
|
|
/// formula to get an intermediate value `flag`, and arrange the flags in 203
|
|
|
|
/// cases from small to large to get the `type_id` value. Similarly, `type_id`
|
|
|
|
/// can also be reversed to get the number of blocks, which are one by one
|
|
|
|
/// corresponding.
|
|
|
|
/// cases from small to large that get the type_id values. Similarly, `type_id`
|
|
|
|
/// can also be used to obtain the number of blocks, which are corresponded one
|
|
|
|
/// by one.
|
|
|
|
///
|
|
|
|
/// flag => | xxx | xxx | xxxx |
|
|
|
|
/// (10-bit) | (n_1x2 + n_2x1) | (n_2x1) | (n_1x1) |
|
|
|
|
/// | (0 ~ 7) | (0 ~ 7) | (0 ~ 14) |
|
|
|
|
///
|
|
|
|
/// flag => ((n_1x2 + n_2x1) << 7) | (n_2x1 << 4) | (n_1x1)
|
|
|
|
/// ┏━━━━━━━━━━━━━━━┳━━━━━━━━━┳━━━━━━━━━━┓
|
|
|
|
/// flag => ┃ xxx ┃ xxx ┃ xxxx ┃
|
|
|
|
/// (10-bit) ┃ n_1x2 + n_2x1 ┃ n_2x1 ┃ n_1x1 ┃
|
|
|
|
/// ┃ (0 ~ 7) ┃ (0 ~ 7) ┃ (0 ~ 14) ┃
|
|
|
|
/// ┗━━━━━━━━━━━━━━━┻━━━━━━━━━┻━━━━━━━━━━┛
|
|
|
|
/// => ((n_1x2 + n_2x1) << 7) | (n_2x1 << 4) | (n_1x1)
|
|
|
|
///
|
|
|
|
/// Using the table lookup method, the `type_id` of any case can be obtained
|
|
|
|
/// within O(1), which is encapsulated in `GroupUnion`.
|
|
|
@ -35,34 +36,117 @@ |
|
|
|
/// Since the `type_id` cannot change when moving, all cases belonging to the
|
|
|
|
/// same `type_id` must be divided into different groups (of course there may
|
|
|
|
/// be only one). For a group, list the CommonCodes of all its cases, the
|
|
|
|
/// smallest of which is called the group's `seed`. List all the groups under
|
|
|
|
/// the same `type_id`, and arrange them from large to small, and arrange the
|
|
|
|
/// groups of the same size from small to large according to the `seed`, then
|
|
|
|
/// start numbering from 0 to get the `group_id`.
|
|
|
|
|
|
|
|
/// All cases of the same group will have the same `type_id` and `group_id`,
|
|
|
|
/// that is to say, for cases with the same two values, there must be a
|
|
|
|
/// reachable path for them, otherwise they will never be reachable. Arrange
|
|
|
|
/// the CommonCodes of all cases in the group from small to large, and start
|
|
|
|
/// numbering from 0 to get `case_id`, which will uniquely determine a legal
|
|
|
|
/// layout. Use the following method to express.
|
|
|
|
/// smallest of which is called the group's `seed`.
|
|
|
|
|
|
|
|
/// Consider selecting any case from a group, if its horizontally symmetrical
|
|
|
|
/// case is also in this group, we say it satisfies horizontal self-symmetry.
|
|
|
|
/// It is easy to prove that the horizontally symmetrical case of each element
|
|
|
|
/// in this group can be found in itself (of course, the case itself may be
|
|
|
|
/// horizontally symmetrical). If a group is not horizontally self-symmetrical,
|
|
|
|
/// there must be another group that is the horizontal mirror of it. We can
|
|
|
|
/// understand that the existence of horizontal self-symmetry is because these
|
|
|
|
/// two groups are "connected" and combined into one group.
|
|
|
|
|
|
|
|
/// Similarly, we can define vertical self-symmetry and centrosymmetric. The
|
|
|
|
/// latter is more special, any case of it can be found after rotating 180
|
|
|
|
/// degrees (i.e., performing horizontal and vertical flipping). If a group
|
|
|
|
/// satisfies both horizontal self-symmetry and vertical self-symmetry, we call
|
|
|
|
/// it fully self-symmetry, it must also satisfy centrosymmetric. That is, the
|
|
|
|
/// symmetrical case of any element, no matter how, the result is within the
|
|
|
|
/// scope of the group. Therefore, we can distinguish the following five
|
|
|
|
/// MirrorType attributes:
|
|
|
|
///
|
|
|
|
/// 1. `Full`: fully self-symmetry group
|
|
|
|
/// 2. `Horizontal`: horizontal self-symmetry group
|
|
|
|
/// 3. `Centro`: centrosymmetric group
|
|
|
|
/// 4. `Vertical`: vertical self-symmetry group
|
|
|
|
/// 5. `Ordinary`: non self-symmetric group
|
|
|
|
///
|
|
|
|
/// Note that for the horizontal self-symmetry group, it does not satisfy the
|
|
|
|
/// vertical self-symmetry and centrosymmetric. The same is true for others,
|
|
|
|
/// any group is one of the above 5 types.
|
|
|
|
|
|
|
|
/// Furthermore, for a horizontal self-symmetry group, if we vertically mirror
|
|
|
|
/// all of its cases, we will get another group with the same MirrorType, which
|
|
|
|
/// are vertically symmetric to each other. Although they are not same group,
|
|
|
|
/// their properties are consistent or mirrored, which we call them `Pattern`.
|
|
|
|
/// Similarly, there is another group in same pattern that is symmetric to the
|
|
|
|
/// vertical self-symmetry group and the centrosymmetric group. For these three
|
|
|
|
/// cases, each pattern contains 2 groups. For the fully self-symmetry group,
|
|
|
|
/// it itself is a pattern, that is, it only contains one group. For the non
|
|
|
|
/// self-symmetric group, it can be horizontally mirrored, vertically mirrored,
|
|
|
|
/// and centrally rotated to obtain another three groups, and the pattern they
|
|
|
|
/// form contains 4 groups.
|
|
|
|
|
|
|
|
/// In a pattern, there may be 1/2/4 groups, each with a different toward. The
|
|
|
|
/// group with the smallest `seed` is defined as A, whose horizontal symmetry
|
|
|
|
/// group is B, vertical symmetry group is C, and group rotated 180 degrees is
|
|
|
|
/// D. The above 5 types of situations correspond to 5 types of patterns. These
|
|
|
|
/// four towards may overlap, if so, the smallest toward is taken. In pattern
|
|
|
|
/// type 1, the four towards A/B/C/D are interconnected, which means that the
|
|
|
|
/// only group toward is A. In pattern type 2, A and B are interconnected, and
|
|
|
|
/// C and D are also interconnected. Therefore, the groups containing the two
|
|
|
|
/// towards A and C are vertically symmetric to each other. Similarly, the
|
|
|
|
/// pattern type 4 contains the two directions A and B, which are horizontally
|
|
|
|
/// symmetric to each other. The pattern type 3 also contains the two towards A
|
|
|
|
/// and B, they are both horizontally and vertically symmetric to each other.
|
|
|
|
/// As for the pattern type 5, it contains the four towards A/B/C/D. The 5
|
|
|
|
/// patterns correspond to the following toward combinations:
|
|
|
|
///
|
|
|
|
/// 1. fully self-symmetry group x1 -> A (=B=C=D)
|
|
|
|
/// 2. horizontal self-symmetry group x2 -> A (=B) + C (=D)
|
|
|
|
/// 3. centrosymmetric group x2 -> A (=D) + B (=C)
|
|
|
|
/// 4. vertical self-symmetry group x2 -> A (=C) + B (=D)
|
|
|
|
/// 5. non-self-symmetric group x4 -> A + B + C + D
|
|
|
|
|
|
|
|
/// In the same type_id, there may be multiple patterns, which are prioritized
|
|
|
|
/// according to the following three conditions and given numbers starting from
|
|
|
|
/// zero, namely `pattern_id`:
|
|
|
|
///
|
|
|
|
/// 1. The number of cases contained in pattern, from large to small.
|
|
|
|
/// 2. The pattern type of the above five from small to large.
|
|
|
|
/// 3. The smallest `seed` of the groups in pattern, from small to large.
|
|
|
|
///
|
|
|
|
/// Therefore, each group can be uniquely identified by type_id, pattern_id and
|
|
|
|
/// toward. The first two are numbers, and the last is an enumeration. In order
|
|
|
|
/// to easily identify the symmetry type of pattern, they will be represented
|
|
|
|
/// by different characters:
|
|
|
|
///
|
|
|
|
/// 1. `x` (Toward::A)
|
|
|
|
/// 2. `n` (Toward::A) / `u` (Toward::C)
|
|
|
|
/// 3. `s` (Toward::A) / `o` (Toward::B)
|
|
|
|
/// 4. `p` (Toward::A) / `q` (Toward::B)
|
|
|
|
/// 5. `a` (Toward::A) / `b` (Toward::B) / `c` (Toward::C) / `d` (Toward::D)
|
|
|
|
///
|
|
|
|
/// That is to say, we can use the format of `{}-{}_` to represent a group. For
|
|
|
|
/// example, the group of `1A9BF0C00`, its type_id is 169, pattern_id is 0, and
|
|
|
|
/// toward is C, which is recorded as `169-0u`, while the group of `4FEA13400`
|
|
|
|
/// is recorded as `164-0x`.
|
|
|
|
|
|
|
|
/// All cases of the group will have the same type_id, pattern_id and toward,
|
|
|
|
/// that is to say, for cases with the same values, there must be a reachable
|
|
|
|
/// path for them, otherwise they will never be reachable. Arrange all the
|
|
|
|
/// CommonCodes in group from small to large, and start numbering from 0 to get
|
|
|
|
/// `case_id`, which will uniquely determine a legal case. Use the following
|
|
|
|
/// method to express:
|
|
|
|
///
|
|
|
|
/// {type_id}-{group_id}-{case_id}
|
|
|
|
/// {type_id}-{pattern_id}{toward_char}-{case_id}
|
|
|
|
///
|
|
|
|
/// Eg1: 1A9BF0C00 -> `169-1-7472`
|
|
|
|
/// Eg2: 4FEA13400 -> `164-0-30833`
|
|
|
|
/// Eg1: 1A9BF0C00 -> `169-0u-7472`
|
|
|
|
/// Eg2: 4FEA13400 -> `164-0x-30833`
|
|
|
|
|
|
|
|
/// The range of `type_id` is [0, 203), the maximum `group_id` is 2652 (there
|
|
|
|
/// are 2653 groups when `type_id = 164`), the maximum `case_id` is 964655
|
|
|
|
/// (there are 964656 cases when `type_id = 58` & `group_id = 0`). Therefore,
|
|
|
|
/// these three numbers meet the following range requirements.
|
|
|
|
/// The value ranges of these four key variables are as follows:
|
|
|
|
///
|
|
|
|
/// | type_id: [0, 203) | group_id: [0, 2653) | case_id: [0, 964656) |
|
|
|
|
/// | (8-bit ~ 256) | (12-bit ~ 4096) | (20-bit ~ 1048576) |
|
|
|
|
/// + type_id: [0, 203) (8-bit ~ 256)
|
|
|
|
/// + pattern_id: [0, 665) (10-bit ~ 1024) (max in type_id `164`)
|
|
|
|
/// + toward: [0, 4) (2-bit ~ 4)
|
|
|
|
/// + case_id: [0, 964656) (20-bit ~ 1048576) (max in group `58-0x`)
|
|
|
|
///
|
|
|
|
/// Typically, these three variables are generally recorded in decimal and
|
|
|
|
/// displayed in the form of strings. They can facilitate the relationship
|
|
|
|
/// between multiple cases.
|
|
|
|
/// These four variables are collectively called CaseInfo, and they correspond
|
|
|
|
/// uniquely to each case. We can quickly get the type_id value of CommonCode,
|
|
|
|
/// but getting other variables requires a lot of calculations. Coincidentally,
|
|
|
|
/// they can be stored in a 32-bit length, which means that we can store all
|
|
|
|
/// case information in advance to quickly convert CommonCode and CaseInfo.
|
|
|
|
|
|
|
|
#pragma once |
|
|
|
|
|
|
|