From a2f8b0ca7fdc1dc5f00c8de13acb65c732e846d9 Mon Sep 17 00:00:00 2001 From: Dnomd343 Date: Sun, 15 Dec 2024 14:53:22 +0800 Subject: [PATCH] docs: update group description --- src/core/group/group.h | 158 +++++++++++++++++++++++++++++++---------- 1 file changed, 121 insertions(+), 37 deletions(-) diff --git a/src/core/group/group.h b/src/core/group/group.h index b60661c..240e2db 100644 --- a/src/core/group/group.h +++ b/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