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