--- /dev/null
+#include "main.h"
+
+#define DBG 0
+
+bool move_possible_north(board *b);
+bool move_possible_south(board *b);
+bool move_possible_east(board *b);
+bool move_possible_west(board *b);
+
+void move_north(board *b);
+void move_south(board *b);
+void move_east(board *b);
+void move_west(board *b);
+void merge(board *b, const int d);
+void merge_north(board *b);
+void merge_south(board *b);
+void merge_east(board *b);
+void merge_west(board *b);
+
+void place_new_piece(board *b);
+void game_loop(board *b);
+
+board* new_board() {
+ board *tmp = malloc(sizeof(board));
+ init_board(tmp);
+ return tmp;
+}
+
+void init_board(board *b) {
+ for(int i = 0; i < 4; ++i) {
+ for(int j = 0; j < 4; ++j)
+ b->x[i][j] = 0;
+ }
+ b->points = 0;
+ b->num_p = 0;
+ srand((uint)time(NULL));
+ place_new_piece(b);
+ // second piece will be placed in game_loop
+}
+
+void game_start() {
+ board *b = new_board();
+ game_loop(b);
+ print_score(b);
+ free_board(b);
+}
+
+void game_loop(board *b) {
+ char c[16];
+ int d;
+ bool r = true;
+ while(move_possible_any(b)) {
+ place_new_piece(b);
+ print_board(b);
+ INPUT:
+ printf("Make a move:\n");
+ fgets(c, sizeof(c), stdin);
+ switch(c[0]) {
+ case 'h':
+ d = west;
+ break;
+ case 'j':
+ d = south;
+ break;
+ case 'k':
+ d = north;
+ break;
+ case 'l':
+ d = east;
+ break;
+ case 'q':
+ r = false;
+ break;
+ default:
+ printf("Invalid move: %c\n", c[0]);
+ goto INPUT;
+ }
+ if(!r)
+ return;
+ if(!move_possible(b, d)) {
+ printf("Move not possible: %c\n", c[0]);
+ goto INPUT;
+ }
+ make_move(b, d);
+ }
+}
+
+void place_new_piece(board *b) {
+ int prob = rand() % 100;
+ int possibs[32];
+ uint val = 0;
+ int cnt = 0;
+ int pair = 0;
+ int rx = 0, ry = 0;
+ for(int i = 0; i < 4; ++i) {
+ for(int j = 0; j < 4; ++j) {
+ if(b->x[i][j] == 0) {
+ possibs[2*cnt] = i;
+ possibs[2*cnt+1] = j;
+ cnt++;
+ }
+ }
+ }
+ assert(cnt > 0);
+ pair = rand() % cnt;
+ rx = possibs[2*pair];
+ ry = possibs[2*pair+1];
+ assert(b->x[rx][ry] == 0);
+ if(prob > 9)
+ val = 2;
+ else
+ val = 4;
+ b->x[rx][ry] = val;
+ b->num_p++;
+}
+
+void free_board(board *b) {
+ free(b);
+}
+
+void make_move(board *b, const int d) {
+ bool ok = (d == south) ||
+ (d == east) ||
+ (d == north) ||
+ (d == west);
+ assert(ok);
+ /*
+ * checked before already
+ * if(!move_possible(b, d))
+ * return;
+ */
+ move(b, d);
+ merge(b, d);
+ move(b, d);
+}
+
+bool move_possible_any(board *b) {
+ if(b->num_p < 16)
+ return true;
+ if(move_possible_south(b))
+ return true;
+ if(move_possible_east(b))
+ return true;
+ if(move_possible_north(b))
+ return true;
+ if(move_possible_west(b))
+ return true;
+ return false;
+}
+
+bool move_possible(board *b, const int d) {
+ bool ok = (d == south) ||
+ (d == east) ||
+ (d == north) ||
+ (d == west);
+ assert(ok);
+ switch(d) {
+ case north:
+ return move_possible_north(b);
+ case south:
+ return move_possible_south(b);
+ case east:
+ return move_possible_east(b);
+ case west:
+ return move_possible_west(b);
+ default: // will never execute anyway
+ return false;
+ }
+}
+
+void move(board *b, const int d) {
+ bool ok = (d == south) ||
+ (d == east) ||
+ (d == north) ||
+ (d == west);
+ assert(ok);
+ switch(d) {
+ case north:
+ move_north(b);
+ break;
+ case south:
+ move_south(b);
+ break;
+ case east:
+ move_east(b);
+ break;
+ case west:
+ move_west(b);
+ break;
+ default: // will never execute anyway
+ return;
+ }
+}
+
+void merge(board *b, const int d) {
+ bool ok = (d == south) ||
+ (d == east) ||
+ (d == north) ||
+ (d == west);
+ assert(ok);
+ switch(d) {
+ case north:
+ merge_north(b);
+ break;
+ case south:
+ merge_south(b);
+ break;
+ case east:
+ merge_east(b);
+ break;
+ case west:
+ merge_west(b);
+ break;
+ default: // will never execute anyway
+ return;
+ }
+}
+
+bool move_possible_south(board *b) {
+ for(int i = 0; i < 4; ++i) {
+ for(int j = 0; j < 3; ++j) {
+ if(b->x[i][j] != 0) {
+ if(b->x[i][j+1] == 0)
+ return true;
+ if(b->x[i][j] == b->x[i][j+1])
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+bool move_possible_north(board *b) {
+ for(int i = 0; i < 4; ++i) {
+ for(int j = 1; j < 4; ++j) {
+ if(b->x[i][j] != 0) {
+ if(b->x[i][j-1] == 0)
+ return true;
+ if(b->x[i][j] == b->x[i][j-1])
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+bool move_possible_east(board *b) {
+ for(int i = 0; i < 3; ++i) {
+ for(int j = 0; j < 4; ++j) {
+ if(b->x[i][j] != 0) {
+ if(b->x[i+1][j] == 0)
+ return true;
+ if(b->x[i+1][j] == b->x[i][j])
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+bool move_possible_west(board *b) {
+ for(int i = 1; i < 4; ++i) {
+ for(int j = 0; j < 4; ++j) {
+ if(b->x[i][j] != 0) {
+ if(b->x[i-1][j] == 0)
+ return true;
+ if(b->x[i-1][j] == b->x[i][j])
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+void move_north(board *b) {
+ int k;
+ for(int i = 0; i < 4; ++i) {
+ for(int j = 1; j < 4; ++j) {
+ if(b->x[i][j] != 0) {
+ k = j;
+ while(b->x[i][k-1] == 0) {
+ b->x[i][k-1] = b->x[i][k];
+ b->x[i][k] = 0;
+ k--;
+ if(k < 1)
+ break;
+ }
+ }
+ }
+ }
+}
+
+void move_south(board *b) {
+ int k;
+ for(int i = 0; i < 4; ++i) {
+ for(int j = 2; j >= 0; --j) {
+ if(b->x[i][j] != 0) {
+ k = j;
+ while(b->x[i][k+1] == 0) {
+ b->x[i][k+1] = b->x[i][k];
+ b->x[i][k] = 0;
+ k++;
+ if(k > 2)
+ break;
+ }
+ }
+ }
+ }
+}
+
+void move_east(board *b) {
+ int k;
+ for(int i = 2; i >= 0; --i) {
+ for(int j = 0; j < 4; ++j) {
+ if(b->x[i][j] != 0) {
+ k = i;
+ while(b->x[k+1][j] == 0) {
+ b->x[k+1][j] = b->x[k][j];
+ b->x[k][j] = 0;
+ k++;
+ if(k > 2)
+ break;
+ }
+ }
+ }
+ }
+}
+
+void move_west(board *b) {
+ int k;
+ for(int i = 1; i < 4; ++i) {
+ for(int j = 0; j < 4; ++j) {
+ if(b->x[i][j] != 0) {
+ k = i;
+ while(b->x[k-1][j] == 0) {
+ b->x[k-1][j] = b->x[k][j];
+ b->x[k][j] = 0;
+ k--;
+ if(k < 1)
+ break;
+ }
+ }
+ }
+ }
+}
+
+void merge_north(board *b) {
+ for(int i = 0; i < 4; ++i) {
+ for(int j = 1; j < 4; ++j) {
+ if(b->x[i][j] != 0 && b->x[i][j] == b->x[i][j-1]) {
+ b->x[i][j-1] *= 2;
+ b->x[i][j] = 0;
+ b->num_p--;
+ b->points += b->x[i][j-1];
+ }
+ }
+ }
+}
+
+void merge_south(board *b) {
+ for(int i = 0; i < 4; ++i) {
+ for(int j = 2; j >= 0; --j) {
+ if(b->x[i][j] != 0 && b->x[i][j] == b->x[i][j+1]) {
+ b->x[i][j+1] *= 2;
+ b->x[i][j] = 0;
+ b->num_p--;
+ b->points += b->x[i][j+1];
+ }
+ }
+ }
+}
+
+void merge_east(board *b) {
+ for(int i = 2; i >= 0; --i) {
+ for(int j = 0; j < 4; ++j) {
+ if(b->x[i][j] != 0 && b->x[i+1][j] == b->x[i][j]) {
+ b->x[i+1][j] *= 2;
+ b->points += b->x[i+1][j];
+ b->num_p--;
+ b->x[i][j] = 0;
+ }
+ }
+ }
+}
+
+void merge_west(board *b) {
+ for(int i = 1; i < 4; ++i) {
+ for(int j = 0; j < 4; ++j) {
+ if(b->x[i][j] != 0 && b->x[i-1][j] == b->x[i][j]) {
+ b->x[i-1][j] *= 2;
+ b->points += b->x[i-1][j];
+ b->num_p--;
+ b->x[i][j] = 0;
+ }
+ }
+ }
+}
+
+void print_sep() {
+ printf("||--------------------------------------------------------------||\n");
+}
+
+void print_board_line(board *b, int l) {
+ printf("||\t%u\t|\t%u\t|\t%u\t|\t%u\t||\n", b->x[0][l], b->x[1][l], b->x[2][l], b->x[3][l]);
+ print_sep();
+}
+
+void print_board(board *b) {
+ printf("Score: %u\n", b->points);
+ print_sep();
+ for(int i = 0; i < 4; ++i) {
+ print_board_line(b, i);
+ }
+ printf("\n");
+}
+
+void print_score(board *b) {
+ printf("Game Over\nScore:%u\n", b->points);
+}
+
+void merge_test1() {
+ board *b = new_board();
+ b->x[2][0] = 2;
+ b->x[3][0] = 2;
+ merge_east(b);
+ assert(b->x[3][0] == 4);
+}
+
+void merge_test2() {
+ board *b = new_board();
+ b->x[2][0] = 2;
+ b->x[3][0] = 2;
+ print_board(b);
+ move_west(b);
+ print_board(b);
+ merge_west(b);
+ print_board(b);
+ assert(b->x[0][0] == 4);
+}
+
+void merge_test3() {
+ board *b = new_board();
+ b->x[0][0] = 2;
+ b->x[0][1] = 2;
+ print_board(b);
+ merge_north(b);
+ print_board(b);
+ assert(b->x[0][0] == 4);
+}
+
+void merge_test4() {
+ board *b = new_board();
+ b->x[0][2] = 2;
+ b->x[0][3] = 2;
+ print_board(b);
+ merge_south(b);
+ print_board(b);
+ assert(b->x[0][3] == 4);
+}
+
+void move_merge_test1() {
+ board *b = new_board();
+ b->x[2][0] = 4;
+ b->x[3][0] = 2;
+ b->x[3][2] = 2;
+ print_board(b);
+ move_south(b);
+ print_board(b);
+ merge_south(b);
+ print_board(b);
+ assert(b->x[3][3] == 4);
+}
+
+void move_north_test() {
+ board *b = new_board();
+ b->x[2][3] = 8;
+ b->x[2][2] = 0;
+ b->x[2][1] = 0;
+ b->x[2][0] = 0;
+ b->x[0][1] = 2;
+ b->x[0][0] = 0;
+ b->x[3][0] = 4;
+ b->x[3][1] = 2;
+ b->x[3][2] = 3;
+ b->x[3][3] = 16;
+ print_board(b);
+ move_north(b);
+ merge_north(b);
+ move_north(b);
+ print_board(b);
+ assert(b->x[2][0] == 8);
+ assert(b->x[0][0] == 2);
+}
+
+void move_merge_test2() {
+ board *b = new_board();
+ b->x[3][0] = 2;
+ b->x[3][1] = 2;
+ b->x[3][2] = 4;
+ b->x[3][3] = 32;
+ print_board(b);
+ move_south(b);
+ merge_south(b);
+ move_south(b);
+ print_board(b);
+ assert(b->x[3][1] == 4);
+}
+
+void move_merge_test3() {
+ board *b = new_board();
+ b->x[0][0] = 0;
+ b->x[0][1] = 0;
+ b->x[0][2] = 0;
+ b->x[0][3] = 2;
+ b->x[1][0] = 0;
+ b->x[1][1] = 0;
+ b->x[1][2] = 0;
+ b->x[1][3] = 8;
+ b->x[2][0] = 0;
+ b->x[2][1] = 0;
+ b->x[2][2] = 0;
+ b->x[2][3] = 4;
+ b->x[3][0] = 2;
+ b->x[3][1] = 2;
+ b->x[3][2] = 4;
+ b->x[3][3] = 16;
+ print_board(b);
+ move_south(b);
+ merge_south(b);
+ move_south(b);
+ print_board(b);
+ assert(b->x[3][1] == 4);
+}
+
+void run_tests() {
+ /*
+ merge_test1();
+ merge_test2();
+ merge_test3();
+ merge_test4();
+ move_merge_test1();
+ move_north_test();
+ */
+ move_merge_test2();
+ move_merge_test3();
+}
+
+int main() {
+#if DBG
+ run_tests();
+#else
+ game_start();
+#endif
+}