ttfe/main.c

624 lines
10 KiB
C

#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() {
printf("\e[?1049h\e[?25l]");
struct termios oldtio, newtio;
tcgetattr(STDIN_FILENO, &oldtio);
newtio = oldtio;
newtio.c_lflag &= ~(ICANON | ECHO);
tcsetattr(STDIN_FILENO, TCSANOW, &newtio);
board *b = new_board();
game_loop(b);
printf("\e[?1049l\e[?25h");
tcsetattr(STDIN_FILENO, TCSANOW, &oldtio);
print_score(b);
free_board(b);
}
enum direction get_input()
{
switch(fgetc(stdin)) {
case 'a':
return west;
case 's':
return south;
case 'w':
return north;
case 'd':
return east;
case 'q':
return quit;
default:
return get_input();
}
}
void game_loop(board *b) {
while(move_possible_any(b)) {
place_new_piece(b);
print_board(b);
while(true) {
enum direction d = get_input();
if (d == quit) {
return;
} else if(move_possible(b, d)) {
make_move(b, d);
break;
}
}
}
}
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 center_print(uint n, int width)
{
char s[20] = {'\0'};
int len;
sprintf(s, "%u", n);
len = strlen(s);
if (len >= width) {
printf("%s", s);
} else {
int remaining = width - len;
int spaces_right = remaining / 2;
int spaces_left = remaining - spaces_right;
printf("%*s%s%*s", spaces_left, "", s, spaces_right, "");
}
}
void print_sep(const char *left, const char *right, const char *cross, const char *line)
{
printf("%s", left);
for(int i = 0; i < 4; i++) {
for(int j = 0; j < 6; j++)
printf("%s", line);
if(i == 3)
printf("%s", right);
else
printf("%s", cross);
}
printf("\n");
}
uint mylog2(uint n) {
uint i;
for (i = 0; ! (n & 1); i++)
n >>= 1;
return i;
}
void print_board_line(board *b, int l) {
printf("\u2503");
for(int i = 0; i < 4; i++) {
uint n = b->x[i][l];
if(n == 0) {
printf(" ");
} else {
uint c = mylog2(n);
if (c > 6) {
c -= 6;
printf("\e[1m");
}
printf("\e[3%1um", c);
center_print(n, 6);
printf("\e[0m");
}
if(i == 3)
printf("\u2503");
else
printf("\u2502");
}
printf("\n");
print_sep("\u2503", "\u2503", "\u2502", " ");
if(l == 3)
print_sep("\u2517", "\u251B", "\u2537", "\u2501");
else
print_sep("\u2520", "\u2528", "\u253C", "\u2500");
}
void print_board(board *b) {
printf("\e[2J\e[0;0H");
printf("\e[1mScore: \e[0m%u\n", b->points);
print_sep("\u250F", "\u2513", "\u252F", "\u2501");
for(int i = 0; i < 4; ++i) {
print_board_line(b, i);
}
}
void print_score(board *b) {
printf("\e[1m\e[91mGame Over\e[0m\n\e[1mScore: \e[0m%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
}