Add puzzle difficulty support. Load and save some game options
This commit is contained in:
parent
91290acec6
commit
8d4120db21
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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]);
|
||||
}
|
|
@ -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__) */
|
||||
|
|
185
a2sudoku/ui.c
185
a2sudoku/ui.c
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue