A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita audio rust zig deno mpris rockbox mpd
at master 540 lines 22 kB view raw
1/* 2 * loopgen.c: loop generation functions for grid.[ch]. 3 */ 4 5#include <stdio.h> 6#include <stdlib.h> 7#include <stddef.h> 8#include <string.h> 9#include <assert.h> 10#include <ctype.h> 11#ifdef NO_TGMATH_H 12# include <math.h> 13#else 14# include <tgmath.h> 15#endif 16 17#include "puzzles.h" 18#include "tree234.h" 19#include "grid.h" 20#include "loopgen.h" 21 22 23/* We're going to store lists of current candidate faces for colouring black 24 * or white. 25 * Each face gets a 'score', which tells us how adding that face right 26 * now would affect the curliness of the solution loop. We're trying to 27 * maximise that quantity so will bias our random selection of faces to 28 * colour those with high scores */ 29struct face_score { 30 int white_score; 31 int black_score; 32 unsigned long random; 33 /* No need to store a grid_face* here. The 'face_scores' array will 34 * be a list of 'face_score' objects, one for each face of the grid, so 35 * the position (index) within the 'face_scores' array will determine 36 * which face corresponds to a particular face_score. 37 * Having a single 'face_scores' array for all faces simplifies memory 38 * management, and probably improves performance, because we don't have to 39 * malloc/free each individual face_score, and we don't have to maintain 40 * a mapping from grid_face* pointers to face_score* pointers. 41 */ 42}; 43 44static int generic_sort_cmpfn(void *v1, void *v2, size_t offset) 45{ 46 struct face_score *f1 = v1; 47 struct face_score *f2 = v2; 48 int r; 49 50 r = *(int *)((char *)f2 + offset) - *(int *)((char *)f1 + offset); 51 if (r) { 52 return r; 53 } 54 55 if (f1->random < f2->random) 56 return -1; 57 else if (f1->random > f2->random) 58 return 1; 59 60 /* 61 * It's _just_ possible that two faces might have been given 62 * the same random value. In that situation, fall back to 63 * comparing based on the positions within the face_scores list. 64 * This introduces a tiny directional bias, but not a significant one. 65 */ 66 return f1 - f2; 67} 68 69static int white_sort_cmpfn(void *v1, void *v2) 70{ 71 return generic_sort_cmpfn(v1, v2, offsetof(struct face_score,white_score)); 72} 73 74static int black_sort_cmpfn(void *v1, void *v2) 75{ 76 return generic_sort_cmpfn(v1, v2, offsetof(struct face_score,black_score)); 77} 78 79/* 'board' is an array of enum face_colour, indicating which faces are 80 * currently black/white/grey. 'colour' is FACE_WHITE or FACE_BLACK. 81 * Returns whether it's legal to colour the given face with this colour. */ 82static bool can_colour_face(grid *g, char* board, int face_index, 83 enum face_colour colour) 84{ 85 int i, j; 86 grid_face *test_face = g->faces[face_index]; 87 grid_face *starting_face, *current_face; 88 grid_dot *starting_dot; 89 int transitions; 90 bool current_state, s; /* equal or not-equal to 'colour' */ 91 bool found_same_coloured_neighbour = false; 92 assert(board[face_index] != colour); 93 94 /* Can only consider a face for colouring if it's adjacent to a face 95 * with the same colour. */ 96 for (i = 0; i < test_face->order; i++) { 97 grid_edge *e = test_face->edges[i]; 98 grid_face *f = (e->face1 == test_face) ? e->face2 : e->face1; 99 if (FACE_COLOUR(f) == colour) { 100 found_same_coloured_neighbour = true; 101 break; 102 } 103 } 104 if (!found_same_coloured_neighbour) 105 return false; 106 107 /* Need to avoid creating a loop of faces of this colour around some 108 * differently-coloured faces. 109 * Also need to avoid meeting a same-coloured face at a corner, with 110 * other-coloured faces in between. Here's a simple test that (I believe) 111 * takes care of both these conditions: 112 * 113 * Take the circular path formed by this face's edges, and inflate it 114 * slightly outwards. Imagine walking around this path and consider 115 * the faces that you visit in sequence. This will include all faces 116 * touching the given face, either along an edge or just at a corner. 117 * Count the number of 'colour'/not-'colour' transitions you encounter, as 118 * you walk along the complete loop. This will obviously turn out to be 119 * an even number. 120 * If 0, we're either in the middle of an "island" of this colour (should 121 * be impossible as we're not supposed to create black or white loops), 122 * or we're about to start a new island - also not allowed. 123 * If 4 or greater, there are too many separate coloured regions touching 124 * this face, and colouring it would create a loop or a corner-violation. 125 * The only allowed case is when the count is exactly 2. */ 126 127 /* i points to a dot around the test face. 128 * j points to a face around the i^th dot. 129 * The current face will always be: 130 * test_face->dots[i]->faces[j] 131 * We assume dots go clockwise around the test face, 132 * and faces go clockwise around dots. */ 133 134 /* 135 * The end condition is slightly fiddly. In sufficiently strange 136 * degenerate grids, our test face may be adjacent to the same 137 * other face multiple times (typically if it's the exterior 138 * face). Consider this, in particular: 139 * 140 * +--+ 141 * | | 142 * +--+--+ 143 * | | | 144 * +--+--+ 145 * 146 * The bottom left face there is adjacent to the exterior face 147 * twice, so we can't just terminate our iteration when we reach 148 * the same _face_ we started at. Furthermore, we can't 149 * condition on having the same (i,j) pair either, because 150 * several (i,j) pairs identify the bottom left contiguity with 151 * the exterior face! We canonicalise the (i,j) pair by taking 152 * one step around before we set the termination tracking. 153 */ 154 155 i = j = 0; 156 current_face = test_face->dots[0]->faces[0]; 157 if (current_face == test_face) { 158 j = 1; 159 current_face = test_face->dots[0]->faces[1]; 160 } 161 transitions = 0; 162 current_state = (FACE_COLOUR(current_face) == colour); 163 starting_dot = NULL; 164 starting_face = NULL; 165 while (true) { 166 /* Advance to next face. 167 * Need to loop here because it might take several goes to 168 * find it. */ 169 while (true) { 170 j++; 171 if (j == test_face->dots[i]->order) 172 j = 0; 173 174 if (test_face->dots[i]->faces[j] == test_face) { 175 /* Advance to next dot round test_face, then 176 * find current_face around new dot 177 * and advance to the next face clockwise */ 178 i++; 179 if (i == test_face->order) 180 i = 0; 181 for (j = 0; j < test_face->dots[i]->order; j++) { 182 if (test_face->dots[i]->faces[j] == current_face) 183 break; 184 } 185 /* Must actually find current_face around new dot, 186 * or else something's wrong with the grid. */ 187 assert(j != test_face->dots[i]->order); 188 /* Found, so advance to next face and try again */ 189 } else { 190 break; 191 } 192 } 193 /* (i,j) are now advanced to next face */ 194 current_face = test_face->dots[i]->faces[j]; 195 s = (FACE_COLOUR(current_face) == colour); 196 if (!starting_dot) { 197 starting_dot = test_face->dots[i]; 198 starting_face = current_face; 199 current_state = s; 200 } else { 201 if (s != current_state) { 202 ++transitions; 203 current_state = s; 204 if (transitions > 2) 205 break; 206 } 207 if (test_face->dots[i] == starting_dot && 208 current_face == starting_face) 209 break; 210 } 211 } 212 213 return (transitions == 2) ? true : false; 214} 215 216/* Count the number of neighbours of 'face', having colour 'colour' */ 217static int face_num_neighbours(grid *g, char *board, grid_face *face, 218 enum face_colour colour) 219{ 220 int colour_count = 0; 221 int i; 222 grid_face *f; 223 grid_edge *e; 224 for (i = 0; i < face->order; i++) { 225 e = face->edges[i]; 226 f = (e->face1 == face) ? e->face2 : e->face1; 227 if (FACE_COLOUR(f) == colour) 228 ++colour_count; 229 } 230 return colour_count; 231} 232 233/* The 'score' of a face reflects its current desirability for selection 234 * as the next face to colour white or black. We want to encourage moving 235 * into grey areas and increasing loopiness, so we give scores according to 236 * how many of the face's neighbours are currently coloured the same as the 237 * proposed colour. */ 238static int face_score(grid *g, char *board, grid_face *face, 239 enum face_colour colour) 240{ 241 /* Simple formula: score = 0 - num. same-coloured neighbours, 242 * so a higher score means fewer same-coloured neighbours. */ 243 return -face_num_neighbours(g, board, face, colour); 244} 245 246/* 247 * Generate a new complete random closed loop for the given grid. 248 * 249 * The method is to generate a WHITE/BLACK colouring of all the faces, 250 * such that the WHITE faces will define the inside of the path, and the 251 * BLACK faces define the outside. 252 * To do this, we initially colour all faces GREY. The infinite space outside 253 * the grid is coloured BLACK, and we choose a random face to colour WHITE. 254 * Then we gradually grow the BLACK and the WHITE regions, eliminating GREY 255 * faces, until the grid is filled with BLACK/WHITE. As we grow the regions, 256 * we avoid creating loops of a single colour, to preserve the topological 257 * shape of the WHITE and BLACK regions. 258 * We also try to make the boundary as loopy and twisty as possible, to avoid 259 * generating paths that are uninteresting. 260 * The algorithm works by choosing a BLACK/WHITE colour, then choosing a GREY 261 * face that can be coloured with that colour (without violating the 262 * topological shape of that region). It's not obvious, but I think this 263 * algorithm is guaranteed to terminate without leaving any GREY faces behind. 264 * Indeed, if there are any GREY faces at all, both the WHITE and BLACK 265 * regions can be grown. 266 * This is checked using assert()ions, and I haven't seen any failures yet. 267 * 268 * Hand-wavy proof: imagine what can go wrong... 269 * 270 * Could the white faces get completely cut off by the black faces, and still 271 * leave some grey faces remaining? 272 * No, because then the black faces would form a loop around both the white 273 * faces and the grey faces, which is disallowed because we continually 274 * maintain the correct topological shape of the black region. 275 * Similarly, the black faces can never get cut off by the white faces. That 276 * means both the WHITE and BLACK regions always have some room to grow into 277 * the GREY regions. 278 * Could it be that we can't colour some GREY face, because there are too many 279 * WHITE/BLACK transitions as we walk round the face? (see the 280 * can_colour_face() function for details) 281 * No. Imagine otherwise, and we see WHITE/BLACK/WHITE/BLACK as we walk 282 * around the face. The two WHITE faces would be connected by a WHITE path, 283 * and the BLACK faces would be connected by a BLACK path. These paths would 284 * have to cross, which is impossible. 285 * Another thing that could go wrong: perhaps we can't find any GREY face to 286 * colour WHITE, because it would create a loop-violation or a corner-violation 287 * with the other WHITE faces? 288 * This is a little bit tricky to prove impossible. Imagine you have such a 289 * GREY face (that is, if you coloured it WHITE, you would create a WHITE loop 290 * or corner violation). 291 * That would cut all the non-white area into two blobs. One of those blobs 292 * must be free of BLACK faces (because the BLACK stuff is a connected blob). 293 * So we have a connected GREY area, completely surrounded by WHITE 294 * (including the GREY face we've tentatively coloured WHITE). 295 * A well-known result in graph theory says that you can always find a GREY 296 * face whose removal leaves the remaining GREY area connected. And it says 297 * there are at least two such faces, so we can always choose the one that 298 * isn't the "tentative" GREY face. Colouring that face WHITE leaves 299 * everything nice and connected, including that "tentative" GREY face which 300 * acts as a gateway to the rest of the non-WHITE grid. 301 */ 302void generate_loop(grid *g, char *board, random_state *rs, 303 loopgen_bias_fn_t bias, void *biasctx) 304{ 305 int i, j; 306 int num_faces = g->num_faces; 307 struct face_score *face_scores; /* Array of face_score objects */ 308 struct face_score *fs; /* Points somewhere in the above list */ 309 struct grid_face *cur_face; 310 tree234 *lightable_faces_sorted; 311 tree234 *darkable_faces_sorted; 312 int *face_list; 313 bool do_random_pass; 314 315 /* Make a board */ 316 memset(board, FACE_GREY, num_faces); 317 318 /* Create and initialise the list of face_scores */ 319 face_scores = snewn(num_faces, struct face_score); 320 for (i = 0; i < num_faces; i++) { 321 face_scores[i].random = random_bits(rs, 31); 322 face_scores[i].black_score = face_scores[i].white_score = 0; 323 } 324 325 /* Colour a random, finite face white. The infinite face is implicitly 326 * coloured black. Together, they will seed the random growth process 327 * for the black and white areas. */ 328 i = random_upto(rs, num_faces); 329 board[i] = FACE_WHITE; 330 331 /* We need a way of favouring faces that will increase our loopiness. 332 * We do this by maintaining a list of all candidate faces sorted by 333 * their score and choose randomly from that with appropriate skew. 334 * In order to avoid consistently biasing towards particular faces, we 335 * need the sort order _within_ each group of scores to be completely 336 * random. But it would be abusing the hospitality of the tree234 data 337 * structure if our comparison function were nondeterministic :-). So with 338 * each face we associate a random number that does not change during a 339 * particular run of the generator, and use that as a secondary sort key. 340 * Yes, this means we will be biased towards particular random faces in 341 * any one run but that doesn't actually matter. */ 342 343 lightable_faces_sorted = newtree234(white_sort_cmpfn); 344 darkable_faces_sorted = newtree234(black_sort_cmpfn); 345 346 /* Initialise the lists of lightable and darkable faces. This is 347 * slightly different from the code inside the while-loop, because we need 348 * to check every face of the board (the grid structure does not keep a 349 * list of the infinite face's neighbours). */ 350 for (i = 0; i < num_faces; i++) { 351 grid_face *f = g->faces[i]; 352 struct face_score *fs = face_scores + i; 353 if (board[i] != FACE_GREY) continue; 354 /* We need the full colourability check here, it's not enough simply 355 * to check neighbourhood. On some grids, a neighbour of the infinite 356 * face is not necessarily darkable. */ 357 if (can_colour_face(g, board, i, FACE_BLACK)) { 358 fs->black_score = face_score(g, board, f, FACE_BLACK); 359 add234(darkable_faces_sorted, fs); 360 } 361 if (can_colour_face(g, board, i, FACE_WHITE)) { 362 fs->white_score = face_score(g, board, f, FACE_WHITE); 363 add234(lightable_faces_sorted, fs); 364 } 365 } 366 367 /* Colour faces one at a time until no more faces are colourable. */ 368 while (true) 369 { 370 enum face_colour colour; 371 tree234 *faces_to_pick; 372 int c_lightable = count234(lightable_faces_sorted); 373 int c_darkable = count234(darkable_faces_sorted); 374 if (c_lightable == 0 && c_darkable == 0) { 375 /* No more faces we can use at all. */ 376 break; 377 } 378 assert(c_lightable != 0 && c_darkable != 0); 379 380 /* Choose a colour, and colour the best available face 381 * with that colour. */ 382 colour = random_upto(rs, 2) ? FACE_WHITE : FACE_BLACK; 383 384 if (colour == FACE_WHITE) 385 faces_to_pick = lightable_faces_sorted; 386 else 387 faces_to_pick = darkable_faces_sorted; 388 if (bias) { 389 /* 390 * Go through all the candidate faces and pick the one the 391 * bias function likes best, breaking ties using the 392 * ordering in our tree234 (which is why we replace only 393 * if score > bestscore, not >=). 394 */ 395 int j, k; 396 struct face_score *best = NULL; 397 int score, bestscore = 0; 398 399 for (j = 0; 400 (fs = (struct face_score *)index234(faces_to_pick, j))!=NULL; 401 j++) { 402 403 assert(fs); 404 k = fs - face_scores; 405 assert(board[k] == FACE_GREY); 406 board[k] = colour; 407 score = bias(biasctx, board, k); 408 board[k] = FACE_GREY; 409 bias(biasctx, board, k); /* let bias know we put it back */ 410 411 if (!best || score > bestscore) { 412 bestscore = score; 413 best = fs; 414 } 415 } 416 fs = best; 417 } else { 418 fs = (struct face_score *)index234(faces_to_pick, 0); 419 } 420 assert(fs); 421 i = fs - face_scores; 422 assert(board[i] == FACE_GREY); 423 board[i] = colour; 424 if (bias) 425 bias(biasctx, board, i); /* notify bias function of the change */ 426 427 /* Remove this newly-coloured face from the lists. These lists should 428 * only contain grey faces. */ 429 del234(lightable_faces_sorted, fs); 430 del234(darkable_faces_sorted, fs); 431 432 /* Remember which face we've just coloured */ 433 cur_face = g->faces[i]; 434 435 /* The face we've just coloured potentially affects the colourability 436 * and the scores of any neighbouring faces (touching at a corner or 437 * edge). So the search needs to be conducted around all faces 438 * touching the one we've just lit. Iterate over its corners, then 439 * over each corner's faces. For each such face, we remove it from 440 * the lists, recalculate any scores, then add it back to the lists 441 * (depending on whether it is lightable, darkable or both). */ 442 for (i = 0; i < cur_face->order; i++) { 443 grid_dot *d = cur_face->dots[i]; 444 for (j = 0; j < d->order; j++) { 445 grid_face *f = d->faces[j]; 446 int fi; /* face index of f */ 447 448 if (f == NULL) 449 continue; 450 if (f == cur_face) 451 continue; 452 453 /* If the face is already coloured, it won't be on our 454 * lightable/darkable lists anyway, so we can skip it without 455 * bothering with the removal step. */ 456 if (FACE_COLOUR(f) != FACE_GREY) continue; 457 458 /* Find the face index and face_score* corresponding to f */ 459 fi = f->index; 460 fs = face_scores + fi; 461 462 /* Remove from lightable list if it's in there. We do this, 463 * even if it is still lightable, because the score might 464 * be different, and we need to remove-then-add to maintain 465 * correct sort order. */ 466 del234(lightable_faces_sorted, fs); 467 if (can_colour_face(g, board, fi, FACE_WHITE)) { 468 fs->white_score = face_score(g, board, f, FACE_WHITE); 469 add234(lightable_faces_sorted, fs); 470 } 471 /* Do the same for darkable list. */ 472 del234(darkable_faces_sorted, fs); 473 if (can_colour_face(g, board, fi, FACE_BLACK)) { 474 fs->black_score = face_score(g, board, f, FACE_BLACK); 475 add234(darkable_faces_sorted, fs); 476 } 477 } 478 } 479 } 480 481 /* Clean up */ 482 freetree234(lightable_faces_sorted); 483 freetree234(darkable_faces_sorted); 484 sfree(face_scores); 485 486 /* The next step requires a shuffled list of all faces */ 487 face_list = snewn(num_faces, int); 488 for (i = 0; i < num_faces; ++i) { 489 face_list[i] = i; 490 } 491 shuffle(face_list, num_faces, sizeof(int), rs); 492 493 /* The above loop-generation algorithm can often leave large clumps 494 * of faces of one colour. In extreme cases, the resulting path can be 495 * degenerate and not very satisfying to solve. 496 * This next step alleviates this problem: 497 * Go through the shuffled list, and flip the colour of any face we can 498 * legally flip, and which is adjacent to only one face of the opposite 499 * colour - this tends to grow 'tendrils' into any clumps. 500 * Repeat until we can find no more faces to flip. This will 501 * eventually terminate, because each flip increases the loop's 502 * perimeter, which cannot increase for ever. 503 * The resulting path will have maximal loopiness (in the sense that it 504 * cannot be improved "locally". Unfortunately, this allows a player to 505 * make some illicit deductions. To combat this (and make the path more 506 * interesting), we do one final pass making random flips. */ 507 508 /* Set to true for final pass */ 509 do_random_pass = false; 510 511 while (true) { 512 /* Remember whether a flip occurred during this pass */ 513 bool flipped = false; 514 515 for (i = 0; i < num_faces; ++i) { 516 int j = face_list[i]; 517 enum face_colour opp = 518 (board[j] == FACE_WHITE) ? FACE_BLACK : FACE_WHITE; 519 if (can_colour_face(g, board, j, opp)) { 520 grid_face *face = g->faces[j]; 521 if (do_random_pass) { 522 /* final random pass */ 523 if (!random_upto(rs, 10)) 524 board[j] = opp; 525 } else { 526 /* normal pass - flip when neighbour count is 1 */ 527 if (face_num_neighbours(g, board, face, opp) == 1) { 528 board[j] = opp; 529 flipped = true; 530 } 531 } 532 } 533 } 534 535 if (do_random_pass) break; 536 if (!flipped) do_random_pass = true; 537 } 538 539 sfree(face_list); 540}