mirror of
https://github.com/jeremysrand/a2sudoku.git
synced 2024-05-31 19:41:35 +00:00
Create the project. Write some code to manage a set of puzzles. Write some code to manage a sudoku game. Start work on the UI for the game.
This commit is contained in:
parent
1a4299854c
commit
6ca2609237
|
@ -17,6 +17,13 @@
|
||||||
9DEE9C2E1B569637003E353E /* prodos_template.dsk */ = {isa = PBXFileReference; lastKnownFileType = file; name = prodos_template.dsk; path = make/prodos_template.dsk; sourceTree = "<group>"; };
|
9DEE9C2E1B569637003E353E /* prodos_template.dsk */ = {isa = PBXFileReference; lastKnownFileType = file; name = prodos_template.dsk; path = make/prodos_template.dsk; sourceTree = "<group>"; };
|
||||||
9DEE9C2F1B569637003E353E /* tail.mk */ = {isa = PBXFileReference; lastKnownFileType = text; name = tail.mk; path = make/tail.mk; sourceTree = "<group>"; };
|
9DEE9C2F1B569637003E353E /* tail.mk */ = {isa = PBXFileReference; lastKnownFileType = text; name = tail.mk; path = make/tail.mk; sourceTree = "<group>"; };
|
||||||
9DEE9C301B569638003E353E /* V2Make.scpt */ = {isa = PBXFileReference; lastKnownFileType = file; name = V2Make.scpt; path = make/V2Make.scpt; sourceTree = "<group>"; };
|
9DEE9C301B569638003E353E /* V2Make.scpt */ = {isa = PBXFileReference; lastKnownFileType = file; name = V2Make.scpt; path = make/V2Make.scpt; sourceTree = "<group>"; };
|
||||||
|
9DEE9C361B569717003E353E /* puzzles.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = puzzles.c; sourceTree = "<group>"; };
|
||||||
|
9DEE9C371B569717003E353E /* puzzles.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = puzzles.h; sourceTree = "<group>"; };
|
||||||
|
9DEE9C381B56979C003E353E /* game.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = game.c; sourceTree = "<group>"; };
|
||||||
|
9DEE9C391B56979C003E353E /* game.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = game.h; sourceTree = "<group>"; };
|
||||||
|
9DEE9C3A1B56CFC0003E353E /* ui.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = ui.c; sourceTree = "<group>"; };
|
||||||
|
9DEE9C3B1B56CFC0003E353E /* ui.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ui.h; sourceTree = "<group>"; };
|
||||||
|
9DEE9C3C1B570AED003E353E /* a2e.hi.s */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.asm; path = a2e.hi.s; sourceTree = "<group>"; };
|
||||||
/* End PBXFileReference section */
|
/* End PBXFileReference section */
|
||||||
|
|
||||||
/* Begin PBXGroup section */
|
/* Begin PBXGroup section */
|
||||||
|
@ -31,6 +38,13 @@
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
9DEE9C261B569637003E353E /* main.c */,
|
9DEE9C261B569637003E353E /* main.c */,
|
||||||
|
9DEE9C361B569717003E353E /* puzzles.c */,
|
||||||
|
9DEE9C371B569717003E353E /* puzzles.h */,
|
||||||
|
9DEE9C381B56979C003E353E /* game.c */,
|
||||||
|
9DEE9C391B56979C003E353E /* game.h */,
|
||||||
|
9DEE9C3A1B56CFC0003E353E /* ui.c */,
|
||||||
|
9DEE9C3B1B56CFC0003E353E /* ui.h */,
|
||||||
|
9DEE9C3C1B570AED003E353E /* a2e.hi.s */,
|
||||||
9DEE9C271B569637003E353E /* Makefile */,
|
9DEE9C271B569637003E353E /* Makefile */,
|
||||||
9DEE9C281B569637003E353E /* make */,
|
9DEE9C281B569637003E353E /* make */,
|
||||||
);
|
);
|
||||||
|
@ -216,6 +230,7 @@
|
||||||
9DEE9C351B569638003E353E /* Release */,
|
9DEE9C351B569638003E353E /* Release */,
|
||||||
);
|
);
|
||||||
defaultConfigurationIsVisible = 0;
|
defaultConfigurationIsVisible = 0;
|
||||||
|
defaultConfigurationName = Release;
|
||||||
};
|
};
|
||||||
/* End XCConfigurationList section */
|
/* End XCConfigurationList section */
|
||||||
};
|
};
|
||||||
|
|
|
@ -5,6 +5,22 @@
|
||||||
<BuildAction
|
<BuildAction
|
||||||
parallelizeBuildables = "YES"
|
parallelizeBuildables = "YES"
|
||||||
buildImplicitDependencies = "YES">
|
buildImplicitDependencies = "YES">
|
||||||
|
<BuildActionEntries>
|
||||||
|
<BuildActionEntry
|
||||||
|
buildForTesting = "YES"
|
||||||
|
buildForRunning = "YES"
|
||||||
|
buildForProfiling = "YES"
|
||||||
|
buildForArchiving = "YES"
|
||||||
|
buildForAnalyzing = "YES">
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "9DEE9C241B569637003E353E"
|
||||||
|
BuildableName = "a2sudoku"
|
||||||
|
BlueprintName = "a2sudoku"
|
||||||
|
ReferencedContainer = "container:a2sudoku.xcodeproj">
|
||||||
|
</BuildableReference>
|
||||||
|
</BuildActionEntry>
|
||||||
|
</BuildActionEntries>
|
||||||
</BuildAction>
|
</BuildAction>
|
||||||
<TestAction
|
<TestAction
|
||||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||||
|
@ -23,6 +39,15 @@
|
||||||
ignoresPersistentStateOnLaunch = "NO"
|
ignoresPersistentStateOnLaunch = "NO"
|
||||||
debugDocumentVersioning = "YES"
|
debugDocumentVersioning = "YES"
|
||||||
allowLocationSimulation = "YES">
|
allowLocationSimulation = "YES">
|
||||||
|
<MacroExpansion>
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "9DEE9C241B569637003E353E"
|
||||||
|
BuildableName = "a2sudoku"
|
||||||
|
BlueprintName = "a2sudoku"
|
||||||
|
ReferencedContainer = "container:a2sudoku.xcodeproj">
|
||||||
|
</BuildableReference>
|
||||||
|
</MacroExpansion>
|
||||||
<AdditionalOptions>
|
<AdditionalOptions>
|
||||||
</AdditionalOptions>
|
</AdditionalOptions>
|
||||||
</LaunchAction>
|
</LaunchAction>
|
||||||
|
@ -32,6 +57,15 @@
|
||||||
useCustomWorkingDirectory = "NO"
|
useCustomWorkingDirectory = "NO"
|
||||||
buildConfiguration = "Release"
|
buildConfiguration = "Release"
|
||||||
debugDocumentVersioning = "YES">
|
debugDocumentVersioning = "YES">
|
||||||
|
<MacroExpansion>
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "9DEE9C241B569637003E353E"
|
||||||
|
BuildableName = "a2sudoku"
|
||||||
|
BlueprintName = "a2sudoku"
|
||||||
|
ReferencedContainer = "container:a2sudoku.xcodeproj">
|
||||||
|
</BuildableReference>
|
||||||
|
</MacroExpansion>
|
||||||
</ProfileAction>
|
</ProfileAction>
|
||||||
<AnalyzeAction
|
<AnalyzeAction
|
||||||
buildConfiguration = "Debug">
|
buildConfiguration = "Debug">
|
||||||
|
|
|
@ -31,12 +31,12 @@ PGM=a2sudoku
|
||||||
# MACHINE = apple2enh
|
# MACHINE = apple2enh
|
||||||
# MACHINE = apple2enh-dos33
|
# MACHINE = apple2enh-dos33
|
||||||
# MACHINE = apple2enh-system
|
# MACHINE = apple2enh-system
|
||||||
# MACHINE = apple2enh-loader
|
MACHINE = apple2enh-loader
|
||||||
# MACHINE = apple2enh-reboot
|
# MACHINE = apple2enh-reboot
|
||||||
|
|
||||||
# Uncomment and set this to your starting address in Apple II memory
|
# Uncomment and set this to your starting address in Apple II memory
|
||||||
# if necessary:
|
# if necessary:
|
||||||
# START_ADDR = 6000
|
START_ADDR = 4000
|
||||||
|
|
||||||
# Set the default CPU to assemble for. You can change this in the
|
# Set the default CPU to assemble for. You can change this in the
|
||||||
# body of a .s file using control commands like ".PC02". Uncomment
|
# body of a .s file using control commands like ".PC02". Uncomment
|
||||||
|
|
2096
a2sudoku/a2e.hi.s
Normal file
2096
a2sudoku/a2e.hi.s
Normal file
File diff suppressed because it is too large
Load Diff
223
a2sudoku/game.c
Normal file
223
a2sudoku/game.c
Normal file
|
@ -0,0 +1,223 @@
|
||||||
|
//
|
||||||
|
// game.c
|
||||||
|
// a2sudoku
|
||||||
|
//
|
||||||
|
// Created by Jeremy Rand on 2015-07-15.
|
||||||
|
// Copyright (c) 2015 Jeremy Rand. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "game.h"
|
||||||
|
#include "puzzles.h"
|
||||||
|
|
||||||
|
|
||||||
|
// Macros
|
||||||
|
#define SQUARE_XY(x, y) (theGame.squares[((y) * BOARD_SIZE) + (x)])
|
||||||
|
|
||||||
|
|
||||||
|
// Typedefs
|
||||||
|
|
||||||
|
typedef struct tGameSquare {
|
||||||
|
tSquareVal value;
|
||||||
|
tScratchValues scratchValues;
|
||||||
|
bool knownAtStart;
|
||||||
|
bool correct;
|
||||||
|
bool invalid;
|
||||||
|
} tGameSquare;
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct tGame {
|
||||||
|
tGameSquare squares[BOARD_SIZE * BOARD_SIZE];
|
||||||
|
struct tPuzzle *puzzle;
|
||||||
|
tUpdatePosCallback callback;
|
||||||
|
} tGame;
|
||||||
|
|
||||||
|
|
||||||
|
// Globals
|
||||||
|
|
||||||
|
tGame theGame;
|
||||||
|
|
||||||
|
|
||||||
|
// Implementation
|
||||||
|
|
||||||
|
void refreshPos(tPos x, tPos y)
|
||||||
|
{
|
||||||
|
tGameSquare *square;
|
||||||
|
|
||||||
|
if (theGame.callback == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
square = &(SQUARE_XY(x, y));
|
||||||
|
theGame.callback(x, y, square->value, square->scratchValues, square->correct, square->invalid);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void startGame(tUpdatePosCallback callback)
|
||||||
|
{
|
||||||
|
tPos x, y;
|
||||||
|
|
||||||
|
theGame.puzzle = getRandomPuzzle();
|
||||||
|
theGame.callback = callback;
|
||||||
|
memset(&(theGame.squares), 0, sizeof(theGame.squares));
|
||||||
|
|
||||||
|
for (y = 0; y < BOARD_SIZE; y++) {
|
||||||
|
for (x = 0; x < BOARD_SIZE; x++) {
|
||||||
|
tSquareVal squareVal = getStartValueAtPos(theGame.puzzle, x, y);
|
||||||
|
|
||||||
|
if (squareVal != EMPTY_SQUARE) {
|
||||||
|
SQUARE_XY(x, y).value = squareVal;
|
||||||
|
SQUARE_XY(x, y).knownAtStart = true;
|
||||||
|
SQUARE_XY(x, y).correct = true;
|
||||||
|
}
|
||||||
|
refreshPos(x, y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void refreshAllPos(void)
|
||||||
|
{
|
||||||
|
tPos x, y;
|
||||||
|
|
||||||
|
for (y = 0; y < BOARD_SIZE; y++) {
|
||||||
|
for (x = 0; x < BOARD_SIZE; x++) {
|
||||||
|
refreshPos(x, y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool isPuzzleSolved(void)
|
||||||
|
{
|
||||||
|
tPos x, y;
|
||||||
|
|
||||||
|
for (y = 0; y < BOARD_SIZE; y++) {
|
||||||
|
for (x = 0; x < BOARD_SIZE; x++) {
|
||||||
|
if (!(SQUARE_XY(x, y).correct))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool isSquareInvalid(tPos col, tPos row)
|
||||||
|
{
|
||||||
|
tSquareVal value = SQUARE_XY(col, row).value;
|
||||||
|
tPos x, y;
|
||||||
|
tPos subSquareXStart, subSquareXEnd;
|
||||||
|
tPos subSquareYStart, subSquareYEnd;
|
||||||
|
|
||||||
|
// Empty is always valid
|
||||||
|
if (value == EMPTY_SQUARE)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
for (y = 0; y < BOARD_SIZE; y++) {
|
||||||
|
// If this value appears somewhere else in the same column, it is invalid
|
||||||
|
if ((y != row) &&
|
||||||
|
(value == SQUARE_XY(col, y).value))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (x = 0; x < BOARD_SIZE; x++) {
|
||||||
|
// If this value appears somewhere else in the same row, it is invalid
|
||||||
|
if ((x != col) &&
|
||||||
|
(value == SQUARE_XY(x, row).value))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Need to find the sub-square for this position and check among them...
|
||||||
|
subSquareXStart = ((col / SUBSQUARE_SIZE) * SUBSQUARE_SIZE);
|
||||||
|
subSquareXEnd = subSquareXStart + SUBSQUARE_SIZE;
|
||||||
|
|
||||||
|
subSquareYStart = ((row / SUBSQUARE_SIZE) * SUBSQUARE_SIZE);
|
||||||
|
subSquareYEnd = subSquareYStart + SUBSQUARE_SIZE;
|
||||||
|
for (y = subSquareYStart; y < subSquareYEnd; y++) {
|
||||||
|
for (x = subSquareXStart; x < subSquareXEnd; x++) {
|
||||||
|
if ((x != col) &&
|
||||||
|
(y != row) &&
|
||||||
|
(value == SQUARE_XY(x, y).value))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we haven't seen this same value in the column, row or subsquare,
|
||||||
|
// then it is not invalid (it is valid).
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool refreshInvalid(tPos col, tPos row)
|
||||||
|
{
|
||||||
|
bool update = false;
|
||||||
|
tPos x, y;
|
||||||
|
|
||||||
|
for (y = 0; y < BOARD_SIZE; y++) {
|
||||||
|
for (x = 0; x < BOARD_SIZE; x++) {
|
||||||
|
bool newInvalid = isSquareInvalid(x, y);
|
||||||
|
if (newInvalid != SQUARE_XY(x, y).invalid) {
|
||||||
|
SQUARE_XY(x, y).invalid = isSquareInvalid(x, y);
|
||||||
|
|
||||||
|
if ((col == x) &&
|
||||||
|
(row == y))
|
||||||
|
update = true;
|
||||||
|
else
|
||||||
|
refreshPos(x, y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return update;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool setValueAtPos(tPos x, tPos y, tSquareVal val)
|
||||||
|
{
|
||||||
|
tGameSquare *square = &(SQUARE_XY(x, y));
|
||||||
|
bool update = false;
|
||||||
|
bool correct;
|
||||||
|
|
||||||
|
if (square->knownAtStart) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (square->value != val) {
|
||||||
|
square->value = val;
|
||||||
|
update = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (square->scratchValues != 0) {
|
||||||
|
square->scratchValues = 0;
|
||||||
|
update = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
correct = checkValueAtPos(theGame.puzzle, val, x, y);
|
||||||
|
if (square->correct != correct) {
|
||||||
|
square->correct = correct;
|
||||||
|
update = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (refreshInvalid(x, y))
|
||||||
|
update = true;
|
||||||
|
|
||||||
|
if (update)
|
||||||
|
refreshPos(x, y);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool toggleScratchValueAtPos(tPos x, tPos y, tSquareVal val)
|
||||||
|
{
|
||||||
|
tGameSquare *square = &(SQUARE_XY(x, y));
|
||||||
|
|
||||||
|
if (square->knownAtStart) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
square->scratchValues ^= (0x1 << val);
|
||||||
|
refreshPos(x, y);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
39
a2sudoku/game.h
Normal file
39
a2sudoku/game.h
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
//
|
||||||
|
// game.h
|
||||||
|
// a2sudoku
|
||||||
|
//
|
||||||
|
// Created by Jeremy Rand on 2015-07-15.
|
||||||
|
// Copyright (c) 2015 Jeremy Rand. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
|
||||||
|
#include "puzzles.h"
|
||||||
|
|
||||||
|
#ifndef __a2sudoku__game__
|
||||||
|
#define __a2sudoku__game__
|
||||||
|
|
||||||
|
|
||||||
|
// Typedefs
|
||||||
|
|
||||||
|
typedef uint16_t tScratchValues;
|
||||||
|
typedef void (*tUpdatePosCallback)(tPos x, tPos y, tSquareVal val, tScratchValues scratch, bool correct, bool invalid);
|
||||||
|
|
||||||
|
|
||||||
|
// API
|
||||||
|
|
||||||
|
extern void startGame(tUpdatePosCallback callback);
|
||||||
|
|
||||||
|
extern void refreshAllPos(void);
|
||||||
|
|
||||||
|
extern void refreshPos(tPos x, tPos y);
|
||||||
|
|
||||||
|
extern bool isPuzzleSolved(void);
|
||||||
|
|
||||||
|
// Returns false if unable to set the value for some reason
|
||||||
|
extern bool setValueAtPos(tPos x, tPos y, tSquareVal val);
|
||||||
|
|
||||||
|
// Returns false if unable to toggle the scratch value for some reason
|
||||||
|
extern bool toggleScratchValueAtPos(tPos x, tPos y, tSquareVal val);
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* defined(__a2sudoku__game__) */
|
|
@ -11,10 +11,16 @@
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <conio.h>
|
#include <conio.h>
|
||||||
|
|
||||||
|
#include "ui.h"
|
||||||
|
|
||||||
|
|
||||||
int main(void)
|
int main(void)
|
||||||
{
|
{
|
||||||
printf("HELLO, WORLD!\n");
|
displayInstructions();
|
||||||
cgetc();
|
|
||||||
|
while (playGame()) {
|
||||||
|
// Loop back and play another game until playGame() returns false.
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
Binary file not shown.
125
a2sudoku/puzzles.c
Normal file
125
a2sudoku/puzzles.c
Normal file
|
@ -0,0 +1,125 @@
|
||||||
|
//
|
||||||
|
// puzzles.c
|
||||||
|
// a2sudoku
|
||||||
|
//
|
||||||
|
// Created by Jeremy Rand on 2015-07-15.
|
||||||
|
// Copyright (c) 2015 Jeremy Rand. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include "puzzles.h"
|
||||||
|
|
||||||
|
|
||||||
|
// Macros
|
||||||
|
|
||||||
|
#define PUZZLE_SOLVED_VAL(square) ((square) & 0xf)
|
||||||
|
#define PUZZLE_START_VAL(square) (((square) & 0x10) ? PUZZLE_SOLVED_VAL(square) : EMPTY_SQUARE)
|
||||||
|
|
||||||
|
#define PUZZLE_SQUARE(puzzle, x, y) ((puzzle)->squares[((y) * BOARD_SIZE) + (x)])
|
||||||
|
|
||||||
|
// PVAL is used to record the value of a square in a puzzle which is unknown at the start of the game.
|
||||||
|
// PVAL is short for "puzzle value"
|
||||||
|
#define PVAL(value) ((value) & 0xf)
|
||||||
|
|
||||||
|
// SVAL is used to record the value of a square in a puzzle which is known at the start of the game.
|
||||||
|
// SVAL is short for "starting value"
|
||||||
|
#define SVAL(value) (((value) & 0xf) | 0x10)
|
||||||
|
|
||||||
|
|
||||||
|
// Typedefs
|
||||||
|
|
||||||
|
typedef uint8_t tPuzzleNum;
|
||||||
|
|
||||||
|
typedef struct tPuzzle {
|
||||||
|
tSquareVal squares[BOARD_SIZE * BOARD_SIZE];
|
||||||
|
} tPuzzle;
|
||||||
|
|
||||||
|
|
||||||
|
// Forward declarations
|
||||||
|
|
||||||
|
tPuzzle puzzles[];
|
||||||
|
|
||||||
|
tPuzzleNum numPuzzles(void);
|
||||||
|
|
||||||
|
|
||||||
|
// Implementation
|
||||||
|
|
||||||
|
tPuzzle *getRandomPuzzle(void)
|
||||||
|
{
|
||||||
|
tPuzzleNum randomPuzzleNum = (rand() % numPuzzles());
|
||||||
|
return &(puzzles[randomPuzzleNum]);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
tSquareVal getStartValueAtPos(tPuzzle *puzzle, tPos x, tPos y)
|
||||||
|
{
|
||||||
|
return PUZZLE_START_VAL(PUZZLE_SQUARE(puzzle, x, y));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool checkValueAtPos(tPuzzle *puzzle, tSquareVal val, tPos x, tPos y)
|
||||||
|
{
|
||||||
|
return (PUZZLE_SOLVED_VAL(PUZZLE_SQUARE(puzzle, x, y)) == val);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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[] = {
|
||||||
|
{
|
||||||
|
{
|
||||||
|
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(void)
|
||||||
|
{
|
||||||
|
return sizeof(puzzles) / sizeof(puzzles[0]);
|
||||||
|
}
|
51
a2sudoku/puzzles.h
Normal file
51
a2sudoku/puzzles.h
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
//
|
||||||
|
// puzzles.h
|
||||||
|
// a2sudoku
|
||||||
|
//
|
||||||
|
// Created by Jeremy Rand on 2015-07-15.
|
||||||
|
// Copyright (c) 2015 Jeremy Rand. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef __a2sudoku__puzzles__
|
||||||
|
#define __a2sudoku__puzzles__
|
||||||
|
|
||||||
|
|
||||||
|
// Defines
|
||||||
|
|
||||||
|
// A subsquare is the 3x3 square inside which each number appears once
|
||||||
|
#define SUBSQUARE_SIZE 3
|
||||||
|
|
||||||
|
// The board has dimensions of the square of the subsquare size
|
||||||
|
#define BOARD_SIZE (SUBSQUARE_SIZE * SUBSQUARE_SIZE)
|
||||||
|
|
||||||
|
#define EMPTY_SQUARE 0
|
||||||
|
|
||||||
|
|
||||||
|
// Forward declarations
|
||||||
|
|
||||||
|
struct tPuzzle;
|
||||||
|
|
||||||
|
|
||||||
|
// Typedefs
|
||||||
|
|
||||||
|
typedef uint8_t tSquareVal;
|
||||||
|
typedef uint8_t tPos;
|
||||||
|
|
||||||
|
|
||||||
|
// API
|
||||||
|
|
||||||
|
extern struct tPuzzle *getRandomPuzzle(void);
|
||||||
|
|
||||||
|
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__) */
|
270
a2sudoku/ui.c
Normal file
270
a2sudoku/ui.c
Normal file
|
@ -0,0 +1,270 @@
|
||||||
|
//
|
||||||
|
// ui.c
|
||||||
|
// a2sudoku
|
||||||
|
//
|
||||||
|
// Created by Jeremy Rand on 2015-07-15.
|
||||||
|
// Copyright (c) 2015 Jeremy Rand. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
|
||||||
|
#include <conio.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <tgi.h>
|
||||||
|
#include <tgi/tgi-mode.h>
|
||||||
|
|
||||||
|
#include "game.h"
|
||||||
|
#include "ui.h"
|
||||||
|
|
||||||
|
|
||||||
|
// Extern symbols for graphics drivers
|
||||||
|
extern char a2e_hi;
|
||||||
|
|
||||||
|
|
||||||
|
// Macros and defines
|
||||||
|
#define SQUARE_WIDTH 29
|
||||||
|
#define SQUARE_HEIGHT 19
|
||||||
|
|
||||||
|
#define THICK_LINE_WIDTH 2
|
||||||
|
#define THIN_LINE_WIDTH 1
|
||||||
|
#define THICK_LINE_HEIGHT 2
|
||||||
|
#define THIN_LINE_HEIGHT 1
|
||||||
|
|
||||||
|
#define TEXT_OFFSET_X 12
|
||||||
|
#define TEXT_OFFSET_Y 6
|
||||||
|
|
||||||
|
#define TOTAL_WIDTH ((BOARD_SIZE * SQUARE_WIDTH) + \
|
||||||
|
((BOARD_SIZE + 1) * THIN_LINE_WIDTH) + \
|
||||||
|
(((BOARD_SIZE / SUBSQUARE_SIZE) + 1) * (THICK_LINE_WIDTH - THIN_LINE_WIDTH)))
|
||||||
|
|
||||||
|
#define TOTAL_HEIGHT ((BOARD_SIZE * SQUARE_HEIGHT) + \
|
||||||
|
((BOARD_SIZE + 1) * THIN_LINE_HEIGHT) + \
|
||||||
|
(((BOARD_SIZE / SUBSQUARE_SIZE) + 1) * (THICK_LINE_HEIGHT - THIN_LINE_HEIGHT)))
|
||||||
|
|
||||||
|
|
||||||
|
// Globals;
|
||||||
|
|
||||||
|
tPos cursorX, cursorY;
|
||||||
|
int screenSquaresX[BOARD_SIZE];
|
||||||
|
int screenSquaresY[BOARD_SIZE];
|
||||||
|
|
||||||
|
|
||||||
|
// Implementation
|
||||||
|
|
||||||
|
|
||||||
|
void initUI(void)
|
||||||
|
{
|
||||||
|
static bool tgi_inited = false;
|
||||||
|
tPos pos;
|
||||||
|
int xPos = 0;
|
||||||
|
int yPos = 0;
|
||||||
|
|
||||||
|
if (tgi_inited)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Install drivers
|
||||||
|
tgi_install(&a2e_hi);
|
||||||
|
|
||||||
|
tgi_init();
|
||||||
|
tgi_clear();
|
||||||
|
|
||||||
|
tgi_setcolor(COLOR_WHITE);
|
||||||
|
for (pos = 0; pos <= BOARD_SIZE; pos++) {
|
||||||
|
if ((pos % SUBSQUARE_SIZE) == 0) {
|
||||||
|
tgi_bar(xPos, 0, xPos + THICK_LINE_WIDTH - 1, TOTAL_HEIGHT - 1);
|
||||||
|
xPos += THICK_LINE_WIDTH;
|
||||||
|
|
||||||
|
tgi_bar(0, yPos, TOTAL_WIDTH - 1, yPos + THICK_LINE_HEIGHT - 1);
|
||||||
|
yPos += THICK_LINE_HEIGHT;
|
||||||
|
} else {
|
||||||
|
tgi_bar(xPos, 0, xPos + THIN_LINE_WIDTH - 1, TOTAL_HEIGHT - 1);
|
||||||
|
xPos += THIN_LINE_WIDTH;
|
||||||
|
|
||||||
|
tgi_bar(0, yPos, TOTAL_WIDTH - 1, yPos + THIN_LINE_HEIGHT - 1);
|
||||||
|
yPos += THIN_LINE_HEIGHT;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pos < BOARD_SIZE) {
|
||||||
|
screenSquaresX[pos] = xPos;
|
||||||
|
screenSquaresY[pos] = yPos;
|
||||||
|
}
|
||||||
|
|
||||||
|
xPos += SQUARE_WIDTH;
|
||||||
|
yPos += SQUARE_HEIGHT;
|
||||||
|
}
|
||||||
|
|
||||||
|
tgi_inited = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void shutdownUI(void)
|
||||||
|
{
|
||||||
|
// Uninstall drivers
|
||||||
|
tgi_uninstall();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void displayInstructions(void)
|
||||||
|
{
|
||||||
|
int seed = 0;
|
||||||
|
|
||||||
|
clrscr();
|
||||||
|
|
||||||
|
// 1111111111222222222233333333334
|
||||||
|
// 1234567890123456789012345678901234567890
|
||||||
|
printf(
|
||||||
|
" Apple ][ Sudoku\n"
|
||||||
|
" By Jeremy Rand\n"
|
||||||
|
"\n"
|
||||||
|
"The goal is to get the numbers from 1 to"
|
||||||
|
"9 uniquely in each column, row and 3x3\n"
|
||||||
|
"subsquare. Move the cursor with I-J-K-L"
|
||||||
|
"or the arrow keys. Press a number key\n"
|
||||||
|
"to enter a value. Press a number key\n"
|
||||||
|
"while holding shift or closed apple to\n"
|
||||||
|
"toggle a scratch value. Press 0 to\n"
|
||||||
|
"clear a square.\n"
|
||||||
|
"\n"
|
||||||
|
"Play ends when the puzzle is solved.\n"
|
||||||
|
"\n"
|
||||||
|
"Press escape or Q to quit at any time.\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");
|
||||||
|
|
||||||
|
// The amount of time the user waits to read the in
|
||||||
|
while (!kbhit())
|
||||||
|
seed++;
|
||||||
|
|
||||||
|
cgetc();
|
||||||
|
srand(seed);
|
||||||
|
|
||||||
|
clrscr();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void updatePos(tPos x, tPos y, tSquareVal val, tScratchValues scratch, bool correct, bool invalid)
|
||||||
|
{
|
||||||
|
int screenX = screenSquaresX[x];
|
||||||
|
int screenY = screenSquaresY[y];
|
||||||
|
int edgeX = screenX + SQUARE_WIDTH - 1;
|
||||||
|
int edgeY = screenY + SQUARE_HEIGHT - 1;
|
||||||
|
char buffer[2];
|
||||||
|
|
||||||
|
tgi_setcolor(COLOR_BLACK);
|
||||||
|
tgi_bar(screenX, screenY, edgeX, edgeY);
|
||||||
|
|
||||||
|
tgi_setcolor(COLOR_WHITE);
|
||||||
|
if (val != EMPTY_SQUARE) {
|
||||||
|
buffer[0] = '0' + val;
|
||||||
|
buffer[1] = '\0';
|
||||||
|
|
||||||
|
tgi_outtextxy(screenX + TEXT_OFFSET_X, screenY + TEXT_OFFSET_Y, buffer);
|
||||||
|
|
||||||
|
if (!correct)
|
||||||
|
tgi_line(screenX, edgeY, edgeX, screenY);
|
||||||
|
|
||||||
|
if (invalid)
|
||||||
|
tgi_line(screenX, edgeY, edgeX, screenY);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((cursorX == x) &&
|
||||||
|
(cursorY == y)) {
|
||||||
|
tgi_line(screenX, screenY, edgeX, screenY);
|
||||||
|
tgi_lineto(edgeX, edgeY);
|
||||||
|
tgi_lineto(screenX, edgeY);
|
||||||
|
tgi_lineto(screenX, screenY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool playGame(void)
|
||||||
|
{
|
||||||
|
initUI();
|
||||||
|
|
||||||
|
cursorX = 0;
|
||||||
|
cursorY = 0;
|
||||||
|
|
||||||
|
startGame(updatePos);
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
char ch = cgetc();
|
||||||
|
|
||||||
|
switch (ch) {
|
||||||
|
case 'h':
|
||||||
|
case 'H':
|
||||||
|
displayInstructions();
|
||||||
|
clrscr();
|
||||||
|
refreshAllPos();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'r':
|
||||||
|
case 'R':
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case 'q':
|
||||||
|
case 'Q':
|
||||||
|
case CH_ESC:
|
||||||
|
shutdownUI();
|
||||||
|
return false;
|
||||||
|
|
||||||
|
case 'i':
|
||||||
|
case 'I':
|
||||||
|
case CH_CURS_UP:
|
||||||
|
if (cursorY != 0) {
|
||||||
|
cursorY--;
|
||||||
|
refreshPos(cursorX, cursorY);
|
||||||
|
refreshPos(cursorX, cursorY + 1);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'j':
|
||||||
|
case 'J':
|
||||||
|
case CH_CURS_LEFT:
|
||||||
|
if (cursorX != 0) {
|
||||||
|
cursorX--;
|
||||||
|
refreshPos(cursorX, cursorY);
|
||||||
|
refreshPos(cursorX + 1, cursorY);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'k':
|
||||||
|
case 'K':
|
||||||
|
case CH_CURS_RIGHT:
|
||||||
|
if (cursorX < BOARD_SIZE - 1) {
|
||||||
|
cursorX++;
|
||||||
|
refreshPos(cursorX, cursorY);
|
||||||
|
refreshPos(cursorX - 1, cursorY);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'm':
|
||||||
|
case 'M':
|
||||||
|
case CH_CURS_DOWN:
|
||||||
|
if (cursorY < BOARD_SIZE - 1) {
|
||||||
|
cursorY++;
|
||||||
|
refreshPos(cursorX, cursorY);
|
||||||
|
refreshPos(cursorX, cursorY - 1);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case '0':
|
||||||
|
case '1':
|
||||||
|
case '2':
|
||||||
|
case '3':
|
||||||
|
case '4':
|
||||||
|
case '5':
|
||||||
|
case '6':
|
||||||
|
case '7':
|
||||||
|
case '8':
|
||||||
|
case '9':
|
||||||
|
setValueAtPos(cursorX, cursorY, ch - '0');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
23
a2sudoku/ui.h
Normal file
23
a2sudoku/ui.h
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
//
|
||||||
|
// ui.h
|
||||||
|
// a2sudoku
|
||||||
|
//
|
||||||
|
// Created by Jeremy Rand on 2015-07-15.
|
||||||
|
// Copyright (c) 2015 Jeremy Rand. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#ifndef __a2sudoku__ui__
|
||||||
|
#define __a2sudoku__ui__
|
||||||
|
|
||||||
|
|
||||||
|
// API
|
||||||
|
|
||||||
|
extern void displayInstructions(void);
|
||||||
|
|
||||||
|
extern bool playGame(void);
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* defined(__a2sudoku__ui__) */
|
Loading…
Reference in New Issue
Block a user