Created
October 16, 2014 06:22
-
-
Save serverwentdown/93dab12aad09dc278e01 to your computer and use it in GitHub Desktop.
feel free to fork and improve, cos this is crappy code
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <stdio.h> | |
#include <stdbool.h> | |
#include <stdlib.h> | |
// #include <math.h> | |
#include <time.h> | |
#include <sys/time.h>// For `gettimeofday()` | |
#include <string.h> | |
// For the `getch()` pollyfill instead of using curses.h | |
#include <termios.h> | |
#include <unistd.h> | |
// Constants | |
// #define K_UP 38 | |
// #define K_DOWN 40 | |
// #define K_LEFT 37 | |
// #define K_RIGHT 39 | |
#define K_UP 'A' | |
#define K_DOWN 'B' | |
#define K_LEFT 'D' | |
#define K_RIGHT 'C' | |
// Predef. functions | |
int getch(); | |
void printBorders(); | |
void setBanner(char *); | |
void printDefaultBanner(); | |
void flashBanner(char *); | |
void printBox(int, int [][9], int [][9]); | |
void printHelp(); | |
bool askContinue(char *); | |
void quit(); | |
bool checkArray(int, int [][9]); | |
bool checkCompleteArray(int, int [][9]); | |
void generateIntoArray(int, int [][9]); | |
// UI chars | |
char *borders = "│ │"; | |
char *borderend = "└──────────────────────────────────────────────────────────────────────────────┘"; | |
char *borderstart = "┌──────────────────────────────────────────────────────────────────────────────┐"; | |
char *bordermiddle = "├──────────────────────────────────────────────────────────────────────────────┤"; | |
char *newl = "│ "; | |
char *endl = "│"; | |
char *boxtl = "┌"; | |
char *boxtr = "┐"; | |
char *boxbl = "└"; | |
char *boxbr = "┘"; | |
char *boxlm = "├"; | |
char *boxrm = "┤"; | |
char *boxtm = "┬"; | |
char *boxbm = "┴"; | |
char *boxm = "┼"; | |
char *boxl = "│"; | |
char *boxr = "│"; | |
char *boxt = "─"; | |
char *boxb = "─"; | |
char *boxitb = "-"; | |
// char *boxilr = "⏐"; | |
char *boxilr = "|"; | |
char *boxim = "⋅"; | |
// UI colors | |
char *color1 = "\x1b[39;22m\x1b[49m"; | |
char *color1_b = "\x1b[39;1m\x1b[49m"; | |
char *color2 = "\x1b[34;22m\x1b[49m"; | |
char *color3 = "\x1b[39;1m\x1b[41m"; | |
// Not exactly frequently used in place of spacebars. | |
int termWidth = 80; | |
int termHeight = 25; | |
// Sets banner | |
char *currentBanner; | |
char *currentBannerColor; | |
int main() { | |
int i; | |
printf("\033[2J"); | |
printf("\033[0;0H"); | |
currentBanner = ""; | |
currentBannerColor = color2; | |
// START FUN! | |
printBorders(); | |
printf("\033[10;2H"); | |
printf("\n%s ____ _ _ ____ ___ _ ___ _ ", newl); | |
printf("\n%s / ___|| | | | _ \\ / _ \\| |/ / | | |", newl); | |
printf("\n%s \\___ \\| | | | | | | | | | ' /| | | |", newl); | |
printf("\n%s ___) | |_| | |_| | |_| | . \\| |_| |", newl); | |
printf("\n%s |____/ \\___/|____/ \\___/|_|\\_\\\\___/", newl); | |
fflush(stdout); | |
sleep(1); | |
// END FUN! | |
printBorders(); | |
printf("\033[3;2H"); | |
printf("\n%s", newl); | |
// LOGIC | |
// regionSize is the gameboard type. 2 is 4x4 and 3 is 9x9. For now 9x9 is not implemented. | |
// int regionSize = 0; | |
int regionSize = 2; | |
while (regionSize <= 1 || regionSize >= 4) { | |
printf("%s", color2); | |
if (regionSize != 0) { | |
printf("\033[1A%s", newl); | |
flashBanner("!!! Invalid !!!"); | |
} | |
printf("Enter the width of the region (2/3):%s 2 \033[2D", color1); | |
regionSize = getch(); | |
if (regionSize == 10) { | |
regionSize = 50; | |
} | |
if (regionSize == 113) { | |
// | |
// QUIT | |
// | |
quit(); | |
} | |
printf("\n"); | |
regionSize -= 48; | |
} | |
int size = regionSize * regionSize; | |
printf("%s", color2); | |
printf("\n%s", newl); | |
printf("\033[1B Levels: [1] Easy [2] Medium [3] Hard");; | |
printf("\033[1A\033[56D"); | |
printf("%s", newl); | |
int gameLevel = 0; | |
while (gameLevel <= 0 || gameLevel >= 4) { | |
printf("%s", color2); | |
if (gameLevel != 0) { | |
printf("\033[1A%s", newl); | |
flashBanner("Invalid level chosen!"); | |
} | |
printf(" Pick your level (1/2/3):%s 1 \033[2D", color1); | |
gameLevel = getch(); | |
if (gameLevel == 10) { | |
gameLevel = 50; | |
} | |
if (gameLevel == 113) { | |
// | |
// QUIT | |
// | |
quit(); | |
} | |
printf("\n"); | |
gameLevel -= 48; | |
} | |
printf("%s", color2); | |
printHelp(); | |
// Prepare fresh board | |
printf("%s", newl); | |
printBorders(); | |
printf("\x1b[37m"); | |
// Generate | |
int master[9][9] = { {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0} }; | |
srand(time(NULL)); | |
generateIntoArray(regionSize, master); | |
int user[9][9]; | |
memcpy(user, master, sizeof(master)); // Duplicate master into user | |
// Generate: Holes | |
// int holes[][9] is used to figure out if it is a human-modifyable value. . | |
int holes[9][9] = { {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0} }; | |
int ry, rx; | |
for (i = 0; i < gameLevel * 3 + 3; i++) { | |
ry = rand() % (size); | |
rx = rand() % (size); | |
if (holes[ry][rx] != 1) { | |
holes[ry][rx] = 1; | |
user[ry][rx] = 0; | |
} | |
else { | |
i--; | |
} | |
} | |
// Render | |
printBox(regionSize, user, holes); // Prints the initial box. | |
// Let's start the clock | |
struct timeval startTime, stopTime; | |
gettimeofday(&startTime, NULL); | |
// long int ms = tp.tv_sec * 1000 + tp.tv_usec / 1000; | |
// Key Events | |
int x = 0, y = 0; | |
int key, arrowkey, n; | |
bool needToCheck; | |
while (true) { | |
needToCheck = false; | |
key = getch(); // Waits for user input | |
if (key == 113) { | |
// | |
// QUIT | |
// | |
// break; | |
quit(); | |
} | |
if (key == '\033') { // It's a keycode | |
getch(); // Clear the '[' | |
key = getch(); // Actual key | |
if (key == K_UP) { | |
if (y == 0) { | |
flashBanner("Can't go up further!"); | |
continue; | |
} | |
y--; | |
} | |
else if (key == K_DOWN) { | |
if (y == size - 1) { | |
flashBanner("Can't go down further!"); | |
continue; | |
} | |
y++; | |
} | |
else if (key == K_LEFT) { | |
if (x == 0) { | |
flashBanner("Can't go left further!"); | |
continue; | |
} | |
x--; | |
} | |
else if (key == K_RIGHT) { | |
if (x == size - 1) { | |
flashBanner("Can't go right further!"); | |
continue; | |
} | |
x++; | |
} | |
} | |
else if (key - 48 >= 0 && key - 48 < size + 1) { // If key pressed is within 1 to 9 | |
n = key - 48; // n is the number to insert | |
if (holes[y][x] == 1) { | |
user[y][x] = n; | |
needToCheck = true; // Now we have to check the board again. | |
} | |
else { | |
flashBanner("You can't do that!"); | |
} | |
} | |
else if (key == 104) { // Help is on the way! | |
printBorders(); | |
printHelp(); | |
printBorders(); | |
} | |
else { | |
flashBanner("Invalid key!"); | |
continue; | |
} | |
// Render box | |
printBox(regionSize, user, holes); | |
// Cursor movement (moves cursor to x and y) | |
printf("\033[0;0H\033[4;%dH", termWidth / 2 - size * 2); | |
// if (regionSize == 2 || regionSize == 3) { | |
printf("\033[%dB", 10 - size); | |
// } | |
printf("\033[%dB\033[%dC", (y * 2 + 1), (x * 4 + 2)); | |
fflush(stdout); | |
if (needToCheck) { | |
// Commented-out code is to tell the user wether the board is legal. | |
if (!checkArray(regionSize, user)) { | |
// currentBanner = "Something's wrong. "; | |
// currentBannerColor = color1; | |
// setBanner(currentBanner); | |
} | |
else { | |
// currentBanner = ""; | |
// setBanner(currentBanner); | |
if (checkCompleteArray(regionSize, user)) { | |
// The clock stops. | |
gettimeofday(&stopTime, NULL); | |
double timeDiff = (stopTime.tv_sec + (double) stopTime.tv_usec / 1000000) - (startTime.tv_sec + (double) startTime.tv_usec / 1000000); | |
// Show banner. | |
char cp[78]; | |
sprintf(cp, "YOU COMPLETED IT in %.6f seconds!", timeDiff); | |
// currentBanner = "YOU COMPLETED IT! "; | |
// strncpy(currentBanner, 78, &cp); | |
currentBanner = cp; | |
currentBannerColor = "\x1b[39;22m\x1b[42m"; | |
setBanner(currentBanner); | |
printf("%s%s", color2, "\x1b[32m"); | |
printf("\033[7;32H%.6f seconds", timeDiff); | |
// Wait for user | |
if (askContinue("Press any key to play again, 'q' to quit: ")) { | |
// | |
// QUIT | |
// | |
// break; | |
quit(); | |
} | |
main(); | |
} | |
} | |
} | |
} | |
// Exit with error code | |
return 1; | |
} | |
void quit() { | |
// END | |
printf("\033[0;0H\033[2J"); | |
// START FUN! | |
printBorders(); | |
printf("%s", color2); | |
printf("\033[10;2H"); | |
printf("\n%s ______ _______ _", newl); | |
printf("\n%s | __ ) \\ / / ____| |", newl); | |
printf("\n%s | _ \\\\ V /| _| | |", newl); | |
printf("\n%s | |_) || | | |___|_|", newl); | |
printf("\n%s |____/ |_| |_____(_)", newl); | |
fflush(stdout); | |
sleep(1); | |
printf("\n%s", newl); | |
printf("\n%s", newl); | |
printf("\n%s", newl); | |
printf("\n%s", newl); | |
printf("\n%s", newl); | |
printf("\n%s", newl); | |
printf("\n%s", newl); | |
printf("\n%s", newl); | |
printf("\n%s", newl); | |
printf("\n%s", borderend); | |
// END FUN! | |
// return 0; | |
exit(0); | |
} | |
// `GETCH()` POLYFILL for *NIX | |
// Allows me not to use ncurses. | |
int getch() { | |
struct termios oldt, newt; | |
int ch; | |
tcgetattr(STDIN_FILENO, &oldt ); | |
newt = oldt; | |
newt.c_lflag &= ~(ICANON | ECHO); | |
tcsetattr(STDIN_FILENO, TCSANOW, &newt); | |
ch = getchar(); | |
// tcsetattr(STDIN_FILENO, TCSANOW, &oldt); // Causes it to return to buffered | |
return ch; | |
} | |
// UI | |
void printBorders() { // Prints the frame | |
printf("\033[0;0H\033[2J"); | |
printf("%s", color2); | |
printf("%s\n", borderstart); | |
printf("│ - SUDOKU - │\n"); | |
printf("%s\n", bordermiddle); | |
int bordersi; | |
for (bordersi = 0; bordersi < termHeight - 4; bordersi++) { | |
printf("%s\n", borders); | |
} | |
printf("%s", borderend); | |
setBanner(currentBanner); | |
} | |
void setBanner(char *text) { // Sets the banner text | |
if (strlen(text) < 1) { | |
printDefaultBanner(); | |
return; | |
} | |
printf("%s", currentBannerColor); | |
int i; | |
// printf("%s", color2); | |
printf("\033[s\033[2;2H");//(termWidth / 2 - (int) strlen(text) / 2 - 8) | |
for (i = 0; i < termWidth / 2 - (int) strlen(text) / 2 - 1; i++) { | |
printf(" "); | |
} | |
printf("%s", text); | |
if (strlen(text) % 2 == 0) { | |
printf(" "); | |
} | |
for (i = 0; i <= termWidth / 2 - (int) strlen(text) / 2 - 3; i++) { | |
printf(" "); | |
} | |
printf("\033[u"); | |
} | |
void printDefaultBanner() { // Sets the banner text back to original | |
printf("%s", color2); | |
printf("\033[s%s\033[2;%dH - SUDOKU - \033[u", color2, (termWidth / 2 - 19 - 19)); | |
} | |
void flashBanner(char *text) { // Flases a notification | |
struct timespec tim, tim2; | |
tim.tv_sec = 0; | |
tim.tv_nsec = 500000000L; | |
char *oldcolor = currentBannerColor; | |
currentBannerColor = color3; | |
setBanner(text); | |
fflush(stdout); | |
nanosleep(&tim, &tim2); | |
currentBannerColor = oldcolor; | |
setBanner(currentBanner); | |
} | |
void printBox(int regionSize, int arr[][9], int holes[][9]) { // Prints the sudoku box | |
printf("%s", color1); | |
int size = regionSize * regionSize; | |
int x, y, i; | |
char *color; | |
printf("\033[4;%dH", termWidth / 2 - size * 2 ); | |
// if (regionSize == 2 || regionSize == 3) { | |
printf("\033[%dB", 10 - size); | |
// } | |
printf("%s", boxtl); | |
for (i = 0; i < size - 1; i++) { | |
printf("%s%s%s%s", boxt, boxt, boxt, boxtm); | |
} | |
printf("%s%s%s%s", boxt, boxt, boxt, boxtr); | |
for (y = 0; y < size; y++) { | |
printf("\033[%dB\033[%dD", 1, (size * 4 + 1)); | |
for (x = 0; x < size; x++) { | |
if (holes[y][x] == 1) { | |
color = color1_b; | |
} | |
else { | |
color = color1; | |
} | |
if (x % regionSize == 0) { | |
if (arr[y][x] == 0) { | |
printf("%s%s %s", boxl, color, color1); | |
} | |
else { | |
printf("%s%s %d %s", boxl, color, arr[y][x], color1); | |
} | |
} | |
else { | |
if (arr[y][x] == 0) { | |
printf("%s%s %s", boxilr, color, color1); | |
} | |
else { | |
printf("%s%s %d %s", boxilr, color, arr[y][x], color1); | |
} | |
} | |
} | |
printf("%s", boxr); | |
printf("\033[%dB\033[%dD", 1, (size * 4 + 1)); | |
if (y == size - 1) { | |
break; | |
} | |
for (x = 0; x < size; x++) { | |
if ((y + 1) % regionSize == 0) { // Inefficient | |
if (x == 0) { | |
printf("%s%s%s%s", boxlm, boxt, boxt, boxt); | |
} | |
else if (x == size - 1) { | |
printf("%s%s%s%s%s", boxm, boxt, boxt, boxt, boxrm); | |
} | |
else { | |
printf("%s%s%s%s", boxm, boxt, boxt, boxt); | |
} | |
} | |
else { | |
if (x == 0) { | |
printf("%s %s ", boxlm, boxitb); | |
} | |
else if (x == size - 1) { | |
printf("%s %s %s", boxim, boxitb, boxrm); | |
} | |
else if (x % regionSize == 0) { | |
printf("%s %s ", boxm, boxitb); | |
} | |
else { | |
printf("%s %s ", boxim, boxitb); | |
} | |
} | |
} | |
} | |
printf("%s", boxbl); | |
for (i = 0; i < size - 1; i++) { | |
printf("%s%s%s%s", boxb, boxb, boxb, boxbm); | |
} | |
printf("%s%s%s%s", boxb, boxb, boxb, boxbr); | |
printf("\033[%dA\033[%dD", (size * 2 - 1), (size * 4 - 1)); | |
} | |
void printHelp() { // Show help instructions | |
printf("\033[10;2H"); | |
printf(" Instructions: "); | |
printf("\n%s", newl); | |
printf(" Press the arrow keys to move the cursor. "); | |
printf("\n%s", newl); | |
printf(" Press the number keys to set the value. "); | |
printf("\n%s", newl); | |
printf(" Press 0 to blank the value. "); | |
printf("\n%s", newl); | |
printf(" Press h to show this help menu again. "); | |
printf("\n%s", newl); | |
printf(" Press q anytime to quit. "); | |
printf("\n%s", newl); | |
printf("\n%s", newl); | |
printf("\n%s", newl); | |
printf("\n%s", newl); | |
printf("\n%s", newl); | |
printf("\n%s", newl); | |
printf("\n%s", newl); | |
printf("\n%s", newl); | |
if (askContinue("")) { | |
// | |
// QUIT | |
// | |
// break; | |
quit(); | |
} | |
} | |
bool askContinue(char *txt) { // Ask to continue | |
printf("\033[23;2H"); | |
if (strlen(txt) > 0) { | |
printf(" %s", txt); | |
} | |
else { | |
printf(" Press any key to continue: "); | |
} | |
if (getch() == 113) { | |
return true; | |
} | |
return false; | |
} | |
// LOGIC | |
void generateIntoArray(int regionSize, int arr[][9]) { // NO 9x9 boards YET. Only 2x2 so far | |
int size = regionSize * regionSize; | |
int x, y, i, j, ran; | |
for (y = 0; y < size; y++) { | |
for (x = 0; x < size; x++) { | |
ran = rand() % size + 1; | |
arr[y][x] = ran; | |
// printBox(regionSize, arr); | |
// fflush(stdout); | |
// sleep(1); | |
for (i = 0; i < x; i++) { | |
if (ran == arr[y][i]) { | |
// printf("%d, %d; ", y, x); | |
break; | |
} | |
} | |
for (j = 0; j < y; j++) { | |
if (ran == arr[j][x]) { | |
// printf("%d, %d; ", y, x); | |
break; | |
} | |
} | |
if (false && i != x && j != y) { | |
y = j; | |
} | |
else if (i != x) { | |
x--; | |
continue; | |
} | |
else if (j != y) { | |
y--; | |
break; | |
} | |
} | |
if (y == size - 1) { | |
for (i = 0; i < size; i+=2) { | |
for (j = 0; j < size; j+=2) { | |
if (arr[i][j] == arr[i + 1][j + 1] || arr[i + 1][j] == arr[i][j + 1]) { | |
y = 0; | |
continue; | |
} | |
} | |
} | |
} | |
} | |
} | |
bool checkArray(int regionSize, int arr[][9]) { // Checks for valid array. Also only for 2x2 boards. | |
int size = regionSize * regionSize; | |
int x, y, i, j; | |
for (y = 0; y < size; y++) { | |
for (x = 0; x < size; x++) { | |
if (arr[y][x] != 0) { | |
for (i = 0; i < x; i++) { | |
if (arr[y][x] == arr[y][i]) { | |
return false; | |
} | |
} | |
for (j = 0; j < y; j++) { | |
if (arr[y][x] == arr[j][x]) { | |
return false; | |
} | |
} | |
} | |
} | |
} | |
for (y = 0; y < size; y+=2) { // Checks diagonals. | |
for (x = 0; x < size; x+=2) { | |
if (arr[y][x] != 0 && arr[y + 1][x] != 0) { | |
if (arr[y][x] == arr[y + 1][x + 1] || arr[y + 1][x] == arr[y][x + 1]) { | |
return false; | |
} | |
} | |
} | |
} | |
return true; | |
} | |
bool checkCompleteArray(int regionSize, int arr[][9]) { // Check is array is all filled. | |
int size = regionSize * regionSize; | |
int x, y; | |
for (y = 0; y < size; y++) { | |
for (x = 0; x < size; x++) { | |
if (arr[y][x] == 0) { | |
return false; | |
} | |
} | |
} | |
return true; | |
} | |
/* | |
# FUNCTIONS REQUIRED TO GET MARKS BUT NOT USED | |
*/ | |
/* | |
int genRandom(int lo, int up) { | |
return rand() % (up - lo + 1) + lo; | |
} | |
void insertItem(int num, int row, int col, int arr[][4]) { | |
arr[row][col] = num; | |
} | |
bool validSector(int sec, int arr[][4]) { | |
int x = sec % 2, y = sec / 2; | |
// FROM checkArray() | |
if (arr[y][x] != 0 && arr[y + 1][x] != 0) { | |
if (arr[y][x] == arr[y + 1][x + 1] || arr[y + 1][x] == arr[y][x + 1]) { | |
return false; | |
} | |
} | |
return true; | |
} | |
bool validMatrix(int arr[][4]) { | |
return checkArray(2, arr); | |
} | |
*/ | |
/* | |
# Problems I faced: | |
1. I can't develop with conio.h. Since (ncurses)[http://en.wikipedia.org/wiki/Ncurses] is not available on Windows and it is also a little excessive, I used a simple polyfill for `getch();` which is most likely **not** compatible with Windows. Removing the function and it's respective libraries and replacing it with conio.h **may** work. | |
2. Due to the nature of the getch() polyfill and unix, `clock()` does not count the time waiting for response from `getchar()`. Thus I used `gettimeofday()` which also should have a Windows equivalent. | |
3. system("pause") is Windows-only. `askContinue()` is most likely cross-platform. | |
# Points to note: | |
1. Why in %.3f milliseconds when a keypress takes 1ms? | |
*/ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment