/* Unapply the last move on the undo list, reversing what was done in * domove() and adding the move to the redo list. */ int undomove(void) { dyx move; yx j; if (!state.undo.count) return FALSE; move = state.undo.list[--state.undo.count]; addtomovelist(&state.redo, move); if (move.box) { j = state.player + move.yx; state.map[j] &= ~BOX; state.map[state.player] |= BOX; if (state.map[j] & GOAL) --state.storecount; if (state.map[state.player] & GOAL) ++state.storecount; --state.pushcount; } state.map[state.player] &= ~PLAYER; state.player -= move.yx; state.map[state.player] |= PLAYER; --state.movecount; if (recording && macro->count) { if (--macro->count == 0) recording = FALSE; } return TRUE; }
/* Apply a legal move to the current state, adding it to the undo list. */ static void domove(action move) { move.door = moveblock(move.id, move.dir); state.currblock = move.id; state.ycurrpos = state.xcurrpos = 0; ++state.movecount; if (!state.undo.count || move.id != state.undo.list[state.undo.count - 1].id) ++state.stepcount; addtomovelist(&state.undo, move); }
/* Apply a legal move to the current state, adding it to the undo list * and any macro being recorded. (This function contains the actual * sokoban game logic. Everything else in this program is just * housekeeping.) */ static void domove(dyx move) { yx j; state.map[state.player] &= ~PLAYER; state.player += move.yx; state.map[state.player] |= PLAYER; ++state.movecount; if (move.box) { j = state.player + move.yx; state.map[state.player] &= ~BOX; state.map[j] |= BOX; if (state.map[state.player] & GOAL) --state.storecount; if (state.map[j] & GOAL) ++state.storecount; ++state.pushcount; } addtomovelist(&state.undo, move); if (recording) addtomovelist(macro, move); }
/* Compare the solution currently sitting in the undo list with the * user's best solutions (if any). If this solution beats what's * there, replace them. If this solution has the save number of moves * as the least-moves solution, but fewer pushes, then the replacement * will be done, and likewise for the least-pushes solution. Note that * the undo list contains the moves in backwards order, so the list * needs to be reversed when it is copied. TRUE is returned if any * solution was replaced. */ int replaceanswers(int saveinc) { int i, n; if (saveinc && (state.game->movebestcount || state.game->pushbestcount)) return FALSE; n = 0; if (!state.game->movebestcount || state.movecount < state.game->movebestcount || (state.movecount == state.game->movebestcount && state.pushcount < state.game->movebestpushcount)) { initmovelist(&state.game->moveanswer); i = state.undo.count; while (i--) addtomovelist(&state.game->moveanswer, state.undo.list[i]); if (!saveinc) state.game->movebestcount = state.movecount; state.game->movebestpushcount = state.pushcount; ++n; } if (!state.game->pushbestcount || state.pushcount < state.game->pushbestcount || (state.pushcount == state.game->pushbestcount && state.movecount < state.game->pushbestmovecount)) { initmovelist(&state.game->pushanswer); i = state.undo.count; while (i--) addtomovelist(&state.game->pushanswer, state.undo.list[i]); state.game->pushbestcount = state.pushcount; if (!saveinc) state.game->pushbestmovecount = state.movecount; ++n; } return n > 0; }
/* Unapply the last move on the undo list, reversing what was done in * domove() and adding the move to the redo list. */ int undomove(void) { action move; if (!state.undo.count) return FALSE; move = state.undo.list[--state.undo.count]; addtomovelist(&state.redo, move); moveblock(move.id, backwards(move.dir)); state.currblock = move.id; state.ycurrpos = state.xcurrpos = 0; --state.movecount; if (!state.undo.count || move.id != state.undo.list[state.undo.count - 1].id) --state.stepcount; if (move.door) resetdoors(); return TRUE; }
/* Compare the solution currently sitting in the undo list with the * user's best solutions (if any). If this solution beats what's * there, replace them. If this solution has the same number of moves * as the least-moves solution, but fewer steps, then the replacement * will be done, and likewise for the least-steps solution. Note that * the undo list contains the moves in backwards order, so the list * needs to be reversed when it is copied. TRUE is returned if any * solution was replaced. */ int replaceanswer(int saveinc) { int i; if (state.game->beststepcount) { if (saveinc) return FALSE; if (state.stepcount > state.game->beststepcount || (state.stepcount == state.game->beststepcount && state.movecount >= state.game->answer.count)) return FALSE; state.game->beststepcount = state.stepcount; } else { if (!saveinc) state.game->beststepcount = state.stepcount; } initmovelist(&state.game->answer); i = state.undo.count; while (i--) addtomovelist(&state.game->answer, state.undo.list[i]); return TRUE; }
/* Expand a level's solution data into an actual list of moves. */ int expandsolution(solutioninfo *solution, gamesetup const *game) { unsigned char const *dataend; unsigned char const *p; action act; int n; if (game->solutionsize <= 16) return FALSE; solution->flags = game->solutiondata[6]; solution->rndslidedir = indextodir(game->solutiondata[7] & 7); solution->stepping = (game->solutiondata[7] >> 3) & 7; solution->rndseed = game->solutiondata[8] | (game->solutiondata[9] << 8) | (game->solutiondata[10] << 16) | (game->solutiondata[11] << 24); initmovelist(&solution->moves); act.when = -1; p = game->solutiondata + 16; dataend = game->solutiondata + game->solutionsize; while (p < dataend) { switch (*p & 0x03) { case 0: act.dir = indextodir((*p >> 2) & 0x03); act.when += 4; addtomovelist(&solution->moves, act); act.dir = indextodir((*p >> 4) & 0x03); act.when += 4; addtomovelist(&solution->moves, act); act.dir = indextodir((*p >> 6) & 0x03); act.when += 4; addtomovelist(&solution->moves, act); ++p; break; case 1: act.dir = indextodir((*p >> 2) & 0x07); act.when += ((*p >> 5) & 0x07) + 1; addtomovelist(&solution->moves, act); ++p; break; case 2: if (p + 2 > dataend) goto truncated; act.dir = indextodir((*p >> 2) & 0x07); act.when += ((p[0] >> 5) & 0x07) + ((unsigned long)p[1] << 3) + 1; addtomovelist(&solution->moves, act); p += 2; break; case 3: if (*p & 0x10) { n = (*p >> 2) & 0x03; if (p + 2 + n > dataend) goto truncated; act.dir = ((p[0] >> 5) & 0x07) | ((p[1] & 0x3F) << 3); act.when += (p[1] >> 6) & 0x03; while (n--) act.when += (unsigned long)p[2 + n] << (2 + n * 8); ++act.when; p += 2 + ((*p >> 2) & 0x03); } else { if (p + 4 > dataend) goto truncated; act.dir = indextodir((*p >> 2) & 0x03); act.when += ((p[0] >> 5) & 0x07) | ((unsigned long)p[1] << 3) | ((unsigned long)p[2] << 11) | ((unsigned long)p[3] << 19); ++act.when; p += 4; } addtomovelist(&solution->moves, act); break; }