summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHaoran S. Diao <0@hairydiode.xyz>2020-02-03 15:04:56 -0800
committerHaoran S. Diao <0@hairydiode.xyz>2020-02-03 15:04:56 -0800
commitaf24a0d667976215456099d85f8b7216c5047f92 (patch)
tree998f8b4236d626a7a0dfc264b28df08dfe711638
Initial Commit
-rw-r--r--Makefile2
-rw-r--r--main.c250
2 files changed, 252 insertions, 0 deletions
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..b974136
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,2 @@
+0x800: main.c
+ gcc main.c -o 0x800
diff --git a/main.c b/main.c
new file mode 100644
index 0000000..0273894
--- /dev/null
+++ b/main.c
@@ -0,0 +1,250 @@
+#include<stdio.h>
+#include<stdlib.h>
+#include<time.h>
+#include<unistd.h>
+#include<termios.h>
+/*
+ *macro for random number from 0, up to but not including x, however this does
+ *not guarantee absolute equal distribution as RAND_MAX % x isn't guaranteed to
+ *be x-1, so all values up to and including RAND_MAX %x will have a slight
+ *advantage
+ */
+#define r(x) (rand() % x)
+/*
+ * Stores the state of the game
+ */
+unsigned int field[4][4] = {0};
+//if victory is 1, then 2048 has been found, if -1, then we ran out of space
+int victory = 0;
+//loop var
+int loop = 1;
+/*
+ * Adds another value randomly to board, if this can not be done declare lose
+ * condition. It also looks for 2048 and handles the win condition
+ */
+void addlose() {
+ // Number of empty spaces left, it this is 0 then this function will
+ // handle the lose condition/ death screen
+ int empty = 0;
+ //loops through and counts empty spaces
+ for (int i = 0; i <4 ; i++) {
+ for (int j =0; j < 4; j++) {
+ if (field[i][j] == 0) {
+ empty++;
+ }
+ //victory detected
+ if (field[i][j] == 2048) {
+ loop = 0;
+ victory = 1;
+ return;
+ }
+ }
+ }
+ //no empty spaces left, lose
+ if (empty == 0) {
+ loop = 0;
+ victory = -1;
+ return;
+ }
+ int addfield = r(empty);
+ int k = 0;
+ for (int i = 0; i < 4; i++) {
+ for (int j = 0; j < 4; j++) {
+ //the kth empty field has been found
+ if (field[i][j] == 0) {
+ if ( k == addfield) {
+ field[i][j] = 2;
+ }
+ k++;
+ }
+ }
+ }
+}
+/*
+ * Shifts the values in field according to dx and dy, handles all logic even for
+ * the case where there is no shift. Returns 1 if it has shifted, return 0 if
+ * not.
+ */
+int shift (int dx, int dy) {
+ //if there is no shift, return
+ if (dx == 0 && dy == 0) {
+ return 0;
+ }
+ int shift = 0;
+ //initial and final x and y values
+ int xi, xf, yi, yf = 0;
+ //increment values, default to going low to high
+ int di, dj = 1;
+ /*
+ * We want it to go from highest x to lowest x if dx is positive, and
+ * the opposite if it's negative, likewise for y values
+ */
+ if (dx > 0) {
+ di = -1;
+ xi = 3;
+ xf = -1;
+ } else {
+ di = 1;
+ xi = 0;
+ xf = 4;
+ }
+ if (dy > 0) {
+ dj = -1;
+ yi = 3;
+ yf = -1;
+ } else {
+ dj = 1;
+ yi = 0;
+ yf = 4;
+ }
+ //loops through and does all the shifting
+ for (int i = xi; i != xf; i += di) {
+ for (int j = yi; j != yf; j += dj) {
+ //finds prime field to compare to
+ int xp = i + dx;
+ int yp = j + dy;
+ //checks that there's an actual shift and that it's in
+ //bounds
+ if (xp == i && yp == j) {
+ //don't do anything
+ } else if (xp < 0 || xp > 3 || yp < 0 || yp > 3) {
+ //don't do anythign either
+ } else {
+ //it shifts and it's in bound
+ //check to see if you should merge
+ if (field[xp][yp] == 0 ||
+ field[xp][yp] == field[i][j]) {
+ //make shift not 0 if that wasn't a zero
+ //being shifted
+ if (field[i][j] != 0) {
+ shift = 1;
+ }
+ //add the current field to the
+ //transformed field, then set current
+ //field to 0 so somebody else can move
+ //in.
+ field[xp][yp] += field[i][j];
+ field[i][j] = 0;
+ }
+ }
+
+ }
+ }
+ return shift;
+}
+int main (int argc, char * argv[]) {
+ //seeding RNG with time
+ srand(time(NULL));
+ //initializes the game board
+ field[r(4)][r(4)] = 2;
+ //sets raw tty mode
+ struct termios term;
+ tcgetattr(STDIN_FILENO, &term);
+ struct termios old_term = term;
+ cfmakeraw(&term);
+ tcsetattr(STDIN_FILENO, TCSANOW, &term);
+ while (loop) {
+ //clears the screen
+ printf("\033[2J");
+ //homes the cursor
+ printf("\033[H");
+ //displays the field
+ for (int i =0; i < 4; i ++) {
+ for (int j = 0; j < 4; j++) {
+ //adds color
+ switch (field[j][i]) {
+ case 0:
+ printf("\033[31m");
+ break;
+ case 2:
+ printf("\033[32m");
+ break;
+ case 4:
+ printf("\033[33m");
+ break;
+ case 8:
+ printf("\033[34m");
+ break;
+ case 16:
+ printf("\033[35m");
+ break;
+ case 32:
+ printf("\033[36m");
+ break;
+ case 64:
+ printf("\033[1;31m");
+ break;
+ case 128:
+ printf("\033[1;32m");
+ break;
+ case 256:
+ printf("\033[30m\033[41m");
+ break;
+ case 512:
+ printf("\033[30m\033[42m");
+ break;
+ case 1024:
+ printf("\033[30m\033[43m");
+ break;
+ case 2048:
+ printf("\033[30m\033[44m");
+ break;
+ }
+ printf("%.3X\033[0;39m\033[49m ",field[j][i]);
+ }
+ printf("\r\n\n");
+ }
+ //Determines the direction that blocks will attempt to move
+ int dx = 0;
+ int dy = 0;
+ //only reads one byte, may get messy with utf-8
+ char input;
+ read(STDIN_FILENO, &input, 1);
+ //write(STDOUT_FILENO, &input, 1);
+ //input handling
+ switch (input) {
+ case 'q':
+ loop = 0;
+ break;
+
+ case 'h':
+ dx = -1;
+ break;
+
+ case 'j':
+ dy = 1;
+ break;
+
+ case 'k':
+ dy = -1;
+ break;
+
+ case 'l':
+ dx = 1;
+ break;
+
+ default:
+ break;
+ }
+ //shifts until it can't shift anymore
+ while ( shift(dx,dy) ) {
+ //pass
+ }
+ //detects win/lose state, add another '2' to field otherwise
+ if (dx != 0 || dy != 0) {
+ addlose();
+ }
+ }
+ //Prints victory or defeat
+ if (victory == 1) {
+ printf("YOU WIN\r\n");
+ } else if (victory == -1) {
+ printf("YOU LOSE\r\n");
+ } else {
+ printf("Goodbye!\r\n");
+ }
+ //restores terminal state
+ printf("\033[0;39m\033[49m");
+ tcsetattr(STDIN_FILENO, TCSANOW, &old_term);
+ return 0;
+}