Do not check for saved games after the first game in a single session. Save the entire board for undo, not just the last modified square. When entering a value in a square, update all scratch values in the row, column and sub square. Fix a bug where some squares in a sub square were not being checked for invalid values.
This commit is contained in:
parent
9a57ed036f
commit
237c854e42
135
a2sudoku/game.c
135
a2sudoku/game.c
|
@ -17,6 +17,7 @@
|
|||
|
||||
// Macros
|
||||
#define SQUARE_XY(x, y) (theGame.squares[((y) * BOARD_SIZE) + (x)])
|
||||
#define PREV_SQUARE_XY(x, y) (theGame.prevSquares[((y) * BOARD_SIZE) + (x)])
|
||||
#define SAVE_GAME_FILE "a2sudoku.game"
|
||||
|
||||
|
||||
|
@ -31,19 +32,12 @@ typedef struct tGameSquare {
|
|||
} tGameSquare;
|
||||
|
||||
|
||||
typedef struct tUndoMove {
|
||||
tGameSquare oldSquare;
|
||||
tPos x;
|
||||
tPos y;
|
||||
bool isValid;
|
||||
} tUndoMove;
|
||||
|
||||
|
||||
typedef struct tGame {
|
||||
tGameSquare squares[BOARD_SIZE * BOARD_SIZE];
|
||||
tGameSquare prevSquares[BOARD_SIZE * BOARD_SIZE];
|
||||
struct tPuzzle *puzzle;
|
||||
tUpdatePosCallback callback;
|
||||
tUndoMove undo;
|
||||
bool undoValid;
|
||||
} tGame;
|
||||
|
||||
|
||||
|
@ -89,6 +83,8 @@ void restartGame(void)
|
|||
refreshPos(x, y);
|
||||
}
|
||||
}
|
||||
|
||||
theGame.undoValid = false;
|
||||
}
|
||||
|
||||
|
||||
|
@ -258,7 +254,7 @@ bool isSquareInvalid(tPos col, tPos row)
|
|||
for (x = subSquareXStart; x < subSquareXEnd; x++) {
|
||||
if (x == col)
|
||||
continue;
|
||||
if (y == col)
|
||||
if (y == row)
|
||||
continue;
|
||||
|
||||
if (value == SQUARE_XY(x, y).value)
|
||||
|
@ -272,11 +268,12 @@ bool isSquareInvalid(tPos col, tPos row)
|
|||
}
|
||||
|
||||
|
||||
void refreshInvalid(tPos col, tPos row, tSquareVal oldValue)
|
||||
void refreshNeighbours(tPos col, tPos row, tSquareVal newValue, tSquareVal oldValue)
|
||||
{
|
||||
tPos x, y;
|
||||
tGameSquare *square = &(SQUARE_XY(col, row));
|
||||
bool newInvalid;
|
||||
bool checkInvalid = true;
|
||||
tPos subSquareXStart, subSquareXEnd;
|
||||
tPos subSquareYStart, subSquareYEnd;
|
||||
|
||||
|
@ -293,7 +290,7 @@ void refreshInvalid(tPos col, tPos row, tSquareVal oldValue)
|
|||
// square.
|
||||
if ((oldValue == EMPTY_SQUARE) &&
|
||||
(!newInvalid))
|
||||
return;
|
||||
checkInvalid = false;
|
||||
|
||||
for (y = 0; y < BOARD_SIZE; y++) {
|
||||
if (y == row)
|
||||
|
@ -302,11 +299,18 @@ void refreshInvalid(tPos col, tPos row, tSquareVal oldValue)
|
|||
square = &(SQUARE_XY(col, y));
|
||||
if (square->knownAtStart)
|
||||
continue;
|
||||
if (checkInvalid) {
|
||||
newInvalid = isSquareInvalid(col, y);
|
||||
|
||||
if (newInvalid != square->invalid) {
|
||||
square->invalid = newInvalid;
|
||||
refreshPos(col, y);
|
||||
}
|
||||
}
|
||||
|
||||
newInvalid = isSquareInvalid(col, y);
|
||||
|
||||
if (newInvalid != square->invalid) {
|
||||
square->invalid = newInvalid;
|
||||
if ((newValue != EMPTY_SQUARE) &&
|
||||
(SCRATCH_TEST(square->scratchValues, newValue))) {
|
||||
square->scratchValues ^= (0x1 << newValue);
|
||||
refreshPos(col, y);
|
||||
}
|
||||
}
|
||||
|
@ -319,10 +323,18 @@ void refreshInvalid(tPos col, tPos row, tSquareVal oldValue)
|
|||
if (square->knownAtStart)
|
||||
continue;
|
||||
|
||||
newInvalid = isSquareInvalid(x, row);
|
||||
if (checkInvalid) {
|
||||
newInvalid = isSquareInvalid(x, row);
|
||||
|
||||
if (newInvalid != square->invalid) {
|
||||
square->invalid = newInvalid;
|
||||
refreshPos(x, row);
|
||||
}
|
||||
}
|
||||
|
||||
if (newInvalid != square->invalid) {
|
||||
square->invalid = newInvalid;
|
||||
if ((newValue != EMPTY_SQUARE) &&
|
||||
(SCRATCH_TEST(square->scratchValues, newValue))) {
|
||||
square->scratchValues ^= (0x1 << newValue);
|
||||
refreshPos(x, row);
|
||||
}
|
||||
}
|
||||
|
@ -336,17 +348,25 @@ void refreshInvalid(tPos col, tPos row, tSquareVal oldValue)
|
|||
for (x = subSquareXStart; x < subSquareXEnd; x++) {
|
||||
if (x == col)
|
||||
continue;
|
||||
if (y == col)
|
||||
if (y == row)
|
||||
continue;
|
||||
|
||||
square = &(SQUARE_XY(x, y));
|
||||
if (square->knownAtStart)
|
||||
continue;
|
||||
|
||||
newInvalid = isSquareInvalid(x, y);
|
||||
if (checkInvalid) {
|
||||
newInvalid = isSquareInvalid(x, y);
|
||||
|
||||
if (newInvalid != square->invalid) {
|
||||
square->invalid = newInvalid;
|
||||
refreshPos(x, y);
|
||||
}
|
||||
}
|
||||
|
||||
if (newInvalid != square->invalid) {
|
||||
square->invalid = newInvalid;
|
||||
if ((newValue != EMPTY_SQUARE) &&
|
||||
(SCRATCH_TEST(square->scratchValues, newValue))) {
|
||||
square->scratchValues ^= (0x1 << newValue);
|
||||
refreshPos(x, y);
|
||||
}
|
||||
}
|
||||
|
@ -366,10 +386,8 @@ bool setValueAtPos(tPos x, tPos y, tSquareVal val)
|
|||
return false;
|
||||
}
|
||||
|
||||
theGame.undo.isValid = true;
|
||||
memcpy(&(theGame.undo.oldSquare), square, sizeof(*square));
|
||||
theGame.undo.x = x;
|
||||
theGame.undo.y = y;
|
||||
theGame.undoValid = true;
|
||||
memcpy(theGame.prevSquares, theGame.squares, sizeof(theGame.squares));
|
||||
|
||||
if (square->value != val) {
|
||||
oldValue = square->value;
|
||||
|
@ -395,7 +413,7 @@ bool setValueAtPos(tPos x, tPos y, tSquareVal val)
|
|||
refreshPos(x,y);
|
||||
|
||||
if (checkValues)
|
||||
refreshInvalid(x, y, oldValue);
|
||||
refreshNeighbours(x, y, val, oldValue);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -409,10 +427,8 @@ bool toggleScratchValueAtPos(tPos x, tPos y, tSquareVal val)
|
|||
return false;
|
||||
}
|
||||
|
||||
theGame.undo.isValid = true;
|
||||
memcpy(&(theGame.undo.oldSquare), square, sizeof(*square));
|
||||
theGame.undo.x = x;
|
||||
theGame.undo.y = y;
|
||||
theGame.undoValid = true;
|
||||
memcpy(theGame.prevSquares, theGame.squares, sizeof(theGame.squares));
|
||||
|
||||
square->scratchValues ^= (0x1 << val);
|
||||
refreshPos(x, y);
|
||||
|
@ -424,49 +440,30 @@ bool toggleScratchValueAtPos(tPos x, tPos y, tSquareVal val)
|
|||
bool undoLastMove(void)
|
||||
{
|
||||
tGameSquare *square;
|
||||
bool update = false;
|
||||
bool checkValues = false;
|
||||
tPos x = theGame.undo.x;
|
||||
tPos y = theGame.undo.y;
|
||||
bool correct;
|
||||
tSquareVal oldValue = EMPTY_SQUARE;
|
||||
tGameSquare *prevSquare;
|
||||
tPos x, y;
|
||||
|
||||
if (!theGame.undo.isValid)
|
||||
if (!theGame.undoValid)
|
||||
return false;
|
||||
|
||||
square = &(SQUARE_XY(x, y));
|
||||
|
||||
if (square->knownAtStart) {
|
||||
return false;
|
||||
}
|
||||
|
||||
theGame.undo.isValid = false;
|
||||
|
||||
if (square->value != theGame.undo.oldSquare.value) {
|
||||
oldValue = square->value;
|
||||
square->value = theGame.undo.oldSquare.value;
|
||||
update = true;
|
||||
checkValues = true;
|
||||
}
|
||||
|
||||
if (square->scratchValues != theGame.undo.oldSquare.scratchValues) {
|
||||
square->scratchValues = theGame.undo.oldSquare.scratchValues;
|
||||
update = true;
|
||||
}
|
||||
|
||||
if (checkValues) {
|
||||
correct = checkValueAtPos(theGame.puzzle, square->value, x, y);
|
||||
if (square->correct != correct) {
|
||||
square->correct = correct;
|
||||
update = true;
|
||||
for (y = 0; y < BOARD_SIZE; y++) {
|
||||
for (x = 0; x < BOARD_SIZE; x++) {
|
||||
square = &(SQUARE_XY(x, y));
|
||||
|
||||
if (square->knownAtStart)
|
||||
continue;
|
||||
|
||||
prevSquare = &(PREV_SQUARE_XY(x, y));
|
||||
if ((square->value != prevSquare->value) ||
|
||||
(square->scratchValues != prevSquare->scratchValues) ||
|
||||
(square->invalid != prevSquare->invalid)) {
|
||||
memcpy(square, prevSquare, sizeof(*square));
|
||||
refreshPos(x, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (update)
|
||||
refreshPos(x,y);
|
||||
|
||||
if (checkValues)
|
||||
refreshInvalid(x, y, oldValue);
|
||||
theGame.undoValid = false;
|
||||
|
||||
return true;
|
||||
}
|
Binary file not shown.
|
@ -516,6 +516,7 @@ void youWon(void)
|
|||
|
||||
bool playGame(void)
|
||||
{
|
||||
static bool firstGame = true;
|
||||
bool shouldSave = false;
|
||||
bool gameLoaded = false;
|
||||
char ch;
|
||||
|
@ -523,33 +524,35 @@ bool playGame(void)
|
|||
initUI();
|
||||
|
||||
textMode();
|
||||
|
||||
printf("\n\nChecking for a saved puzzle...");
|
||||
|
||||
if (loadGame(updatePos)) {
|
||||
bool gotAnswer = false;
|
||||
if (firstGame) {
|
||||
firstGame = false;
|
||||
printf("\n\nChecking for a saved puzzle...");
|
||||
|
||||
printf("\n\nYou have a saved puzzle!\n Would you like to continue it (Y/N)");
|
||||
|
||||
while (!gotAnswer) {
|
||||
ch = cgetc();
|
||||
switch (ch) {
|
||||
case 'y':
|
||||
case 'Y':
|
||||
printf("\n\nLoading your saved puzzle");
|
||||
gameLoaded = true;
|
||||
gotAnswer = true;
|
||||
shouldSave = true;
|
||||
break;
|
||||
|
||||
case 'n':
|
||||
case 'N':
|
||||
gotAnswer = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
printf("\007");
|
||||
break;
|
||||
if (loadGame(updatePos)) {
|
||||
bool gotAnswer = false;
|
||||
|
||||
printf("\n\nYou have a saved puzzle!\n Would you like to continue it (Y/N)");
|
||||
|
||||
while (!gotAnswer) {
|
||||
ch = cgetc();
|
||||
switch (ch) {
|
||||
case 'y':
|
||||
case 'Y':
|
||||
printf("\n\nLoading your saved puzzle");
|
||||
gameLoaded = true;
|
||||
gotAnswer = true;
|
||||
shouldSave = true;
|
||||
break;
|
||||
|
||||
case 'n':
|
||||
case 'N':
|
||||
gotAnswer = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
printf("\007");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue