1- #include < iostream>
1+ #include < iostream>
22#include < vector>
33#include < array>
44#include < memory>
@@ -78,37 +78,74 @@ void create_mark_map() {
7878 mark_to_idx[n][0 ] = imark0_val;
7979}
8080
81- // Fast, integer-only scoring function
81+ // Fast, integer-only scoring function (optimized version)
8282inline uFast score (code_t guess, code_t secret) {
8383 if (guess.v == secret.v ) return imark0_val;
84- std::array<uFast, n> g_digits, s_digits;
85- guess.unpack (g_digits);
86- secret.unpack (s_digits);
8784
85+ uFast g_val = guess.v , s_val = secret.v ;
8886 uFast black = 0 ;
89- std::array<uFast, c> g_counts{}, s_counts{};
9087
88+ uFast g_counts[c] = { 0 };
89+ uFast s_counts[c] = { 0 };
90+
91+ // Single pass through positions without intermediate arrays
9192 for (uFast i = 0 ; i < n; ++i) {
92- if (g_digits[i] == s_digits[i]) {
93+ uFast g_digit = g_val % c;
94+ uFast s_digit = s_val % c;
95+
96+ if (g_digit == s_digit) {
9397 black++;
9498 }
9599 else {
96- g_counts[g_digits[i] ]++;
97- s_counts[s_digits[i] ]++;
100+ g_counts[g_digit ]++;
101+ s_counts[s_digit ]++;
98102 }
103+ g_val /= c;
104+ s_val /= c;
99105 }
106+
100107 uFast white = 0 ;
101- for (uFast i = 0 ; i < c; ++i) white += std::min (g_counts[i], s_counts[i]);
108+ for (uFast i = 0 ; i < c; ++i) {
109+ white += std::min (g_counts[i], s_counts[i]);
110+ }
111+
102112 return mark_to_idx[black][white];
103113}
104114
115+ // --- Custom Hash Table for Unique Partition Filtering ---
116+ constexpr uFast HT_SIZE = 2048 ; // Power of two >= s, keeps load factor low
117+ alignas (64 ) uFast ht_key[HT_SIZE];
118+ alignas (64 ) uFast ht_val[HT_SIZE]; // Stores the compact index (1 to k)
119+
120+ // Returns the 1-based compact index for a given key.
121+ // If the key is new, it increments *k_ptr and assigns the new index.
122+ inline uFast find_or_add_signature (uFast key, uFast* k_ptr) {
123+ // Fast multiplicative hash using a golden-ratio-based constant
124+ uFast idx = (key * 0x9e3779b97f4a7c15ULL ) & (HT_SIZE - 1 );
125+
126+ while (true ) {
127+ if (ht_key[idx] == key) { // Found existing key
128+ return ht_val[idx];
129+ }
130+ if (ht_key[idx] == 0 ) { // Found empty slot, this is a new key
131+ ht_key[idx] = key;
132+ (*k_ptr)++;
133+ ht_val[idx] = *k_ptr;
134+ return *k_ptr;
135+ }
136+ // Collision: move to the next slot (linear probing)
137+ idx = (idx + 1 ) & (HT_SIZE - 1 );
138+ }
139+ }
140+
141+
105142int main () {
106143 uFast i, j, k, l, m, p, iv, iv2, o, o2;
107144
108145 const uFast imark0 = imark0_val; // Use the calculated value locally
109146 create_mark_map ();
110147
111- // Heap-allocated storage (from previous step)
148+ // Heap-allocated storage
112149 const uFast S_DIM = s + 1 , IMARK0_DIM = imark0 + 1 ;
113150 auto MapConsistent_ptr = std::make_unique<uFast[]>((maxdepth + 1 ) * S_DIM * IMARK0_DIM);
114151 auto MapConsistent = [&](uFast d1, uFast d2, uFast d3) -> uFast& { return MapConsistent_ptr[d1 * S_DIM * IMARK0_DIM + d2 * IMARK0_DIM + d3]; };
@@ -117,12 +154,13 @@ int main() {
117154 typedef uFast (iValid_)[s + 1 ]; iValid_* iValid = new iValid_[maxdepth + 1 ];
118155
119156 alignas (64 ) static uint16_t Mark[s + 1 ][s + 1 ];
120-
157+
121158 auto RowSum = std::make_unique<uFast[]>(1'000'000 );
122159 typedef uFast (list_)[1'000'000 ]; list_* list = new list_[maxdepth + 1 ];
123160
124161 uFast ubPure[maxdepth + 1 ] = { 0 }, g[maxdepth + 1 ] = { 0 }, r[maxdepth + 1 ] = { 0 }, GuessSum[s + 1 ] = { 0 };
125- uFast guess, sign, lvl = 1 , x = 0 , x0 = 0 , gMin = 0 ;
162+ uFast guess, sign, lvl = 1 , x = 0 , x0 = 0 ;
163+ uFast gMin = -1 ; // Initialize to max value for unsigned
126164 std::array<uFast, s + 1 > MPScore;
127165 static std::array<uFast, imark0 + 1 > mult;
128166 for (i = 0 ; i < imark0; i++) mult[i + 1 ] = uFast (round (pow (16.0 , double (i))));
@@ -144,6 +182,8 @@ int main() {
144182 }
145183 auto mark_end_time = std::chrono::high_resolution_clock::now ();
146184 std::cout << " Mark table computed in " << std::chrono::duration<double >(mark_end_time - mark_start_time).count () << " seconds.\n " ;
185+
186+ // --- Interactive Input ---
147187 std::vector<uFast> initial_guesses, initial_signs;
148188 uFast input_count = 1 ;
149189 std::cout << " \n " ;
@@ -178,7 +218,7 @@ int main() {
178218 std::cout << " \n " << kk << " possible solutions!\n\n " << " Calculating...\n\n " ;
179219 if (kk == 1 ) {
180220 RowSum[1 ] = 1 ;
181- list[1 ][1 ] = Valids[iValid[1 ][1 ]].to_decimal () * 100 + 40 ;
221+ list[1 ][1 ] = Valids[iValid[1 ][1 ]].to_decimal () * 100 + iMark[imark0] ;
182222 goto finished;
183223 }
184224
@@ -190,6 +230,7 @@ int main() {
190230 }
191231
192232GetNext:
233+ // --- Partition Calculation (Original, Fast Version) ---
193234 memset (&MapConsistent (lvl, 0 , 0 ), 0 , sizeof (uFast) * S_DIM * IMARK0_DIM);
194235 uFast q[s + 1 ] = { 0 };
195236 k = lvl - 1 ;
@@ -198,50 +239,63 @@ int main() {
198239 iv2 = iValid[lvl][2 ];
199240 for (i = 1 ; i <= s; i++) {
200241 o = Mark[i][iv]; o2 = Mark[i][iv2];
201- MapConsistent (lvl, i, o)++; // vectorized for speed
242+ MapConsistent (lvl, i, o)++;
202243 MapConsistent (lvl, i, o2)++;
203- q[i] += ((o * mult[o]) + (o2 * mult[o2]));
244+ q[i] += (mult[o] + mult[o2]); // Simplified from original for clarity
204245 }
205246 for (j = 3 ; j <= l; j++) {
206247 iv = iValid[lvl][j];
207248 for (i = 1 ; i <= s; i++) {
208249 o = Mark[i][iv];
209250 MapConsistent (lvl, i, o)++;
210- q[i] += (o * mult[o]) ;
251+ q[i] += mult[o];
211252 }
212253 }
254+
255+ // --- High-performance filtering using a custom hash table ---
256+ std::fill_n (ht_key, HT_SIZE, 0 );
213257 k = 0 ;
214258 for (i = 1 ; i <= s; i++) {
215- k++; iFiltered[lvl][k] = i;
216- q[k] = q[i];
217- for (m = 1 ; m < k; m++) if (q[m] == q[k]) {
218- k--;
219- break ;
259+ uFast unique_idx = find_or_add_signature (q[i], &k);
260+ if (unique_idx == k) { // True only for the first time this signature is seen
261+ iFiltered[lvl][k] = i;
220262 }
221263 }
264+
265+ // --- Compaction and Scoring ---
222266 for (i = 1 ; i <= k; i++) {
223267 MPScore[i] = 0 ;
268+ uFast original_idx = iFiltered[lvl][i];
224269 for (j = 1 ; j <= imark0; j++) {
225- MapConsistent (lvl, i, j) = MapConsistent (lvl, iFiltered[lvl][i], j);
226- MPScore[i] += MapConsistent (lvl, i, j) != 0 ;
270+ uFast count = MapConsistent (lvl, original_idx, j);
271+ MapConsistent (lvl, i, j) = count;
272+ if (count != 0 ) {
273+ MPScore[i]++;
274+ }
227275 }
228- q[i] = MPScore[i];
276+ q[i] = MPScore[i]; // Reuse q[] to hold the score
229277 }
230- // concurrency::parallel_buffered_sort from ppl.h is non-standard. std::sort is faster for small sizes.
278+
231279 std::sort (&MPScore[1 ], &MPScore[k + 1 ]);
232280
233- // l = (k > 133) ? k - 16 : 0.88 * k; //correct formula for all combinations
234- l = (k > 119 ) ? k - 8 : 0.88 * k;// works with (4,6)
281+ // --- Heuristic Pruning ---
282+ l = (k > 119 ) ? k - 8 : 0.88 * k;
235283
236284 m = 0 ;
237285 for (i = 1 ; i <= k; i++) {
238286 if (q[i] >= MPScore[l]) {
239287 m++;
240- iFiltered[lvl][m] = iFiltered[lvl][i];
241- for (j = 1 ; j <= imark0; j++) MapConsistent (lvl, m, j) = MapConsistent (lvl, i, j);
288+ // This compaction is now safe because iFiltered[lvl][i] holds the original index
289+ uFast original_idx = iFiltered[lvl][i];
290+ iFiltered[lvl][m] = original_idx;
291+ for (j = 1 ; j <= imark0; j++) {
292+ MapConsistent (lvl, m, j) = MapConsistent (lvl, i, j);
293+ }
242294 }
243295 }
244296 ubPure[lvl] = m;
297+
298+ // --- Main DFS Loop ---
245299 while (lvl) {
246300 for (guess = g[lvl]; guess <= ubPure[lvl]; guess++) {
247301 for (sign = r[lvl]; sign <= imark0; sign++) {
@@ -279,24 +333,24 @@ int main() {
279333 goto GetNext;
280334 }
281335 if ((g[lvl] != 1 ) && (ubPure[lvl] == g[lvl])) {
282- gMin = -1 ;
336+ gMin = -1 ; // Max uFast value
283337 l = lvl - 1 ;
284338 p = MapConsistent (l, g[l], r[l]);
285339 x0 = x - (ubPure[lvl] * p);
286340 for (i = 1 ; i <= ubPure[lvl]; i++) {
287- int sum = 0 ;
288- int baseOffset = x0 + (i - 1 ) * p;
341+ uFast sum = 0 ;
342+ uFast baseOffset = x0 + (i - 1 ) * p;
289343 for (j = 1 ; j <= p; j++) sum += RowSum[baseOffset + j];
290344 GuessSum[i] = sum;
291345 if (sum < gMin ) {
292346 gMin = sum;
293347 k = i;
294348 }
295349 }
296- int sourceBase = x0 + (k - 1 ) * p;
350+ uFast sourceBase = x0 + (k - 1 ) * p;
297351 for (i = 1 ; i <= p; i++) {
298- int destIndex = x0 + i;
299- int sourceIndex = sourceBase + i;
352+ uFast destIndex = x0 + i;
353+ uFast sourceIndex = sourceBase + i;
300354 RowSum[destIndex] = RowSum[sourceIndex];
301355 for (j = 1 ; j < maxdepth; j++) list[j][destIndex] = list[j][sourceIndex];
302356 }
0 commit comments