Add puzzle difficulty support. Load and save some game options

This commit is contained in:
Jeremy Rand 2015-07-16 09:40:56 -05:00
parent 91290acec6
commit 8d4120db21
5 changed files with 248 additions and 61 deletions

View File

@ -50,15 +50,15 @@ void refreshPos(tPos x, tPos y)
return;
square = &(SQUARE_XY(x, y));
theGame.callback(x, y, square->value, square->scratchValues, square->correct, square->invalid);
theGame.callback(x, y, square->value, square->scratchValues, square->correct, square->invalid, square->knownAtStart);
}
void startGame(tUpdatePosCallback callback)
void startGame(tDifficulty difficulty, tUpdatePosCallback callback)
{
tPos x, y;
theGame.puzzle = getRandomPuzzle();
theGame.puzzle = getRandomPuzzle(difficulty);
theGame.callback = callback;
memset(&(theGame.squares), 0, sizeof(theGame.squares));
@ -157,9 +157,13 @@ void refreshInvalid(tPos col, tPos row)
for (y = 0; y < BOARD_SIZE; y++) {
for (x = 0; x < BOARD_SIZE; x++) {
newInvalid = isSquareInvalid(x, y);
square = &(SQUARE_XY(x, y));
if (square->knownAtStart)
continue;
newInvalid = isSquareInvalid(x, y);
if (newInvalid != square->invalid) {
square->invalid = newInvalid;
refreshPos(x, y);

View File

@ -21,12 +21,12 @@
// Typedefs
typedef uint16_t tScratchValues;
typedef void (*tUpdatePosCallback)(tPos x, tPos y, tSquareVal val, tScratchValues scratch, bool correct, bool invalid);
typedef void (*tUpdatePosCallback)(tPos x, tPos y, tSquareVal val, tScratchValues scratch, bool correct, bool invalid, bool knownAtStart);
// API
extern void startGame(tUpdatePosCallback callback);
extern void startGame(tDifficulty difficulty, tUpdatePosCallback callback);
extern void refreshAllPos(void);

View File

@ -39,17 +39,25 @@ typedef struct tPuzzle {
// Forward declarations
tPuzzle puzzles[];
tPuzzle easyPuzzles[];
tPuzzle mediumPuzzles[];
tPuzzle hardPuzzles[];
tPuzzleNum numPuzzles(void);
tPuzzleNum numPuzzles(tDifficulty difficulty);
// Implementation
tPuzzle *getRandomPuzzle(void)
tPuzzle *getRandomPuzzle(tDifficulty difficulty)
{
tPuzzleNum randomPuzzleNum = (rand() % numPuzzles());
return &(puzzles[randomPuzzleNum]);
tPuzzleNum randomPuzzleNum = (rand() % numPuzzles(difficulty));
if (difficulty == DIFFICULTY_EASY)
return &(easyPuzzles[randomPuzzleNum]);
else if (difficulty == DIFFICULTY_MEDIUM)
return &(mediumPuzzles[randomPuzzleNum]);
return &(hardPuzzles[randomPuzzleNum]);
}
@ -66,41 +74,9 @@ bool checkValueAtPos(tPuzzle *puzzle, tSquareVal val, tPos x, tPos y)
}
void printPuzzle(tPuzzle *puzzle, bool solution)
{
tPos x, y;
for (y = 0; y < BOARD_SIZE; y++) {
if ((y % SUBSQUARE_SIZE) == 0) {
printf("\n");
}
for (x = 0; x < BOARD_SIZE; x++) {
char displayChar = ' ';
tSquareVal squareVal;
if ((x % SUBSQUARE_SIZE) == 0) {
printf(" ");
}
if (solution) {
squareVal = PUZZLE_SOLVED_VAL(PUZZLE_SQUARE(puzzle, x, y));
} else {
squareVal = PUZZLE_START_VAL(PUZZLE_SQUARE(puzzle, x, y));
}
if (squareVal != EMPTY_SQUARE)
displayChar = '0' + squareVal;
printf(" %c ", displayChar);
}
printf("\n");
}
}
// Puzzle definitions
tPuzzle puzzles[] = {
tPuzzle easyPuzzles[] = {
{
{
PVAL(4),PVAL(3),PVAL(5), SVAL(2),SVAL(6),PVAL(9), SVAL(7),PVAL(8),SVAL(1),
@ -119,7 +95,50 @@ tPuzzle puzzles[] = {
};
tPuzzleNum numPuzzles(void)
tPuzzle mediumPuzzles[] = {
{
{
PVAL(4),PVAL(3),PVAL(5), SVAL(2),SVAL(6),PVAL(9), SVAL(7),PVAL(8),SVAL(1),
SVAL(6),SVAL(8),PVAL(2), PVAL(5),SVAL(7),PVAL(1), PVAL(4),SVAL(9),PVAL(3),
SVAL(1),SVAL(9),PVAL(7), PVAL(8),PVAL(3),SVAL(4), SVAL(5),PVAL(6),PVAL(2),
SVAL(8),SVAL(2),PVAL(6), SVAL(1),PVAL(9),PVAL(5), PVAL(3),SVAL(4),PVAL(7),
PVAL(3),PVAL(7),SVAL(4), SVAL(6),PVAL(8),SVAL(2), SVAL(9),PVAL(1),PVAL(5),
PVAL(9),SVAL(5),PVAL(1), PVAL(7),PVAL(4),SVAL(3), PVAL(6),SVAL(2),SVAL(8),
PVAL(5),PVAL(1),SVAL(9), SVAL(3),PVAL(2),PVAL(6), PVAL(8),SVAL(7),SVAL(4),
PVAL(2),SVAL(4),PVAL(8), PVAL(9),SVAL(5),PVAL(7), PVAL(1),SVAL(3),SVAL(6),
SVAL(7),PVAL(6),SVAL(3), PVAL(4),SVAL(1),SVAL(8), PVAL(2),PVAL(5),PVAL(9)
}
},
};
tPuzzle hardPuzzles[] = {
{
{
PVAL(4),PVAL(3),PVAL(5), SVAL(2),SVAL(6),PVAL(9), SVAL(7),PVAL(8),SVAL(1),
SVAL(6),SVAL(8),PVAL(2), PVAL(5),SVAL(7),PVAL(1), PVAL(4),SVAL(9),PVAL(3),
SVAL(1),SVAL(9),PVAL(7), PVAL(8),PVAL(3),SVAL(4), SVAL(5),PVAL(6),PVAL(2),
SVAL(8),SVAL(2),PVAL(6), SVAL(1),PVAL(9),PVAL(5), PVAL(3),SVAL(4),PVAL(7),
PVAL(3),PVAL(7),SVAL(4), SVAL(6),PVAL(8),SVAL(2), SVAL(9),PVAL(1),PVAL(5),
PVAL(9),SVAL(5),PVAL(1), PVAL(7),PVAL(4),SVAL(3), PVAL(6),SVAL(2),SVAL(8),
PVAL(5),PVAL(1),SVAL(9), SVAL(3),PVAL(2),PVAL(6), PVAL(8),SVAL(7),SVAL(4),
PVAL(2),SVAL(4),PVAL(8), PVAL(9),SVAL(5),PVAL(7), PVAL(1),SVAL(3),SVAL(6),
SVAL(7),PVAL(6),SVAL(3), PVAL(4),SVAL(1),SVAL(8), PVAL(2),PVAL(5),PVAL(9)
}
},
};
tPuzzleNum numPuzzles(tDifficulty difficulty)
{
return sizeof(puzzles) / sizeof(puzzles[0]);
if (difficulty == DIFFICULTY_EASY)
return sizeof(easyPuzzles) / sizeof(easyPuzzles[0]);
else if (difficulty == DIFFICULTY_MEDIUM)
return sizeof(mediumPuzzles) / sizeof(mediumPuzzles[0]);
return sizeof(hardPuzzles) / sizeof(hardPuzzles[0]);
}

View File

@ -25,6 +25,10 @@
#define EMPTY_SQUARE 0
#define DIFFICULTY_EASY 0
#define DIFFICULTY_MEDIUM 1
#define DIFFICULTY_HARD 2
// Forward declarations
@ -33,19 +37,18 @@ struct tPuzzle;
// Typedefs
typedef uint8_t tDifficulty;
typedef uint8_t tSquareVal;
typedef uint8_t tPos;
// API
extern struct tPuzzle *getRandomPuzzle(void);
extern struct tPuzzle *getRandomPuzzle(tDifficulty difficulty);
extern tSquareVal getStartValueAtPos(struct tPuzzle *puzzle, tPos x, tPos y);
extern bool checkValueAtPos(struct tPuzzle *puzzle, tSquareVal val, tPos x, tPos y);
extern void printPuzzle(struct tPuzzle *puzzle, bool solution);
#endif /* defined(__a2sudoku__puzzles__) */

View File

@ -33,6 +33,10 @@ extern char a2e_hi;
#define TEXT_OFFSET_X 12
#define TEXT_OFFSET_Y 6
#define TEXT_UNDERLINE_OFFSET_X -2
#define TEXT_UNDERLINE_OFFSET_Y 9
#define TEXT_UNDERLINE_WIDTH 8
#define TOTAL_WIDTH ((BOARD_SIZE * SQUARE_WIDTH) + \
((BOARD_SIZE + 1) * THIN_LINE_WIDTH) + \
(((BOARD_SIZE / SUBSQUARE_SIZE) + 1) * (THICK_LINE_WIDTH - THIN_LINE_WIDTH)))
@ -48,16 +52,42 @@ extern char a2e_hi;
#define SCRATCH_HEIGHT 6
// Typedefs
typedef struct tOptions {
tDifficulty difficulty;
bool showInvalid;
bool showWrong;
} tOptions;
// Globals;
tPos cursorX, cursorY;
int screenSquaresX[BOARD_SIZE];
int screenSquaresY[BOARD_SIZE];
tOptions gameOptions = {
DIFFICULTY_EASY,
true,
true
};
// Implementation
char *difficultyString(tDifficulty difficulty)
{
if (difficulty == DIFFICULTY_EASY)
return "Easy";
else if (difficulty == DIFFICULTY_MEDIUM)
return "Medium";
return "Hard";
}
void drawGrid(void)
{
tPos pos;
@ -109,16 +139,118 @@ void initUI(void)
}
void loadOptions(void)
{
static bool optionsLoaded = false;
FILE *optionsFile;
if (optionsLoaded)
return;
optionsFile = fopen("a2sudokuopts", "rb");
if (optionsFile != NULL) {
fread(&gameOptions, sizeof(gameOptions), 1, optionsFile);
fclose(optionsFile);
}
optionsLoaded = true;
}
void shutdownUI(void)
{
FILE *optionsFile;
optionsFile = fopen("a2sudokuopts", "wb");
if (optionsFile != NULL) {
fwrite(&gameOptions, sizeof(gameOptions), 1, optionsFile);
fclose(optionsFile);
}
// Uninstall drivers
tgi_uninstall();
}
void textMode(void)
{
asm ("STA %w", 0xc051);
}
void graphicsMode(void)
{
asm ("STA %w", 0xc050);
}
bool setOptions(void)
{
bool shouldUpdate = false;
bool keepLooping = true;
while (keepLooping) {
clrscr();
// 1111111111222222222233333333334
// 1234567890123456789012345678901234567890
printf(
" Apple ][ Sudoku\n"
" By Jeremy Rand\n"
"\n"
"Options:\n"
" Difficulty : %s\n"
" Show invalid values : %s\n"
" Show wrong values : %s\n"
"\n"
"Press D to change difficulty.\n"
"Press I to %sshow invalid values.\n"
"Press W to %sshow wrong values.\n"
"\n"
"Press any other key to return.\n",
difficultyString(gameOptions.difficulty),
(gameOptions.showInvalid ? "On" : "Off"),
(gameOptions.showWrong ? "On" : "Off"),
(gameOptions.showInvalid ? "not " : ""),
(gameOptions.showWrong ? "not " : ""));
switch (cgetc()) {
case 'd':
case 'D':
if (gameOptions.difficulty == DIFFICULTY_HARD)
gameOptions.difficulty = DIFFICULTY_EASY;
else
gameOptions.difficulty++;
break;
case 'i':
case 'I':
gameOptions.showInvalid = !gameOptions.showInvalid;
shouldUpdate = true;
break;
case 'w':
case 'W':
gameOptions.showWrong = !gameOptions.showWrong;
shouldUpdate = true;
break;
default:
keepLooping = false;
break;
}
}
clrscr();
return shouldUpdate;
}
void displayInstructions(void)
{
int seed = 0;
char ch;
loadOptions();
clrscr();
@ -135,25 +267,35 @@ void displayInstructions(void)
"to enter a value. Press a number key\n"
"while holding shift or open apple to\n"
"toggle a scratch value. Press 0 to\n"
"clear a square.\n"
"clear a square. Play ends when the\n"
"puzzle is solved.\n"
"\n"
"Play ends when the puzzle is solved.\n"
" Difficulty : %s\n"
" Show invalid values : %s\n"
" Show wrong values : %s\n"
"\n"
"Press escape or Q to quit at any time.\n"
"Press O to change options.\n"
"Press R to start a new game.\n"
"Press H to see this info again.\n"
"\n"
"\n"
"\n"
" Press any key to start");
" Press O to change options or any other\n"
" key to start",
difficultyString(gameOptions.difficulty),
(gameOptions.showInvalid ? "On" : "Off"),
(gameOptions.showWrong ? "On" : "Off"));
// The amount of time the user waits to read the in
while (!kbhit())
seed++;
cgetc();
ch = cgetc();
srand(seed);
if ((ch == 'o') ||
(ch == 'O'))
setOptions();
clrscr();
}
@ -295,7 +437,7 @@ void drawScratch(tPos x, tPos y, tScratchValues scratch)
}
void updatePos(tPos x, tPos y, tSquareVal val, tScratchValues scratch, bool correct, bool invalid)
void updatePos(tPos x, tPos y, tSquareVal val, tScratchValues scratch, bool correct, bool invalid, bool knownAtStart)
{
int screenX = screenSquaresX[x];
int screenY = screenSquaresY[y];
@ -313,10 +455,19 @@ void updatePos(tPos x, tPos y, tSquareVal val, tScratchValues scratch, bool corr
tgi_outtextxy(screenX + TEXT_OFFSET_X, screenY + TEXT_OFFSET_Y, buffer);
if (!correct)
if (knownAtStart) {
tgi_line(screenX + TEXT_OFFSET_X + TEXT_UNDERLINE_OFFSET_X,
screenY + TEXT_OFFSET_Y + TEXT_UNDERLINE_OFFSET_Y,
screenX + TEXT_OFFSET_X + TEXT_UNDERLINE_OFFSET_X + TEXT_UNDERLINE_WIDTH,
screenY + TEXT_OFFSET_Y + TEXT_UNDERLINE_OFFSET_Y);
}
if ((gameOptions.showWrong) &&
(!correct))
tgi_line(screenX, edgeY, edgeX, screenY);
if (invalid)
if ((gameOptions.showInvalid) &&
(invalid))
tgi_line(screenX, edgeY, edgeX, screenY);
} else if (scratch != 0) {
drawScratch(x, y, scratch);
@ -360,11 +511,12 @@ bool playGame(void)
drawGrid();
startGame(updatePos);
startGame(DIFFICULTY_EASY, updatePos);
while (true) {
char ch;
bool shouldNotBeep = true;
bool shouldRefresh = false;
if (isPuzzleSolved()) {
youWon();
@ -376,9 +528,16 @@ bool playGame(void)
switch (ch) {
case 'h':
case 'H':
textMode();
displayInstructions();
clrscr();
refreshAllPos();
graphicsMode();
break;
case 'o':
case 'O':
textMode();
shouldRefresh = setOptions();
graphicsMode();
break;
case 'r':
@ -492,6 +651,8 @@ bool playGame(void)
if (!shouldNotBeep) {
printf("\007");
}
if (shouldRefresh)
refreshAllPos();
}
return false;