|  |  | @ -1,21 +1,22 @@ | 
			
		
	
		
			
				
					|  |  |  | #include <iostream> | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  | #include "reverse.h" | 
			
		
	
		
			
				
					|  |  |  | #include "utility.h" | 
			
		
	
		
			
				
					|  |  |  | #include "all_cases.h" | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  | //typedef uint32_t Range;
 | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  | using klotski::cases::BasicRanges; | 
			
		
	
		
			
				
					|  |  |  | using klotski::cases::range_reverse; | 
			
		
	
		
			
				
					|  |  |  | namespace klotski { | 
			
		
	
		
			
				
					|  |  |  | namespace cases { | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  | inline int low_zero_num(uint32_t bin) { | 
			
		
	
		
			
				
					|  |  |  |     return __builtin_ctzl(bin); | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |     // WARN: be aware of serious performance issues
 | 
			
		
	
		
			
				
					|  |  |  |     // return __builtin_popcount(~(bin ^ -bin)) - 1;
 | 
			
		
	
		
			
				
					|  |  |  | /// Calculate all possible klotski heads.
 | 
			
		
	
		
			
				
					|  |  |  | static consteval std::array<int, 12> case_heads() { | 
			
		
	
		
			
				
					|  |  |  |     std::array<int, 12> heads = {}; | 
			
		
	
		
			
				
					|  |  |  |     for (int i = 0, head = 0; head < 15; ++head) { | 
			
		
	
		
			
				
					|  |  |  |         if (head % 4 != 3) { | 
			
		
	
		
			
				
					|  |  |  |             heads[i++] = head; | 
			
		
	
		
			
				
					|  |  |  |         } | 
			
		
	
		
			
				
					|  |  |  |     } | 
			
		
	
		
			
				
					|  |  |  |     return heads; | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  | int check_range(int head, uint32_t range) noexcept { | 
			
		
	
		
			
				
					|  |  |  | /// Check whether the combination of head and range is valid.
 | 
			
		
	
		
			
				
					|  |  |  | static int check_range(int head, uint32_t range) noexcept { | 
			
		
	
		
			
				
					|  |  |  |     constexpr uint32_t MASK_1x1 = 0b00000001; | 
			
		
	
		
			
				
					|  |  |  |     constexpr uint32_t MASK_1x2 = 0b00000011; | 
			
		
	
		
			
				
					|  |  |  |     constexpr uint32_t MASK_2x1 = 0b00010001; | 
			
		
	
	
		
			
				
					|  |  | @ -48,69 +49,59 @@ int check_range(int head, uint32_t range) noexcept { | 
			
		
	
		
			
				
					|  |  |  |     return 0; // pass check
 | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  | void demo() { | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  | //    std::cout << __builtin_popcount(~(6 ^ -6)) - 1 << std::endl;
 | 
			
		
	
		
			
				
					|  |  |  | //    return;
 | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |     constexpr std::array<int, 12> heads = { | 
			
		
	
		
			
				
					|  |  |  |         0x0, 0x1, 0x2, | 
			
		
	
		
			
				
					|  |  |  |         0x4, 0x5, 0x6, | 
			
		
	
		
			
				
					|  |  |  |         0x8, 0x9, 0xA, | 
			
		
	
		
			
				
					|  |  |  |         0xC, 0xD, 0xE, | 
			
		
	
		
			
				
					|  |  |  |     }; | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |     auto num = 0; | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |     std::array<std::vector<uint32_t>, 16> data; | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |     // TODO: vector reserve
 | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |     auto basic_ranges = BasicRanges::Instance().Fetch(); | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  | //    std::vector<uint32_t> reversed {basic_ranges.begin(), basic_ranges.end()};
 | 
			
		
	
		
			
				
					|  |  |  | //    for (auto &x : reversed) {
 | 
			
		
	
		
			
				
					|  |  |  | //        x = range_reverse(x);
 | 
			
		
	
		
			
				
					|  |  |  | //    }
 | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |     for (auto head : heads) { | 
			
		
	
		
			
				
					|  |  |  |         for (uint32_t index = 0; index < basic_ranges.size(); ++index) { | 
			
		
	
		
			
				
					|  |  |  |             auto offset = check_range(head, basic_ranges[index]); | 
			
		
	
		
			
				
					|  |  |  |             if (offset) { // invalid case
 | 
			
		
	
		
			
				
					|  |  |  |                 auto tmp = (uint32_t)0b1 << (32 - offset * 2); // distance to next possible range
 | 
			
		
	
		
			
				
					|  |  |  |                 ///         !! <- broken
 | 
			
		
	
		
			
				
					|  |  |  |                 /// ( xx xx xx ) xx xx xx ... [reversed range]
 | 
			
		
	
		
			
				
					|  |  |  |                 ///         +1   00 00 00 ...     (delta)
 | 
			
		
	
		
			
				
					|  |  |  |                 tmp += range_reverse(basic_ranges[index]) & ~(tmp - 1); | 
			
		
	
		
			
				
					|  |  |  |                 while (range_reverse(basic_ranges[++index]) < tmp); // located next range
 | 
			
		
	
		
			
				
					|  |  |  |                 --index; | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  | //                next += reversed[index] & ~(next - 1);
 | 
			
		
	
		
			
				
					|  |  |  | //                auto kk = std::lower_bound(reversed.begin() + index, reversed.end(), next);
 | 
			
		
	
		
			
				
					|  |  |  | //                index = kk - reversed.begin() - 1;
 | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |                 continue; | 
			
		
	
		
			
				
					|  |  |  |             } | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |             data[head].emplace_back(range_reverse(basic_ranges[index])); | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  | //            ++index;
 | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |             ++num; | 
			
		
	
		
			
				
					|  |  |  | //            volatile auto r = range_reverse(basic_ranges[index]); // release valid cases
 | 
			
		
	
		
			
				
					|  |  |  | /// Build all valid ranges of the specified head.
 | 
			
		
	
		
			
				
					|  |  |  | void AllCases::BuildCases(int head, Ranges &basic_ranges, Ranges &release) noexcept { | 
			
		
	
		
			
				
					|  |  |  |     release.reserve(ALL_CASES_NUM[head]); | 
			
		
	
		
			
				
					|  |  |  |     for (uint32_t index = 0; index < basic_ranges.size(); ++index) { | 
			
		
	
		
			
				
					|  |  |  |         auto offset = check_range(head, basic_ranges[index]); | 
			
		
	
		
			
				
					|  |  |  |         if (offset) { // invalid case
 | 
			
		
	
		
			
				
					|  |  |  |             auto tmp = (uint32_t)0b1 << (32 - offset * 2); // distance to next possible range
 | 
			
		
	
		
			
				
					|  |  |  |             ///         !! <- broken
 | 
			
		
	
		
			
				
					|  |  |  |             /// ( xx xx xx ) xx xx xx ... [reversed range]
 | 
			
		
	
		
			
				
					|  |  |  |             ///         +1   00 00 00 ...     (delta)
 | 
			
		
	
		
			
				
					|  |  |  |             tmp += range_reverse(basic_ranges[index]) & ~(tmp - 1); | 
			
		
	
		
			
				
					|  |  |  |             while (range_reverse(basic_ranges[++index]) < tmp); // located next range
 | 
			
		
	
		
			
				
					|  |  |  |             --index; | 
			
		
	
		
			
				
					|  |  |  |             continue; | 
			
		
	
		
			
				
					|  |  |  |         } | 
			
		
	
		
			
				
					|  |  |  |         release.emplace_back(range_reverse(basic_ranges[index])); // release valid case
 | 
			
		
	
		
			
				
					|  |  |  |     } | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  | //    for (auto &x : data) {
 | 
			
		
	
		
			
				
					|  |  |  | //        std::cout << x.size() << std::endl;
 | 
			
		
	
		
			
				
					|  |  |  | //    }
 | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |     for (uint64_t head = 0; head < 15; ++head) { | 
			
		
	
		
			
				
					|  |  |  |         for (auto x : data[head]) { | 
			
		
	
		
			
				
					|  |  |  |             printf("%09llX\n", (head << 32) | x); | 
			
		
	
		
			
				
					|  |  |  | void AllCases::Build() noexcept { | 
			
		
	
		
			
				
					|  |  |  |     if (available_) { // data already available
 | 
			
		
	
		
			
				
					|  |  |  |         return; | 
			
		
	
		
			
				
					|  |  |  |     } | 
			
		
	
		
			
				
					|  |  |  |     if (building_.try_lock()) { // mutex lock success
 | 
			
		
	
		
			
				
					|  |  |  |         if (available_) { | 
			
		
	
		
			
				
					|  |  |  |             building_.unlock(); | 
			
		
	
		
			
				
					|  |  |  |             return; | 
			
		
	
		
			
				
					|  |  |  |         } | 
			
		
	
		
			
				
					|  |  |  |         auto basic_ranges = BasicRanges::Instance().Fetch(); | 
			
		
	
		
			
				
					|  |  |  |         for (auto head : case_heads()) { | 
			
		
	
		
			
				
					|  |  |  |             BuildCases(head, basic_ranges, GetCases()[head]); | 
			
		
	
		
			
				
					|  |  |  |         } | 
			
		
	
		
			
				
					|  |  |  |         available_ = true; | 
			
		
	
		
			
				
					|  |  |  |     } else { | 
			
		
	
		
			
				
					|  |  |  |         building_.lock(); // blocking waiting
 | 
			
		
	
		
			
				
					|  |  |  |     } | 
			
		
	
		
			
				
					|  |  |  |     building_.unlock(); // release mutex
 | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  | //    std::cout << num << std::endl;
 | 
			
		
	
		
			
				
					|  |  |  | AllRanges& AllCases::GetCases() noexcept { | 
			
		
	
		
			
				
					|  |  |  |     static std::array<Ranges, 16> cases; | 
			
		
	
		
			
				
					|  |  |  |     return cases; | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  | AllCases& AllCases::Instance() noexcept { | 
			
		
	
		
			
				
					|  |  |  |     static AllCases instance; | 
			
		
	
		
			
				
					|  |  |  |     return instance; | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  | AllRanges& AllCases::Fetch() noexcept { | 
			
		
	
		
			
				
					|  |  |  |     this->Build(); | 
			
		
	
		
			
				
					|  |  |  |     return GetCases(); | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  | } // namespace cases
 | 
			
		
	
		
			
				
					|  |  |  | } // namespace klotski
 | 
			
		
	
	
		
			
				
					|  |  | 
 |