/* Build a wall if appropriate. */ void maze_build_one_cell_wall(struct maze *mp, int wallrow, int wallcol, int prevrow, int prevcol, int currow, int curcol, int nextrow, int nextcol) { cell_t wallbit; cell_t *cp; /* If the candidate cell is either prev or next, no wall. */ if (wallrow == prevrow && wallcol == prevcol) return; if (wallrow == nextrow && wallcol == nextcol) return; /* If the wall cell actually exists, we need a wall. */ if (maze_cell_exists(mp, wallrow, wallcol)) { /* Wall in other cell. */ wallbit = maze_find_wallbit(wallrow, wallcol, currow, curcol); cp = maze_get_cell_addr(mp, wallrow, wallcol); *cp |= wallbit; /* Wall in current cell. */ wallbit = maze_find_wallbit(currow, curcol, wallrow, wallcol); cp = maze_get_cell_addr(mp, currow, curcol); *cp |= wallbit; } }
/* Remove a wall to allow another maze segment to be constructed. */ void maze_remove_wall(struct maze *mp, int currow, int curcol, int otherrow, int othercol) { cell_t *cp; cp = maze_get_cell_addr(mp, currow, curcol); *cp &= ~maze_find_wallbit(currow, curcol, otherrow, othercol); cp = maze_get_cell_addr(mp, otherrow, othercol); *cp &= ~maze_find_wallbit(otherrow, othercol, currow, curcol); }
/* * Assign a child-thread starting point within the maze. Return 1 if * successful, return 0 if not. For example, if some other thread * already is starting at the desired point, a second attempt to assign * that same point will fail. Just ABORT() if invalid starting point * given. */ int maze_solve_start_assign(struct maze *mp, int tid, int r, int c) { cell_t *cp; int i; struct maze_child *mcp = mp->msp->mcp; /* Die if the point lies outside of the maze. */ if (!maze_cell_exists(mp, r, c) || tid >= nthreads) ABORT(); /* * Return 0 if this point is already assigned to some other thread. * Or if some other point is already assigned to this thread, for * that matter. */ cp = maze_get_cell_addr(mp, r, c); if (*cp & VISITED || mcp[tid].startrow != -1) return 0; /* Assign the point. */ mcp[tid].startrow = r; mcp[tid].startcol = c; mcp[tid].visited[0].row = r; mcp[tid].visited[0].col = c; mcp[tid].vi = 1; *cp |= VISITED | tid; maze_set_cell_distance(mp, r, c, 1); return 1; }
/* Advance the solution marking by one cell. */ void maze_mark_advance_cell(struct maze *mp, int cr, int cc, int *nr, int *nc) { cell_t *cp = maze_get_cell_addr(mp, cr, cc); *cp |= SOLUTION; if (maze_get_cell_distance(mp, cr, cc) == 1) ABORT(); if (!maze_is_prev_cell(mp, cr, cc, cr - 1, cc, nr, nc) && !maze_is_prev_cell(mp, cr, cc, cr + 1, cc, nr, nc) && !maze_is_prev_cell(mp, cr, cc, cr, cc - 1, nr, nc) && !maze_is_prev_cell(mp, cr, cc, cr, cc + 1, nr, nc)) ABORT(); }
/* Remove visitation, solution, and distance tags to allow maze to be solved. */ void maze_unvisit_all(struct maze *mp) { cell_t *cp; int i; int j; for (i = 0; i < mp->nrows; i++) for (j = 0; j < mp->ncols; j++) { cp = maze_get_cell_addr(mp, i, j); *cp &= ~(VISITED | SOLUTION | COOKIE); maze_set_cell_distance(mp, i, j, 0); } mp->vi = 0; }
/* Visit the specified cell. Die if already visited. */ void maze_visit_cell(struct maze *mp, int row, int col, int distance) { cell_t *cp = maze_get_cell_addr(mp, row, col); if (*cp & VISITED) ABORT(); if (mp->visited != NULL) { if (mp->vi >= mp->nrows * mp->ncols) ABORT(); mp->visited[mp->vi].row = row; mp->visited[mp->vi].col = col; mp->vi++; } *cp |= VISITED; maze_set_cell_distance(mp, row, col, distance); }
/* Input a maze in binary form. */ struct maze *maze_binary_in(char *path) { char buf[8]; FILE *fp = fopen(path, "r"); int i; int j; struct maze maze_raw; struct maze *mp; int t; if (fp == NULL) { perror("maze_binary_in:"); fprintf(stderr, "Unable to open %s\n", path); return NULL; } t = fread(buf, sizeof(buf), 1, fp); if (strncmp(buf, bin_file_header, sizeof(buf)) != 0) { fprintf(stderr, "Bad header in maze_binary_in()\n"); return NULL; } t = fread(&maze_raw, sizeof(maze_raw), 1, fp); mp = (struct maze *)malloc(sizeof(*mp) + maze_raw.nrows * maze_raw.ncols * sizeof(struct cell)); if (mp == NULL) { fprintf(stderr, "Out of memory\n"); ABORT(); } *mp = maze_raw; mp->visited = (struct rowcol *)malloc(mp->nrows * mp->ncols * sizeof(*mp->visited)); if (mp->visited == NULL) { fprintf(stderr, "Out of memory"); ABORT(); } mp->vi = 0; mp->lastvi = -1; for (i = 0; i < mp->nrows; i++) for (j = 0; j < mp->ncols; j++) t = fread(maze_get_cell_addr(mp, i, j), sizeof(cell_t), 1, fp); new_empty_maze_solve(mp); return mp; }
/* * Output a maze in binary form. This allows different solvers to be * compared on an equal footing, particularly when testing cache * alignment. Differences in mazes can wash out large effects, so the * ability to compare algorithms running on exactly the same maze * can be quite important. */ int maze_binary_out(char *path, struct maze *mp) { FILE *fp = fopen(path, "w"); int i; int j; if (fp == NULL) { perror("maze_binary_out:"); fprintf(stderr, "Unable to open %s\n", path); return 0; } fwrite(bin_file_header, strlen(bin_file_header) + 1, 1, fp); fwrite(mp, sizeof(*mp), 1, fp); for (i = 0; i < mp->nrows; i++) for (j = 0; j < mp->ncols; j++) fwrite(maze_get_cell_addr(mp, i, j), sizeof(cell_t), 1, fp); return 1; }
/* * Mark the subsolution located by a interior thread, in other words, a * thread other than the ones that started at the beginning and end cells. */ int maze_mark_subsolution(struct maze *mp, int r1, int c1, int r2, int c2) { int cr1 = r1; int cc1 = c1; int cr2 = r2; int cc2 = c2; cell_t *cp; int d1 = maze_get_cell_distance(mp, cr1, cc1); int d2 = maze_get_cell_distance(mp, cr2, cc2); int nr; int nc; int s = 0; for (;;) { if (maze_get_cell_tid(mp, cr1, cc1) != maze_get_cell_tid(mp, cr2, cc2)) ABORT(); if (cr1 == cr2 && cc1 == cc2) { cp = maze_get_cell_addr(mp, cr1, cc1); *cp |= SOLUTION; return s + 1; } if (d1 > d2) { maze_mark_advance_cell(mp, cr1, cc1, &nr, &nc); s++; cr1 = nr; cc1 = nc; d1 = maze_get_cell_distance(mp, cr1, cc1); } else if (d1 <= d2 && d2 != 1) { maze_mark_advance_cell(mp, cr2, cc2, &nr, &nc); s++; cr2 = nr; cc2 = nc; d2 = maze_get_cell_distance(mp, cr2, cc2); } else ABORT(); } }
/* Attempt to visit a cell, return 0 if it has already been visited. */ int maze_try_visit_cell(struct maze *mp, int cr, int cc, int tr, int tc, int *nextrow, int *nextcol, int distance) { cell_t t; cell_t *tp; int vi; if (!maze_cells_connected(mp, cr, cc, tr, tc)) return -1; tp = maze_get_cell_addr(mp, tr, tc); do { t = ACCESS_ONCE(*tp); if (t & VISITED) { record_encounter(mp, cr, cc, tr, tc); return 1; } } while (!__sync_bool_compare_and_swap(tp, t, t | VISITED | myid)); maze_set_cell_distance(mp, tr, tc, distance); *nextrow = tr; *nextcol = tc; /* If we are solving the maze, mymcp is non-NULL. */ if (mymcp != NULL) { /* Use this thread's state. */ vi = mymcp->vi++; mymcp->visited[vi].row = tr; mymcp->visited[vi].col = tc; if (nthreads == 1 && tr == mymcp->mcsp->endrow && tc == mymcp->mcsp->endcol) mymcp->mcsp->done = 1; } else { /* Use global state. */ vi = mp->vi++; mp->visited[vi].row = tr; mp->visited[vi].col = tc; } return 0; }
/* * Mark the maze's solution, working from the end to the beginning. * Returns the length of the solution, including the two endpoints. * A length of zero implies an unsolvable maze. */ int maze_mark_solution(struct maze *mp, int startrow, int startcol, int endrow, int endcol) { int cr; int cc; cell_t *cp; int nr = endrow; int nc = endcol; for (;;) { cr = nr; cc = nc; cp = maze_get_cell_addr(mp, cr, cc); *cp |= SOLUTION; if (cr == startrow && cc == startcol) return maze_get_cell_distance(mp, endrow, endcol); if (!maze_is_prev_cell(mp, cr, cc, cr - 1, cc, &nr, &nc) && !maze_is_prev_cell(mp, cr, cc, cr + 1, cc, &nr, &nc) && !maze_is_prev_cell(mp, cr, cc, cr, cc - 1, &nr, &nc) && !maze_is_prev_cell(mp, cr, cc, cr, cc + 1, &nr, &nc)) ABORT(); } }