Add the ability to save and load your game.
This commit is contained in:
parent
0bce2a1da6
commit
45ca073efa
|
@ -7,6 +7,8 @@
|
|||
//
|
||||
|
||||
|
||||
#include <conio.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "game.h"
|
||||
|
@ -15,6 +17,7 @@
|
|||
|
||||
// Macros
|
||||
#define SQUARE_XY(x, y) (theGame.squares[((y) * BOARD_SIZE) + (x)])
|
||||
#define SAVE_GAME_FILE "a2sudoku.game"
|
||||
|
||||
|
||||
// Typedefs
|
||||
|
@ -80,12 +83,94 @@ void startGame(tDifficulty difficulty, tUpdatePosCallback callback)
|
|||
SQUARE_XY(x, y).knownAtStart = true;
|
||||
SQUARE_XY(x, y).correct = true;
|
||||
}
|
||||
refreshPos(x, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void saveGame(void)
|
||||
{
|
||||
FILE *saveFile = fopen(SAVE_GAME_FILE, "wb");
|
||||
if (saveFile != NULL) {
|
||||
bool isValid = true;
|
||||
fwrite(&isValid, sizeof(isValid), 1, saveFile);
|
||||
fwrite(&theGame, sizeof(theGame), 1, saveFile);
|
||||
savePuzzle(theGame.puzzle, saveFile);
|
||||
fclose(saveFile);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void deleteGame(void)
|
||||
{
|
||||
// So, I tried using unlink() from unistd.h but it seems it
|
||||
// does nothing on an Apple // with cc65. Instead, I will
|
||||
// just open the file for writing and close it again which
|
||||
// will leave it empty. That way, there won't be a saved
|
||||
// game in the file.
|
||||
FILE *saveFile = fopen(SAVE_GAME_FILE, "wb");
|
||||
if (saveFile != NULL) {
|
||||
bool isValid = false;
|
||||
fwrite(&isValid, sizeof(isValid), 1, saveFile);
|
||||
fclose(saveFile);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#undef LOAD_GAME_DEBUG
|
||||
bool loadGame(tUpdatePosCallback callback)
|
||||
{
|
||||
bool isValid = false;
|
||||
bool result = false;
|
||||
FILE *saveFile= fopen(SAVE_GAME_FILE, "rb");
|
||||
|
||||
if (saveFile == NULL) {
|
||||
#ifdef LOAD_GAME_DEBUG
|
||||
printf("Cannot open save game file\n");
|
||||
cgetc();
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((fread(&isValid, sizeof(isValid), 1, saveFile) != 1) ||
|
||||
(!isValid)) {
|
||||
fclose(saveFile);
|
||||
#ifdef LOAD_GAME_DEBUG
|
||||
printf("Save is not valid\n");
|
||||
cgetc();
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
if (fread(&theGame, sizeof(theGame), 1, saveFile) != 1) {
|
||||
fclose(saveFile);
|
||||
deleteGame();
|
||||
#ifdef LOAD_GAME_DEBUG
|
||||
printf("Unable to read game from save\n");
|
||||
cgetc();
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
theGame.callback = callback;
|
||||
|
||||
theGame.puzzle = loadPuzzle(saveFile);
|
||||
|
||||
fclose(saveFile);
|
||||
deleteGame();
|
||||
|
||||
if (theGame.puzzle == NULL) {
|
||||
#ifdef LOAD_GAME_DEBUG
|
||||
printf("Unable to read puzzle from save\n");
|
||||
cgetc();
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void refreshAllPos(void)
|
||||
{
|
||||
tPos x, y;
|
||||
|
|
|
@ -43,5 +43,9 @@ extern bool toggleScratchValueAtPos(tPos x, tPos y, tSquareVal val);
|
|||
// Returns false if the last move cannot be undone.
|
||||
extern bool undoLastMove(void);
|
||||
|
||||
extern void saveGame(void);
|
||||
|
||||
extern bool loadGame(tUpdatePosCallback callback);
|
||||
|
||||
|
||||
#endif /* defined(__a2sudoku__game__) */
|
||||
|
|
|
@ -128,4 +128,19 @@ tSquareVal getStartValueAtPos(tPuzzle *puzzle, tPos x, tPos y)
|
|||
bool checkValueAtPos(tPuzzle *puzzle, tSquareVal val, tPos x, tPos y)
|
||||
{
|
||||
return (PUZZLE_SOLVED_VAL(PUZZLE_SQUARE(puzzle, x, y)) == val);
|
||||
}
|
||||
|
||||
|
||||
void savePuzzle(tPuzzle *puzzle, FILE *saveFile)
|
||||
{
|
||||
fwrite(puzzle, sizeof(*puzzle), 1, saveFile);
|
||||
}
|
||||
|
||||
|
||||
tPuzzle *loadPuzzle(FILE *saveFile)
|
||||
{
|
||||
if (fread(&thePuzzle, sizeof(thePuzzle), 1, saveFile) != 1)
|
||||
return NULL;
|
||||
|
||||
return &thePuzzle;
|
||||
}
|
|
@ -9,6 +9,7 @@
|
|||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
|
||||
|
||||
#ifndef __a2sudoku__puzzles__
|
||||
|
@ -50,5 +51,9 @@ extern tSquareVal getStartValueAtPos(struct tPuzzle *puzzle, tPos x, tPos y);
|
|||
|
||||
extern bool checkValueAtPos(struct tPuzzle *puzzle, tSquareVal val, tPos x, tPos y);
|
||||
|
||||
extern void savePuzzle(struct tPuzzle *puzzle, FILE *saveFile);
|
||||
|
||||
extern struct tPuzzle *loadPuzzle(FILE *saveFile);
|
||||
|
||||
|
||||
#endif /* defined(__a2sudoku__puzzles__) */
|
||||
|
|
|
@ -517,24 +517,59 @@ void youWon(void)
|
|||
|
||||
bool playGame(void)
|
||||
{
|
||||
bool shouldSave = false;
|
||||
bool gameLoaded = false;
|
||||
char ch;
|
||||
|
||||
initUI();
|
||||
|
||||
clrscr();
|
||||
printf("\n\nLoading your puzzle for you\n Please be patient...");
|
||||
|
||||
textMode();
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cursorX = 0;
|
||||
cursorY = 0;
|
||||
|
||||
drawGrid();
|
||||
if (!gameLoaded) {
|
||||
clrscr();
|
||||
|
||||
printf("\n\nLoading a new puzzle for you\n Please be patient...");
|
||||
startGame(gameOptions.difficulty, updatePos);
|
||||
}
|
||||
|
||||
startGame(gameOptions.difficulty, updatePos);
|
||||
drawGrid();
|
||||
refreshAllPos();
|
||||
|
||||
graphicsMode();
|
||||
|
||||
while (true) {
|
||||
char ch;
|
||||
bool shouldNotBeep = true;
|
||||
bool shouldRefresh = false;
|
||||
|
||||
|
@ -568,6 +603,11 @@ bool playGame(void)
|
|||
case 'Q':
|
||||
case CH_ESC:
|
||||
shutdownUI();
|
||||
if (shouldSave) {
|
||||
clrscr();
|
||||
printf("\n\nSaving your puzzle so you can continue\n later...");
|
||||
saveGame();
|
||||
}
|
||||
return false;
|
||||
|
||||
case 'i':
|
||||
|
@ -637,51 +677,71 @@ bool playGame(void)
|
|||
case '8':
|
||||
case '9':
|
||||
shouldNotBeep = setValueAtPos(cursorX, cursorY, ch - '0');
|
||||
if (shouldNotBeep)
|
||||
shouldSave = true;
|
||||
break;
|
||||
|
||||
case CH_F1:
|
||||
case '!':
|
||||
shouldNotBeep = toggleScratchValueAtPos(cursorX, cursorY, 1);
|
||||
if (shouldNotBeep)
|
||||
shouldSave = true;
|
||||
break;
|
||||
|
||||
case CH_F2:
|
||||
case '@':
|
||||
shouldNotBeep = toggleScratchValueAtPos(cursorX, cursorY, 2);
|
||||
if (shouldNotBeep)
|
||||
shouldSave = true;
|
||||
break;
|
||||
|
||||
case CH_F3:
|
||||
case '#':
|
||||
shouldNotBeep = toggleScratchValueAtPos(cursorX, cursorY, 3);
|
||||
if (shouldNotBeep)
|
||||
shouldSave = true;
|
||||
break;
|
||||
|
||||
case CH_F4:
|
||||
case '$':
|
||||
shouldNotBeep = toggleScratchValueAtPos(cursorX, cursorY, 4);
|
||||
if (shouldNotBeep)
|
||||
shouldSave = true;
|
||||
break;
|
||||
|
||||
case CH_F5:
|
||||
case '%':
|
||||
shouldNotBeep = toggleScratchValueAtPos(cursorX, cursorY, 5);
|
||||
if (shouldNotBeep)
|
||||
shouldSave = true;
|
||||
break;
|
||||
|
||||
case CH_F6:
|
||||
case '^':
|
||||
shouldNotBeep = toggleScratchValueAtPos(cursorX, cursorY, 6);
|
||||
if (shouldNotBeep)
|
||||
shouldSave = true;
|
||||
break;
|
||||
|
||||
case CH_F7:
|
||||
case '&':
|
||||
shouldNotBeep = toggleScratchValueAtPos(cursorX, cursorY, 7);
|
||||
if (shouldNotBeep)
|
||||
shouldSave = true;
|
||||
break;
|
||||
|
||||
case CH_F8:
|
||||
case '*':
|
||||
shouldNotBeep = toggleScratchValueAtPos(cursorX, cursorY, 8);
|
||||
if (shouldNotBeep)
|
||||
shouldSave = true;
|
||||
break;
|
||||
|
||||
case CH_F9:
|
||||
case '(':
|
||||
shouldNotBeep = toggleScratchValueAtPos(cursorX, cursorY, 9);
|
||||
if (shouldNotBeep)
|
||||
shouldSave = true;
|
||||
break;
|
||||
|
||||
case 'u':
|
||||
|
|
Loading…
Reference in New Issue