Browse Source

docs: update group description

master
Dnomd343 1 week ago
parent
commit
a2f8b0ca7f
  1. 158
      src/core/group/group.h

158
src/core/group/group.h

@ -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

Loading…
Cancel
Save