aboutsummaryrefslogtreecommitdiff
path: root/src/solvers/solutions.h
diff options
context:
space:
mode:
authorSebastiano Tronto <sebastiano@tronto.net>2025-11-25 18:03:50 +0100
committerSebastiano Tronto <sebastiano@tronto.net>2025-11-25 18:03:50 +0100
commit4d0b1a53f04f1c5bc95de20fb92b2bd9cf16a895 (patch)
tree5294d9b3655031535085a5163c2c5b5cbe7413b1 /src/solvers/solutions.h
parent3ca4814562f6c756e9a110dab7d54cda30267fb1 (diff)
parent147b0c3c4615c32478a4923242909b8ae5a30d03 (diff)
downloadnissy-core-4d0b1a53f04f1c5bc95de20fb92b2bd9cf16a895.tar.gz
nissy-core-4d0b1a53f04f1c5bc95de20fb92b2bd9cf16a895.zip
Merge branch 'master' of tronto.net:nissy-core
Diffstat (limited to 'src/solvers/solutions.h')
-rw-r--r--src/solvers/solutions.h224
1 files changed, 152 insertions, 72 deletions
diff --git a/src/solvers/solutions.h b/src/solvers/solutions.h
index 9f209d1..d92d5ad 100644
--- a/src/solvers/solutions.h
+++ b/src/solvers/solutions.h
@@ -1,17 +1,23 @@
1STATIC void solution_moves_reset(solution_moves_t [static 1]); 1STATIC void solution_moves_reset(solution_moves_t [static 1]);
2STATIC void solution_moves_transform(solution_moves_t [static 1], uint8_t); 2STATIC void solution_moves_transform(solution_moves_t [static 1], size_t,
3 uint8_t);
3STATIC void solution_moves_reorient(solution_moves_t [static 1], uint8_t); 4STATIC void solution_moves_reorient(solution_moves_t [static 1], uint8_t);
4STATIC bool solution_list_init(solution_list_t [static 1], size_t, char *); 5STATIC bool solution_list_init(solution_list_t [static 1], size_t, char *);
5STATIC bool solution_moves_equal( 6STATIC bool solution_moves_equal(
6 const solution_moves_t [static 1], const solution_moves_t [static 1]); 7 const solution_moves_t [static 1], const solution_moves_t [static 1]);
7STATIC bool solution_moves_is_duplicate(size_t, const solution_moves_t *); 8STATIC bool last_solution_is_duplicate(const solution_list_t [static 1]);
8STATIC bool appendchar(solution_list_t [static 1], char); 9STATIC bool appendchar(solution_list_t [static 1], char);
9STATIC bool appendnormal( 10STATIC bool appendnormal(
10 const solution_moves_t [static 1], solution_list_t [static 1]); 11 const solution_moves_t [static 1], solution_list_t [static 1]);
11STATIC bool appendinverse( 12STATIC bool appendinverse(
12 const solution_moves_t [static 1], solution_list_t [static 1]); 13 const solution_moves_t [static 1], solution_list_t [static 1]);
14STATIC void appendsolution_dfs(const solution_moves_t [static 1], size_t,
15 const uint64_t *, size_t, uint8_t *, const solution_settings_t [static 1],
16 solution_list_t [static 1],
17 solution_moves_t [static NTRANS * SOLUTION_MAXLEN], int64_t [static 1]);
13STATIC int64_t appendsolution(const solution_moves_t [static 1], 18STATIC int64_t appendsolution(const solution_moves_t [static 1],
14 const solution_settings_t [static 1], solution_list_t [static 1]); 19 size_t, const uint64_t *, const solution_settings_t [static 1],
20 solution_list_t [static 1]);
15STATIC bool solutions_done(const solution_list_t [static 1], 21STATIC bool solutions_done(const solution_list_t [static 1],
16 const solution_settings_t [static 1], int8_t depth); 22 const solution_settings_t [static 1], int8_t depth);
17 23
@@ -23,11 +29,11 @@ solution_moves_reset(solution_moves_t sol[static 1])
23} 29}
24 30
25STATIC void 31STATIC void
26solution_moves_transform(solution_moves_t moves[static 1], uint8_t t) 32solution_moves_transform(solution_moves_t moves[static 1], size_t z, uint8_t t)
27{ 33{
28 uint8_t i; 34 uint8_t i;
29 35
30 for (i = 0; i < moves->nmoves; i++) 36 for (i = z; i < moves->nmoves; i++)
31 moves->moves[i] = transform_move(moves->moves[i], t); 37 moves->moves[i] = transform_move(moves->moves[i], t);
32 38
33 for (i = 0; i < moves->npremoves; i++) 39 for (i = 0; i < moves->npremoves; i++)
@@ -87,13 +93,27 @@ solution_moves_equal(
87} 93}
88 94
89STATIC bool 95STATIC bool
90solution_moves_is_duplicate(size_t n, const solution_moves_t *s) 96last_solution_is_duplicate(const solution_list_t l[static 1])
91{ 97{
92 size_t i; 98 size_t i, j;
99
100 if (l->nsols == 1)
101 return false;
93 102
94 for (i = 0; i < n; i++) 103 /* We assume the list is newline-terminated */
95 if (solution_moves_equal(&s[i], &s[n])) 104 j = l->used-2;
96 return true; 105 while (true) {
106 for ( ; l->buf[j] != '\n'; j--)
107 if (j == 0) return false;
108 j--;
109 for (i = l->used-2; l->buf[i] == l->buf[j]; i--, j--) {
110 if (l->buf[i-1] == '\n') {
111 if (l->buf[j-1] == '\n' || j == 0)
112 return true;
113 else break;
114 }
115 }
116 }
97 117
98 return false; 118 return false;
99} 119}
@@ -150,97 +170,157 @@ appendinverse(
150 return appendchar(list, ')'); 170 return appendchar(list, ')');
151} 171}
152 172
153STATIC int64_t 173STATIC void
154appendsolution( 174appendsolution_dfs(
155 const solution_moves_t moves[static 1], 175 const solution_moves_t moves[static 1],
176 size_t ntmask,
177 const uint64_t *tmask,
178 size_t itm,
179 uint8_t *tt,
156 const solution_settings_t settings[static 1], 180 const solution_settings_t settings[static 1],
157 solution_list_t list[static 1] 181 solution_list_t list[static 1],
182 solution_moves_t tsol[static NTRANS * SOLUTION_MAXLEN],
183 int64_t r[static 1]
158) 184)
159{ 185{
160 int64_t r; 186 /*
161 int i; 187 The logic here is quit complex because we have to address H48
162 uint8_t t; 188 solutions that may be reduced by symmetry in the first few moves.
163 solution_moves_t tsol[NTRANS]; 189 */
164 190
165 if (moves->nmoves + moves->npremoves > SOLUTION_MAXLEN) 191 size_t i, last_start;
166 goto appendsolution_error_solution_length; 192 uint8_t t;
193 solution_moves_t moves_copy;
167 194
168 for ( 195 if (list->nsols >= settings->maxsolutions)
169 t = 0, r = 0; 196 return;
170 t < NTRANS && list->nsols < settings->maxsolutions;
171 t++
172 ) {
173 if (!(settings->tmask & TM_SINGLE(t)))
174 continue;
175 197
176 tsol[r] = *moves; 198 if (ntmask == itm) {
177 if (settings->unniss) { 199 tsol[*r] = *moves;
178 tsol[r].nmoves += moves->npremoves;
179 tsol[r].npremoves = 0;
180 for (i = moves->npremoves-1; i >= 0; i--)
181 tsol[r].moves[tsol[r].nmoves - i - 1] =
182 inverse_move(moves->premoves[i]);
183 200
184 /* 201 for (i = ntmask; i > 0; i--)
185 This is a bit ugly: we have to sort now and then again 202 solution_moves_transform(&tsol[*r], i-1, tt[i-1]);
186 later, because the allowedmoves check would fail with
187 improperly sorted parallel moves, but then transforming
188 could swap the pairs the wrong way around.
189 */
190 sortparallel_moves(tsol[r].nmoves, tsol[r].moves);
191 203
192 /* Check if unnissed premoves cancel with normal. */ 204 solution_moves_reorient(&tsol[*r], settings->orientation);
193 if (!allowedmoves(tsol[r].nmoves, tsol[r].moves)) 205 sortparallel_moves(tsol[*r].nmoves, tsol[*r].moves);
194 continue; 206 sortparallel_moves(tsol[*r].npremoves, tsol[*r].premoves);
195 }
196 solution_moves_transform(&tsol[r], t);
197 solution_moves_reorient(&tsol[r], settings->orientation);
198 sortparallel_moves(tsol[r].nmoves, tsol[r].moves);
199 sortparallel_moves(tsol[r].npremoves, tsol[r].premoves);
200 207
201 /* Skip duplicates that may appear after transforming */ 208 last_start = list->used;
202 if (solution_moves_is_duplicate(r, tsol))
203 continue;
204 209
205 /* Append first the moves on the side that has more */ 210 /* Append first the moves on the side that has more */
206 /* E.g. write (U L F) B instead of B (U L F) */ 211 /* E.g. write (U L F) B instead of B (U L F) */
207 if (tsol[r].nmoves >= tsol[r].npremoves) { 212 if (tsol[*r].nmoves >= tsol[*r].npremoves) {
208 if (!appendnormal(&tsol[r], list)) 213 if (!appendnormal(&tsol[*r], list))
209 goto appendsolution_error_buffer; 214 goto appendsolution_dfs_error_buffer;
210 215
211 if (tsol[r].nmoves > 0 && tsol[r].npremoves > 0) 216 if (tsol[*r].nmoves > 0 && tsol[*r].npremoves > 0)
212 if (!appendchar(list, ' ')) 217 if (!appendchar(list, ' '))
213 return false; 218 goto appendsolution_dfs_error_buffer;
214 219
215 if (!appendinverse(&tsol[r], list)) 220 if (!appendinverse(&tsol[*r], list))
216 goto appendsolution_error_buffer; 221 goto appendsolution_dfs_error_buffer;
217 } else { 222 } else {
218 if (!appendinverse(&tsol[r], list)) 223 if (!appendinverse(&tsol[*r], list))
219 goto appendsolution_error_buffer; 224 goto appendsolution_dfs_error_buffer;
220 225
221 if (tsol[r].nmoves > 0 && tsol[r].npremoves > 0) 226 if (tsol[*r].nmoves > 0 && tsol[*r].npremoves > 0)
222 if (!appendchar(list, ' ')) 227 if (!appendchar(list, ' '))
223 return false; 228 goto appendsolution_dfs_error_buffer;
224 229
225 if (!appendnormal(&tsol[r], list)) 230 if (!appendnormal(&tsol[*r], list))
226 goto appendsolution_error_buffer; 231 goto appendsolution_dfs_error_buffer;
227 } 232 }
228 233
229 if (!appendchar(list, '\n')) 234 if (!appendchar(list, '\n'))
230 goto appendsolution_error_buffer; 235 goto appendsolution_dfs_error_buffer;
231
232 ++list->nsols; 236 ++list->nsols;
237
238 /*
239 Normaly, it would be enough to check for duplicates in the
240 current "pack" of transformation-equivalent solutions.
241 However, in rare cases, the H48 solver may produce equivalent
242 "packs" of solutions. It would be more elegant to filter out
243 the corresponding tasks in solve_h48_maketasks(), but doing so
244 is not trivial. In the end, duplicate solutions are never
245 desirable, so we might as well do this clean up here.
246 */
247 if (last_solution_is_duplicate(list)) {
248 --list->nsols;
249 list->used = last_start;
250 return;
251 }
252
233 list->shortest_sol = MIN( 253 list->shortest_sol = MIN(
234 list->shortest_sol, tsol[r].nmoves + tsol[r].npremoves); 254 list->shortest_sol, tsol[*r].nmoves + tsol[*r].npremoves);
235 r++; 255 (*r)++;
256 } else {
257 for (t = 0; t < NTRANS; t++) {
258 if (!(tmask[itm] & TM_SINGLE(t)))
259 continue;
260 moves_copy = *moves;
261 tt[itm] = t;
262 appendsolution_dfs(&moves_copy, ntmask, tmask,
263 itm+1, tt, settings, list, tsol, r);
264 if (*r < 0)
265 return;
266 }
236 } 267 }
237 268
238 list->buf[list->used] = '\0'; 269 return;
239 return r;
240 270
241appendsolution_error_buffer: 271appendsolution_dfs_error_buffer:
242 list->buf[0] = '\0'; 272 list->buf[0] = '\0';
243 return NISSY_ERROR_BUFFER_SIZE; 273 *r = NISSY_ERROR_BUFFER_SIZE;
274 return;
275}
276
277STATIC int64_t
278appendsolution(
279 const solution_moves_t moves[static 1],
280 size_t ntmask,
281 const uint64_t *tmask,
282 const solution_settings_t settings[static 1],
283 solution_list_t list[static 1]
284)
285{
286 int64_t r;
287 int i;
288 uint8_t tt[SOLUTION_MAXLEN];
289 solution_moves_t moves_copy, tsol[NTRANS * SOLUTION_MAXLEN];
290
291 if (moves->nmoves + moves->npremoves > SOLUTION_MAXLEN)
292 goto appendsolution_error_solution_length;
293
294 moves_copy = *moves;
295 if (settings->unniss) {
296 moves_copy.nmoves += moves->npremoves;
297 moves_copy.npremoves = 0;
298 for (i = moves->npremoves-1; i >= 0; i--)
299 moves_copy.moves[moves_copy.nmoves - i - 1] =
300 inverse_move(moves->premoves[i]);
301
302 /*
303 This is a bit ugly: we have to sort now and then again
304 later, because the allowedmoves check would fail with
305 improperly sorted parallel moves, but then transforming
306 could swap the pairs the wrong way around.
307 */
308 sortparallel_moves(moves_copy.nmoves, moves_copy.moves);
309
310 /* Check if unnissed premoves cancel with normal. */
311 if (!allowedmoves(moves_copy.nmoves, moves_copy.moves))
312 return 0;
313 }
314
315 r = 0;
316 memset(tt, TRANS_UFr, SOLUTION_MAXLEN);
317 appendsolution_dfs(
318 &moves_copy, ntmask, tmask, 0, tt, settings, list, tsol, &r);
319 if (r < 0)
320 return r;
321
322 list->buf[list->used] = '\0';
323 return r;
244 324
245appendsolution_error_solution_length: 325appendsolution_error_solution_length:
246 list->buf[0] = '\0'; 326 list->buf[0] = '\0';

Generated with cgit - Back to sebastiano.tronto.net