Skip to content

Instantly share code, notes, and snippets.

@serverwentdown
Created October 16, 2014 06:22
Show Gist options
  • Save serverwentdown/93dab12aad09dc278e01 to your computer and use it in GitHub Desktop.
Save serverwentdown/93dab12aad09dc278e01 to your computer and use it in GitHub Desktop.
feel free to fork and improve, cos this is crappy code
#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