initial commit
This commit is contained in:
commit
222d0166f9
4
README
Normal file
4
README
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
My own toy implementation of 2048 on the command line.
|
||||||
|
|
||||||
|
It supports hjkl for movements and q for quit.
|
||||||
|
|
554
main.c
Normal file
554
main.c
Normal file
@ -0,0 +1,554 @@
|
|||||||
|
#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
|
||||||
|
}
|
38
main.h
Normal file
38
main.h
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
#ifndef BOARD_H_
|
||||||
|
#define BOARD_H_
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
typedef struct board board;
|
||||||
|
typedef unsigned int uint;
|
||||||
|
typedef unsigned long ulong;
|
||||||
|
|
||||||
|
enum direction {
|
||||||
|
north,
|
||||||
|
south,
|
||||||
|
west,
|
||||||
|
east,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct board {
|
||||||
|
uint x[4][4];
|
||||||
|
uint points;
|
||||||
|
uint num_p;
|
||||||
|
};
|
||||||
|
|
||||||
|
board *new_board();
|
||||||
|
void init_board(board*);
|
||||||
|
void free_board(board*);
|
||||||
|
bool move_possible_any(board*);
|
||||||
|
bool move_possible(board*, const int d);
|
||||||
|
void make_move(board*, const int d);
|
||||||
|
void move(board*, const int d);
|
||||||
|
|
||||||
|
void print_board(board*);
|
||||||
|
void print_score(board*);
|
||||||
|
|
||||||
|
#endif
|
Loading…
x
Reference in New Issue
Block a user