#include #include #include #include #include /* *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; }