diff options
Diffstat (limited to 'src/solvers/solutions.h')
| -rw-r--r-- | src/solvers/solutions.h | 224 |
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 @@ | |||
| 1 | STATIC void solution_moves_reset(solution_moves_t [static 1]); | 1 | STATIC void solution_moves_reset(solution_moves_t [static 1]); |
| 2 | STATIC void solution_moves_transform(solution_moves_t [static 1], uint8_t); | 2 | STATIC void solution_moves_transform(solution_moves_t [static 1], size_t, |
| 3 | uint8_t); | ||
| 3 | STATIC void solution_moves_reorient(solution_moves_t [static 1], uint8_t); | 4 | STATIC void solution_moves_reorient(solution_moves_t [static 1], uint8_t); |
| 4 | STATIC bool solution_list_init(solution_list_t [static 1], size_t, char *); | 5 | STATIC bool solution_list_init(solution_list_t [static 1], size_t, char *); |
| 5 | STATIC bool solution_moves_equal( | 6 | STATIC 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]); |
| 7 | STATIC bool solution_moves_is_duplicate(size_t, const solution_moves_t *); | 8 | STATIC bool last_solution_is_duplicate(const solution_list_t [static 1]); |
| 8 | STATIC bool appendchar(solution_list_t [static 1], char); | 9 | STATIC bool appendchar(solution_list_t [static 1], char); |
| 9 | STATIC bool appendnormal( | 10 | STATIC 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]); |
| 11 | STATIC bool appendinverse( | 12 | STATIC 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]); |
| 14 | STATIC 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]); | ||
| 13 | STATIC int64_t appendsolution(const solution_moves_t [static 1], | 18 | STATIC 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]); | ||
| 15 | STATIC bool solutions_done(const solution_list_t [static 1], | 21 | STATIC 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 | ||
| 25 | STATIC void | 31 | STATIC void |
| 26 | solution_moves_transform(solution_moves_t moves[static 1], uint8_t t) | 32 | solution_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 | ||
| 89 | STATIC bool | 95 | STATIC bool |
| 90 | solution_moves_is_duplicate(size_t n, const solution_moves_t *s) | 96 | last_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 | ||
| 153 | STATIC int64_t | 173 | STATIC void |
| 154 | appendsolution( | 174 | appendsolution_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 | ||
| 241 | appendsolution_error_buffer: | 271 | appendsolution_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 | |||
| 277 | STATIC int64_t | ||
| 278 | appendsolution( | ||
| 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 | ||
| 245 | appendsolution_error_solution_length: | 325 | appendsolution_error_solution_length: |
| 246 | list->buf[0] = '\0'; | 326 | list->buf[0] = '\0'; |
