#include #include #include #include #define LINKS_MAX 2UL #define OPTIONS_MAX 4UL #define CHOICES_MAX 3UL #define PIPE_W 1UL #define PIPE_N 2UL #define PIPE_S 4UL #define PIPE_E 8UL #define CALLS_MAX 12UL #define ROLLS_MAX 4UL typedef struct cell_s cell_t; typedef struct color_s color_t; struct cell_s { unsigned long col; unsigned long row; unsigned long pipes; cell_t *path; unsigned long rank; color_t *color; unsigned long links_todo; unsigned long links_done; cell_t *links[LINKS_MAX]; cell_t *last; cell_t *next; }; struct color_s { cell_t *start; cell_t *end; }; typedef enum { CALL_SELECTION, CALL_CHAIN, CALL_UNROLL, CALL_ROLL, CALL_UNCHAIN } call_t; typedef struct { unsigned long pipe_used; cell_t *link; } option_t; typedef struct { cell_t *cell; } selection_t; typedef struct { cell_t *cell; unsigned long options_n; option_t options[OPTIONS_MAX]; } chain_t; typedef struct { cell_t *cell; cell_t *link; cell_t *cell_path; color_t *cell_color; cell_t *link_path; color_t *link_color; } unroll_t; typedef struct { cell_t *cell; cell_t *link; unroll_t *unroll; } roll_t; typedef struct { cell_t *link; unroll_t *unroll; } choice_t; typedef struct { unsigned long options_n; option_t options[OPTIONS_MAX]; unsigned long choices_n; choice_t choices[CHOICES_MAX][LINKS_MAX]; } constraint_t; void set_cell(cell_t *, unsigned long, unsigned long, unsigned long); void link_cell(cell_t *, cell_t *, cell_t *); int read_color(color_t *); cell_t *read_cell(int); int set_color(color_t *); int search_color(void); unsigned long random_xy(unsigned long *, unsigned long *); unsigned long erand(unsigned long); unsigned long get_distance(unsigned long, unsigned long, unsigned long, unsigned long); void set_cell_color(cell_t *, color_t *); void flowfree(void); void stack_call(call_t *, call_t); void perform_call(call_t *); void stack_selection(selection_t *, cell_t *); void perform_selection(selection_t *); void set_constraint(cell_t *, unsigned long, constraint_t *); unsigned long set_options_empty(cell_t *, option_t []); unsigned long set_options_w(cell_t *, option_t []); unsigned long set_options_n(cell_t *, option_t []); unsigned long set_options_wn(cell_t *, option_t []); unsigned long set_options_s(cell_t *, option_t []); unsigned long set_options_ws(cell_t *, option_t []); unsigned long set_options_ns(cell_t *, option_t []); unsigned long set_options_wns(cell_t *, option_t []); unsigned long set_options_e(cell_t *, option_t []); unsigned long set_options_we(cell_t *, option_t []); unsigned long set_options_ne(cell_t *, option_t []); unsigned long set_options_wne(cell_t *, option_t []); unsigned long set_options_se(cell_t *, option_t []); unsigned long set_options_wse(cell_t *, option_t []); unsigned long set_options_nse(cell_t *, option_t []); unsigned long set_options_wnse(cell_t *, option_t []); void set_option(option_t *, unsigned long, cell_t *); void add_roll_option(cell_t *, option_t *); void add_choices(cell_t *, unsigned long, unsigned long, unsigned long, unsigned long, unsigned long *, choice_t [][LINKS_MAX]); int roll_option(cell_t *, cell_t *); int no_touching(cell_t *); int touching_w(cell_t *); int touching_n(cell_t *); int touching_wn(cell_t *); int touching_s(cell_t *); int touching_ws(cell_t *); int touching_ns(cell_t *); int touching_e(cell_t *); int touching_we(cell_t *); int touching_ne(cell_t *); int touching_se(cell_t *); int same_path(cell_t *, cell_t *); void stack_chain(chain_t *, cell_t *, unsigned long, option_t []); void stack_unroll(unroll_t *, cell_t *, choice_t *); void stack_roll(roll_t *, cell_t *, choice_t *); void stack_unchain(chain_t *, cell_t *, unsigned long, option_t []); void print_color(color_t *); void print_cell(cell_t *, int); void perform_chain(chain_t *); void add_pipe(cell_t *, unsigned long, cell_t *); void chain_cell(cell_t *); void perform_unroll(unroll_t *); void unroll_link(cell_t *, cell_t *, cell_t *, color_t *, cell_t *, color_t *); void perform_roll(roll_t *); void roll_link(cell_t *, cell_t *, unroll_t *); void perform_unchain(chain_t *); void unchain_cell(cell_t *); void remove_pipe(cell_t *, unsigned long, cell_t *); void update_path(cell_t *, cell_t *); cell_t *next_cell(cell_t *, cell_t *); void reset_color(color_t *); int grid_rotated, touching_allowed, (*touching[])(cell_t *) = { no_touching, touching_w, touching_n, touching_wn, touching_s, touching_ws, touching_ns, no_touching, touching_e, touching_we, touching_ne, no_touching, touching_se, no_touching, no_touching, no_touching }; unsigned long colors_n, cols_n, rows_n, distance_min, solutions_max, nodes_n, solutions_n, stack_calls_n, stack_selections_n, stack_chains_n, stack_unrolls_n, stack_rolls_n, stack_unchains_n, (*set_options[])(cell_t *, option_t []) = { set_options_empty, set_options_w, set_options_n, set_options_wn, set_options_s, set_options_ws, set_options_ns, set_options_wns, set_options_e, set_options_we, set_options_ne, set_options_wne, set_options_se, set_options_wse, set_options_nse, set_options_wnse }, rolls_n, rolls_idx[LINKS_MAX]; cell_t *cells, *cells_header, *cell_start, *cell_end; color_t *colors; call_t *stack_calls; option_t *rolls[OPTIONS_MAX]; selection_t *stack_selections; chain_t *stack_chains, *stack_unchains; unroll_t *stack_unrolls; roll_t *stack_rolls; int main(void) { unsigned long cells_n, row, col, attempts_n_mod; cell_t *cell; if (scanf("%lu%lu%lu", &colors_n, &cols_n, &rows_n) != 3 || colors_n < 1UL || cols_n < 1UL || rows_n < 1UL) { fprintf(stderr, "Invalid parameters\n"); fflush(stderr); return EXIT_FAILURE; } grid_rotated = cols_n < rows_n; if (grid_rotated) { unsigned long tmp = cols_n; cols_n = rows_n; rows_n = tmp; } cells_n = cols_n*rows_n; if (cells_n < 2UL || cells_n < colors_n*2UL) { fprintf(stderr, "Invalid parameters\n"); fflush(stderr); return EXIT_FAILURE; } cells = malloc(sizeof(cell_t)*(cells_n+1UL)); if (!cells) { fprintf(stderr, "Could not allocate memory for cells\n"); fflush(stderr); return EXIT_FAILURE; } if (cols_n == 1UL) { set_cell(cells, 0UL, 0UL, PIPE_S); cell = cells+1UL; for (row = 1UL; row < rows_n-1UL; row++) { set_cell(cell++, 0UL, row, PIPE_N+PIPE_S); } set_cell(cell++, 0UL, row, PIPE_N); } else if (rows_n == 1UL) { set_cell(cells, 0UL, 0UL, PIPE_E); cell = cells+1UL; for (col = 1UL; col < cols_n-1UL; col++) { set_cell(cell++, col, 0UL, PIPE_W+PIPE_E); } set_cell(cell++, col, 0UL, PIPE_W); } else { cell = cells; set_cell(cell++, 0UL, 0UL, PIPE_E+PIPE_S); for (row = 1UL; row < rows_n-1UL; row++) { set_cell(cell++, 0UL, row, PIPE_N+PIPE_E+PIPE_S); } set_cell(cell++, 0UL, row, PIPE_N+PIPE_E); for (col = 1UL; col < cols_n-1UL; col++) { set_cell(cell++, col, 0UL, PIPE_W+PIPE_E+PIPE_S); for (row = 1UL; row < rows_n-1UL; row++) { set_cell(cell++, col, row, PIPE_W+PIPE_N+PIPE_E+PIPE_S); } set_cell(cell++, col, row, PIPE_W+PIPE_N+PIPE_E); } set_cell(cell++, col, 0UL, PIPE_W+PIPE_S); for (row = 1UL; row < rows_n-1UL; row++) { set_cell(cell++, col, row, PIPE_W+PIPE_N+PIPE_S); } set_cell(cell++, col, row, PIPE_W+PIPE_N); } cells_header = cell; link_cell(cells, cells_header, cells+1UL); for (cell = cells+1UL; cell < cells_header; cell++) { link_cell(cell, cell-1UL, cell+1UL); } link_cell(cell, cell-1UL, cells); colors = malloc(sizeof(color_t)*colors_n); if (!colors) { fprintf(stderr, "Could not allocate memory for colors\n"); fflush(stderr); free(cells); return EXIT_FAILURE; } if (scanf("%d", &touching_allowed) == 1) { unsigned long path_len; if (colors_n <= rows_n) { path_len = cols_n+rows_n-colors_n; } else { path_len = cells_n/colors_n; while (path_len > rows_n && path_len > cols_n-path_len+rows_n-colors_n+rows_n) { path_len--; } } if (scanf("%lu", &distance_min) != 1 || distance_min < 1UL || distance_min >= path_len || scanf("%lu%lu", &solutions_max, &attempts_n_mod) != 2 || solutions_max < 1UL || attempts_n_mod < 1UL) { fprintf(stderr, "Invalid generator parameters\n"); fflush(stderr); return EXIT_FAILURE; } } else { unsigned long i; distance_min = 0UL; solutions_max = ULONG_MAX; attempts_n_mod = 1UL; for (i = 0UL; i < colors_n; i++) { if (!read_color(colors+i)) { free(colors); free(cells); return EXIT_FAILURE; } } } stack_calls = malloc(sizeof(call_t)*(cells_n*CALLS_MAX+1UL)); if (!stack_calls) { fprintf(stderr, "Could not allocate memory for calls stack\n"); fflush(stderr); free(colors); free(cells); return EXIT_FAILURE; } stack_selections = malloc(sizeof(selection_t)*(cells_n*(CHOICES_MAX-1UL)+1UL)); if (!stack_selections) { fprintf(stderr, "Could not allocate memory for selections stack\n"); fflush(stderr); free(stack_calls); free(colors); free(cells); return EXIT_FAILURE; } stack_chains = malloc(sizeof(chain_t)*cells_n); if (!stack_chains) { fprintf(stderr, "Could not allocate memory for chains stack\n"); fflush(stderr); free(stack_selections); free(stack_calls); free(colors); free(cells); return EXIT_FAILURE; } stack_unrolls = malloc(sizeof(unroll_t)*cells_n*ROLLS_MAX); if (!stack_unrolls) { fprintf(stderr, "Could not allocate memory for unrolls stack\n"); fflush(stderr); free(stack_chains); free(stack_selections); free(stack_calls); free(colors); free(cells); return EXIT_FAILURE; } stack_rolls = malloc(sizeof(roll_t)*cells_n*ROLLS_MAX); if (!stack_rolls) { fprintf(stderr, "Could not allocate memory for rolls stack\n"); fflush(stderr); free(stack_unrolls); free(stack_chains); free(stack_selections); free(stack_calls); free(colors); free(cells); return EXIT_FAILURE; } stack_unchains = malloc(sizeof(chain_t)*cells_n); if (!stack_unchains) { fprintf(stderr, "Could not allocate memory for unchains stack\n"); fflush(stderr); free(stack_rolls); free(stack_unrolls); free(stack_chains); free(stack_selections); free(stack_calls); free(colors); free(cells); return EXIT_FAILURE; } if (distance_min > 0UL) { unsigned long attempts_n; srand((unsigned)time(NULL)); attempts_n = 0UL; do { unsigned long i; cell_start = cells; cell_end = cell_start->next; for (i = 0UL; i < colors_n && set_color(colors+i); i++); if (i == colors_n) { flowfree(); } for (; i > 0UL; i--) { reset_color(colors+i-1UL); } attempts_n++; if (attempts_n%attempts_n_mod == 0UL) { printf("Attempts %lu\n", attempts_n); fflush(stdout); } } while (solutions_n == 0UL || solutions_n > solutions_max); printf("\nNodes %lu\nSolutions %lu\nAttempts %lu\n", nodes_n, solutions_n, attempts_n); fflush(stdout); } else { touching_allowed = 0; flowfree(); printf("\nTouching forbidden\nNodes %lu\nSolutions %lu\n", nodes_n, solutions_n); fflush(stdout); if (solutions_n == 0UL) { touching_allowed = 1; flowfree(); printf("\nTouching allowed\nNodes %lu\nSolutions %lu\n", nodes_n, solutions_n); fflush(stdout); } } free(stack_unchains); free(stack_rolls); free(stack_unrolls); free(stack_chains); free(stack_selections); free(stack_calls); free(colors); free(cells); return EXIT_SUCCESS; } void set_cell(cell_t *cell, unsigned long col, unsigned long row, unsigned long pipes) { unsigned long i; cell->col = col; cell->row = row; cell->pipes = pipes; cell->path = cell; cell->rank = 1UL; cell->color = NULL; cell->links_todo = LINKS_MAX; cell->links_done = 0UL; for (i = 0UL; i < LINKS_MAX; i++) { cell->links[i] = NULL; } } void link_cell(cell_t *cell, cell_t *last, cell_t *next) { cell->last = last; cell->next = next; } int read_color(color_t *color) { color->start = read_cell(' '); if (!color->start) { fprintf(stderr, "Invalid start cell\n"); fflush(stderr); return 0; } if (color->start->color) { fprintf(stderr, "Start cell color already set\n"); fflush(stderr); return 0; } color->end = read_cell('\n'); if (!color->end) { fprintf(stderr, "Invalid end cell\n"); fflush(stderr); return 0; } if (color->end->color) { fprintf(stderr, "End cell color already set\n"); fflush(stderr); return 0; } set_cell_color(color->start, color); set_cell_color(color->end, color); return 1; } cell_t *read_cell(int c) { unsigned long x, y; if (grid_rotated) { if (scanf("(%lu, %lu)", &y, &x) != 2 || x > cols_n || y > rows_n || fgetc(stdin) != c) { return NULL; } } else { if (scanf("(%lu, %lu)", &x, &y) != 2 || x > cols_n || y > rows_n || fgetc(stdin) != c) { return NULL; } } return cells+rows_n*x+y; } int set_color(color_t *color) { if (!search_color()) { return 0; } unsigned long start_x, start_y, end_x, end_y; do { color->start = cells+random_xy(&start_x, &start_y); color->end = cells+random_xy(&end_x, &end_y); } while (color->start == color->end || color->start->color || color->end->color || get_distance(start_x, start_y, end_x, end_y) < distance_min); set_cell_color(color->start, color); set_cell_color(color->end, color); return 1; } int search_color(void) { if (!cell_start->color) { for (; cell_end != cells_header; cell_end = cell_end->next) { if (!cell_end->color && get_distance(cell_start->col, cell_start->row, cell_end->col, cell_end->row) >= distance_min) { return 1; } } } for (cell_start = cell_start->next; cell_start != cells_header; cell_start = cell_start->next) { if (!cell_start->color) { for (cell_end = cell_start->next; cell_end != cells_header; cell_end = cell_end->next) { if (!cell_end->color && get_distance(cell_start->col, cell_start->row, cell_end->col, cell_end->row) >= distance_min) { return 1; } } } } return 0; } unsigned long random_xy(unsigned long *x, unsigned long *y) { *x = erand(cols_n); *y = erand(rows_n); return *x*rows_n+*y; } unsigned long erand(unsigned long values) { return (unsigned long)(rand()/(RAND_MAX+1.0)*(double)values); } unsigned long get_distance(unsigned long x1, unsigned long y1, unsigned long x2, unsigned long y2) { if (x1 < x2) { if (y1 < y2) { return x2-x1+y2-y1; } return x2-x1+y1-y2; } if (y1 < y2) { return x1-x2+y2-y1; } return x1-x2+y1-y2; } void set_cell_color(cell_t *cell, color_t *color) { cell->color = color; cell->links_todo--; } void flowfree(void) { nodes_n = 0UL; solutions_n = 0UL; stack_calls_n = 0UL; stack_selections_n = 0UL; stack_chains_n = 0UL; stack_unrolls_n = 0UL; stack_rolls_n = 0UL; stack_unchains_n = 0UL; stack_selection(stack_selections+stack_selections_n, cells_header->next); stack_call(stack_calls, CALL_SELECTION); do { stack_calls_n--; perform_call(stack_calls+stack_calls_n); } while (stack_calls_n > 0UL); } void stack_call(call_t *call, call_t type) { *call = type; stack_calls_n++; } void perform_call(call_t *call) { switch (*call) { case CALL_SELECTION: stack_selections_n--; perform_selection(stack_selections+stack_selections_n); break; case CALL_CHAIN: stack_chains_n--; perform_chain(stack_chains+stack_chains_n); break; case CALL_UNROLL: stack_unrolls_n--; perform_unroll(stack_unrolls+stack_unrolls_n); break; case CALL_ROLL: stack_rolls_n--; perform_roll(stack_rolls+stack_rolls_n); break; case CALL_UNCHAIN: stack_unchains_n--; perform_unchain(stack_unchains+stack_unchains_n); break; } } void stack_selection(selection_t *selection, cell_t *cell) { selection->cell = cell; stack_selections_n++; } void perform_selection(selection_t *selection) { nodes_n++; if (cells_header->next != cells_header) { if (solutions_n <= solutions_max) { cell_t *cell_min = selection->cell, *cell; constraint_t constraint_min; set_constraint(cell_min, CHOICES_MAX, &constraint_min); for (cell = cell_min->next; cell != selection->cell && constraint_min.choices_n > 1UL; cell = cell->next) { constraint_t constraint; if (cell == cells_header) { continue; } if (cell->links_todo < cell_min->links_todo || (cell > cell_min && cell->links_todo == cell_min->links_todo)) { set_constraint(cell, constraint_min.choices_n, &constraint); if (constraint.choices_n < constraint_min.choices_n) { cell_min = cell; constraint_min = constraint; } } else { if (constraint_min.choices_n < CHOICES_MAX) { set_constraint(cell, constraint_min.choices_n+1, &constraint); if (constraint.choices_n <= constraint_min.choices_n) { cell_min = cell; constraint_min = constraint; } } else { set_constraint(cell, CHOICES_MAX, &constraint); cell_min = cell; constraint_min = constraint; } } } if (constraint_min.choices_n > 0UL) { unsigned long i; stack_chain(stack_chains+stack_chains_n, cell_min, constraint_min.options_n, constraint_min.options); stack_call(stack_calls+stack_calls_n, CALL_CHAIN); for (i = constraint_min.choices_n; i > 0UL; i--) { unsigned long j; for (j = 0UL; j < cell_min->links_todo; j++) { stack_unroll(stack_unrolls+stack_unrolls_n, cell_min, &constraint_min.choices[i-1UL][j]); stack_call(stack_calls+stack_calls_n, CALL_UNROLL); } if (constraint_min.choices_n == 1UL) { if (cell_min->next != cells_header) { stack_selection(stack_selections+stack_selections_n, cell_min->next); } else { stack_selection(stack_selections+stack_selections_n, cells_header->next); } } else { if (cell_min != cells_header->next) { stack_selection(stack_selections+stack_selections_n, cells_header->next); } else { stack_selection(stack_selections+stack_selections_n, cell_min->next); } } stack_call(stack_calls+stack_calls_n, CALL_SELECTION); for (; j > 0UL; j--) { stack_roll(stack_rolls+stack_rolls_n, cell_min, &constraint_min.choices[i-1UL][j-1UL]); stack_call(stack_calls+stack_calls_n, CALL_ROLL); } } stack_unchain(stack_unchains+stack_unchains_n, cell_min, constraint_min.options_n, constraint_min.options); stack_call(stack_calls+stack_calls_n, CALL_UNCHAIN); } } } else { solutions_n++; if (solutions_n == 1UL) { unsigned long i; puts(""); if (distance_min > 0UL) { if (grid_rotated) { printf("%lu %lu %lu\n", colors_n, rows_n, cols_n); } else { printf("%lu %lu %lu\n", colors_n, cols_n, rows_n); } } for (i = 0UL; i < colors_n; i++) { print_color(colors+i); } } else if (solutions_n > solutions_max) { puts("\nToo many solutions"); } fflush(stdout); } } void set_constraint(cell_t *cell, unsigned long choices_max, constraint_t *constraint) { constraint->options_n = set_options[cell->pipes](cell, constraint->options); if (cell->links_todo > 0UL) { unsigned long i; rolls_n = 0UL; for (i = 0UL; i < constraint->options_n; i++) { add_roll_option(cell, constraint->options+i); } constraint->choices_n = 0UL; if (rolls_n >= cell->links_todo) { add_choices(cell, choices_max, 0UL, 0UL, 0UL, &constraint->choices_n, constraint->choices); } } else { constraint->choices_n = 1UL; } } unsigned long set_options_empty(cell_t *cell, option_t options[]) { set_option(options, 0UL, cell); return 0UL; } unsigned long set_options_w(cell_t *cell, option_t options[]) { set_option(options, PIPE_W, cell-rows_n); return 1UL; } unsigned long set_options_n(cell_t *cell, option_t options[]) { set_option(options, PIPE_N, cell-1UL); return 1UL; } unsigned long set_options_wn(cell_t *cell, option_t options[]) { set_option(options++, PIPE_W, cell-rows_n); set_option(options, PIPE_N, cell-1UL); return 2UL; } unsigned long set_options_s(cell_t *cell, option_t options[]) { set_option(options, PIPE_S, cell+1UL); return 1UL; } unsigned long set_options_ws(cell_t *cell, option_t options[]) { set_option(options++, PIPE_W, cell-rows_n); set_option(options, PIPE_S, cell+1UL); return 2UL; } unsigned long set_options_ns(cell_t *cell, option_t options[]) { set_option(options++, PIPE_N, cell-1UL); set_option(options, PIPE_S, cell+1UL); return 2UL; } unsigned long set_options_wns(cell_t *cell, option_t options[]) { set_option(options++, PIPE_W, cell-rows_n); set_option(options++, PIPE_N, cell-1UL); set_option(options, PIPE_S, cell+1UL); return 3UL; } unsigned long set_options_e(cell_t *cell, option_t options[]) { set_option(options, PIPE_E, cell+rows_n); return 1UL; } unsigned long set_options_we(cell_t *cell, option_t options[]) { set_option(options++, PIPE_W, cell-rows_n); set_option(options, PIPE_E, cell+rows_n); return 2UL; } unsigned long set_options_ne(cell_t *cell, option_t options[]) { set_option(options++, PIPE_N, cell-1UL); set_option(options, PIPE_E, cell+rows_n); return 2UL; } unsigned long set_options_wne(cell_t *cell, option_t options[]) { set_option(options++, PIPE_W, cell-rows_n); set_option(options++, PIPE_N, cell-1UL); set_option(options, PIPE_E, cell+rows_n); return 3UL; } unsigned long set_options_se(cell_t *cell, option_t options[]) { set_option(options++, PIPE_S, cell+1UL); set_option(options, PIPE_E, cell+rows_n); return 2UL; } unsigned long set_options_wse(cell_t *cell, option_t options[]) { set_option(options++, PIPE_W, cell-rows_n); set_option(options++, PIPE_S, cell+1UL); set_option(options, PIPE_E, cell+rows_n); return 3UL; } unsigned long set_options_nse(cell_t *cell, option_t options[]) { set_option(options++, PIPE_N, cell-1UL); set_option(options++, PIPE_S, cell+1UL); set_option(options, PIPE_E, cell+rows_n); return 3UL; } unsigned long set_options_wnse(cell_t *cell, option_t options[]) { set_option(options++, PIPE_W, cell-rows_n); set_option(options++, PIPE_N, cell-1UL); set_option(options++, PIPE_S, cell+1UL); set_option(options, PIPE_E, cell+rows_n); return 4UL; } void set_option(option_t *option, unsigned long pipe_used, cell_t *link) { option->pipe_used = pipe_used; option->link = link; } void add_roll_option(cell_t *cell, option_t *option) { if (option->link->links_todo > 0UL && roll_option(cell, option->link)) { rolls[rolls_n++] = option; } } void add_choices(cell_t *cell, unsigned long choices_max, unsigned long step, unsigned long rolls_start, unsigned long pipes, unsigned long *choices_n, choice_t choices[][LINKS_MAX]) { if (step < cell->links_todo) { unsigned long i; for (i = rolls_start; i < rolls_n && *choices_n < choices_max; i++) { unsigned long j; for (j = 0UL; j < step && roll_option(rolls[rolls_idx[j]]->link, rolls[i]->link); j++); if (j == step) { rolls_idx[step] = i; add_choices(cell, choices_max, step+1UL, i+1UL, pipes+rolls[i]->pipe_used, choices_n, choices); } } } else { if (touching_allowed || !touching[pipes](cell)) { unsigned long i; for (i = 0UL; i < cell->links_todo; i++) { choices[*choices_n][i].link = rolls[rolls_idx[i]]->link; } *choices_n = *choices_n+1UL; } } } int roll_option(cell_t *cell, cell_t *link) { if (link->path == cell->path) { return 0; } if (link->path->color && cell->path->color) { return link->path->color == cell->path->color; } return 1; } int no_touching(cell_t *cell) { return cell == NULL; } int touching_w(cell_t *cell) { if (cell->row > 0UL) { if (same_path(cell, cell-rows_n-1UL) || same_path(cell-rows_n, cell-1UL)) { return 1; } if (same_path(cell, cell-1UL) && same_path(cell-rows_n, cell-rows_n-1UL)) { return 1; } } if (cell->row < rows_n-1UL) { if (same_path(cell, cell-rows_n+1UL) || same_path(cell-rows_n, cell+1UL)) { return 1; } if (same_path(cell, cell+1UL) && same_path(cell-rows_n, cell-rows_n+1UL)) { return 1; } } return 0; } int touching_n(cell_t *cell) { if (cell->col > 0UL) { if (same_path(cell, cell-rows_n-1UL) || same_path(cell-1UL, cell-rows_n)) { return 1; } if (same_path(cell, cell-rows_n) && same_path(cell-1UL, cell-rows_n-1UL)) { return 1; } } if (cell->col < cols_n-1UL) { if (same_path(cell, cell+rows_n-1UL) || same_path(cell-1UL, cell+rows_n)) { return 1; } if (same_path(cell, cell+rows_n) && same_path(cell-1UL, cell+rows_n-1UL)) { return 1; } } return 0; } int touching_wn(cell_t *cell) { if (same_path(cell-rows_n, cell-rows_n-1UL) || same_path(cell-1UL, cell-rows_n-1UL)) { return 1; } if (cell->row < rows_n-1UL && same_path(cell-rows_n, cell+1UL)) { return 1; } if (cell->col < cols_n-1UL && same_path(cell-1UL, cell+rows_n)) { return 1; } return 0; } int touching_s(cell_t *cell) { if (cell->col > 0UL) { if (same_path(cell, cell-rows_n+1UL) || same_path(cell+1UL, cell-rows_n)) { return 1; } if (same_path(cell, cell-rows_n) && same_path(cell+1UL, cell-rows_n+1UL)) { return 1; } } if (cell->col < cols_n-1UL) { if (same_path(cell, cell+rows_n+1UL) || same_path(cell+1UL, cell+rows_n)) { return 1; } if (same_path(cell, cell+rows_n) && same_path(cell+1UL, cell+rows_n+1UL)) { return 1; } } return 0; } int touching_ws(cell_t *cell) { if (same_path(cell-rows_n, cell-rows_n+1UL) || same_path(cell+1UL, cell-rows_n+1UL)) { return 1; } if (cell->row > 0UL && same_path(cell-rows_n, cell-1UL)) { return 1; } if (cell->col < cols_n-1UL && same_path(cell+1UL, cell+rows_n)) { return 1; } return 0; } int touching_ns(cell_t *cell) { if (cell->col > 0UL) { if (same_path(cell-1UL, cell-rows_n) || same_path(cell-1UL, cell-rows_n+1UL) || same_path(cell+1UL, cell-rows_n-1UL) || same_path(cell+1UL, cell-rows_n)) { return 1; } } if (cell->col < cols_n-1UL) { if (same_path(cell-1UL, cell+rows_n) || same_path(cell-1UL, cell+rows_n+1UL) || same_path(cell+1UL, cell+rows_n-1UL) || same_path(cell+1UL, cell+rows_n)) { return 1; } } return 0; } int touching_e(cell_t *cell) { if (cell->row > 0UL) { if (same_path(cell, cell+rows_n-1UL) || same_path(cell+rows_n, cell-1UL)) { return 1; } if (same_path(cell, cell-1UL) && same_path(cell+rows_n, cell+rows_n-1UL)) { return 1; } } if (cell->row < rows_n-1UL) { if (same_path(cell, cell+rows_n+1UL) || same_path(cell+rows_n, cell+1UL)) { return 1; } if (same_path(cell, cell+1UL) && same_path(cell+rows_n, cell+rows_n+1UL)) { return 1; } } return 0; } int touching_we(cell_t *cell) { if (cell->row > 0UL) { if (same_path(cell-rows_n, cell-1UL) || same_path(cell-rows_n, cell+rows_n-1UL) || same_path(cell+rows_n, cell-rows_n-1UL) || same_path(cell+rows_n, cell-1UL)) { return 1; } } if (cell->row < rows_n-1UL) { if (same_path(cell-rows_n, cell+1UL) || same_path(cell-rows_n, cell+rows_n+1UL) || same_path(cell+rows_n, cell-rows_n+1UL) || same_path(cell+rows_n, cell+1UL)) { return 1; } } return 0; } int touching_ne(cell_t *cell) { if (same_path(cell-1UL, cell+rows_n-1UL) || same_path(cell+rows_n, cell+rows_n-1UL)) { return 1; } if (cell->col > 0UL && same_path(cell-1UL, cell-rows_n)) { return 1; } if (cell->row < rows_n-1UL && same_path(cell+rows_n, cell+1UL)) { return 1; } return 0; } int touching_se(cell_t *cell) { if (same_path(cell+rows_n, cell+rows_n+1UL) || same_path(cell+1UL, cell+rows_n+1UL)) { return 1; } if (cell->col > 0UL && same_path(cell+1UL, cell-rows_n)) { return 1; } if (cell->row > 0UL && same_path(cell+rows_n, cell-1UL)) { return 1; } return 0; } int same_path(cell_t *cell_a, cell_t *cell_b) { return cell_a->path == cell_b->path; } void stack_chain(chain_t *chain, cell_t *cell, unsigned long options_n, option_t options[]) { unsigned long i; chain->cell = cell; chain->options_n = options_n; for (i = 0UL; i < options_n; i++) { chain->options[i] = options[i]; } stack_chains_n++; } void stack_unroll(unroll_t *unroll, cell_t *cell, choice_t *choice) { choice->unroll = unroll; unroll->cell = cell; unroll->link = choice->link; stack_unrolls_n++; } void stack_roll(roll_t *roll, cell_t *cell, choice_t *choice) { roll->cell = cell; roll->link = choice->link; roll->unroll = choice->unroll; stack_rolls_n++; } void stack_unchain(chain_t *unchain, cell_t *cell, unsigned long options_n, option_t options[]) { unsigned long i; unchain->cell = cell; unchain->options_n = options_n; for (i = 0UL; i < options_n; i++) { unchain->options[i] = options[i]; } stack_unchains_n++; } void print_color(color_t *color) { if (distance_min > 0UL) { print_cell(color->start, ' '); print_cell(color->end, '\n'); } else { cell_t *cell = color->start, *next = next_cell(cell, NULL); print_cell(cell, ' '); while (next != color->end) { cell_t *last = cell; cell = next; next = next_cell(cell, last); print_cell(cell, ' '); } print_cell(next, '\n'); } } void print_cell(cell_t *cell, int c) { if (grid_rotated) { printf("(%lu, %lu)", cell->row, cell->col); } else { printf("(%lu, %lu)", cell->col, cell->row); } putchar(c); } void perform_chain(chain_t *chain) { unsigned long i; for (i = 0UL; i < chain->options_n; i++) { add_pipe(chain->cell, chain->options[i].pipe_used, chain->options[i].link); } chain_cell(chain->cell); } void add_pipe(cell_t *cell, unsigned long pipe_used, cell_t *link) { link->pipes += PIPE_E/pipe_used; cell->pipes += pipe_used; } void chain_cell(cell_t *cell) { cell->last->next = cell; cell->next->last = cell; } void perform_unroll(unroll_t *unroll) { unroll_link(unroll->cell, unroll->link, unroll->cell_path, unroll->cell_color, unroll->link_path, unroll->link_color); } void unroll_link(cell_t *cell, cell_t *link, cell_t *cell_path, color_t *cell_color, cell_t *link_path, color_t *link_color) { link->links[--link->links_done] = NULL; link->links_todo++; cell->links[--cell->links_done] = NULL; cell->links_todo++; if (cell_path->rank < link_path->rank) { update_path(cell, cell_path); link_path->rank -= cell_path->rank; } else { update_path(link, link_path); cell_path->rank -= link_path->rank; } if (cell_color) { if (!link_color) { link_path->color = NULL; } } else { if (link_color) { cell_path->color = NULL; } } } void perform_roll(roll_t *roll) { roll_link(roll->cell, roll->link, roll->unroll); } void roll_link(cell_t *cell, cell_t *link, unroll_t *unroll) { unroll->cell_path = cell->path; unroll->cell_color = cell->path->color; unroll->link_path = link->path; unroll->link_color = link->path->color; if (cell->path->color) { if (!link->path->color) { link->path->color = cell->path->color; } } else { if (link->path->color) { cell->path->color = link->path->color; } } if (cell->path->rank < link->path->rank) { link->path->rank += cell->path->rank; update_path(cell, link->path); } else { cell->path->rank += link->path->rank; update_path(link, cell->path); } cell->links_todo--; cell->links[cell->links_done++] = link; link->links_todo--; link->links[link->links_done++] = cell; } void perform_unchain(chain_t *unchain) { unsigned long i; unchain_cell(unchain->cell); for (i = 0UL; i < unchain->options_n; i++) { remove_pipe(unchain->cell, unchain->options[i].pipe_used, unchain->options[i].link); } } void unchain_cell(cell_t *cell) { cell->last->next = cell->next; cell->next->last = cell->last; } void remove_pipe(cell_t *cell, unsigned long pipe_used, cell_t *link) { cell->pipes -= pipe_used; link->pipes -= PIPE_E/pipe_used; } void update_path(cell_t *start, cell_t *path) { cell_t *cell = start, *next = next_cell(cell, NULL); cell->path = path; while (next) { cell_t *last = cell; cell = next; next = next_cell(cell, last); cell->path = path; } } cell_t *next_cell(cell_t *cell, cell_t *last) { unsigned long i; for (i = 0UL; i < cell->links_done && cell->links[i] == last; i++); if (i < cell->links_done) { return cell->links[i]; } return NULL; } void reset_color(color_t *color) { color->start->color = NULL; color->start->links_todo++; color->end->color = NULL; color->end->links_todo++; }