281 lines
7.3 KiB
C
281 lines
7.3 KiB
C
#include "2048.h"
|
|
#include "utils.h"
|
|
|
|
#include <notcurses/notcurses.h>
|
|
#include <stdlib.h>
|
|
|
|
struct _2048_board *_2048_init_board() {
|
|
allocm(struct _2048_board, board);
|
|
|
|
board->state = true;
|
|
|
|
for (uint y = 0; y < 4; y++)
|
|
for (uint x = 0; x < 4; x++)
|
|
board->cells[y][x] = _2048_init_cell(y, x);
|
|
|
|
return board;
|
|
}
|
|
|
|
struct _2048_cell *_2048_init_cell(uint y, uint x) {
|
|
allocm(struct _2048_cell, cell);
|
|
|
|
cell->x = x;
|
|
cell->y = y;
|
|
cell->level = 0;
|
|
|
|
return cell;
|
|
}
|
|
|
|
void _2048_destroy_board(struct _2048_board *board) {
|
|
for (uint y = 0; y < 4; y++)
|
|
for (uint x = 0; x < 4; x++)
|
|
free(board->cells[y][x]);
|
|
|
|
free(board);
|
|
}
|
|
|
|
void _2048_draw_cell(struct _2048_cell *cell, struct ncplane *plane, uint y, uint x) {
|
|
uint drawx = x / 2, drawy = y / 2;
|
|
|
|
// Draw cell in appropriate place by
|
|
// manipulating the starting x draw points
|
|
switch (cell->x) {
|
|
case 0: // -24
|
|
drawx -= 12;
|
|
case 1: // -12
|
|
drawx -= 12;
|
|
break;
|
|
|
|
case 3: // +12
|
|
drawx += 12;
|
|
case 2: // 0
|
|
drawx += 0;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
// Same for y
|
|
switch (cell->y) {
|
|
case 0: // -8
|
|
drawy -= 6;
|
|
case 1: // -2
|
|
drawy -= 2;
|
|
break;
|
|
|
|
case 3: // +10
|
|
drawy += 6;
|
|
case 2: // +4
|
|
drawy += 4;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
// Put cursor at starting draw position
|
|
ncplane_cursor_move_yx(plane, drawy, drawx);
|
|
|
|
// Top + Bottom sides
|
|
for (uint xd = 1; xd < 12u; xd++) {
|
|
ncplane_putwc_yx(plane, drawy, drawx + xd, 0x2500);
|
|
ncplane_putwc_yx(plane, drawy + 6, drawx + xd, 0x2500);
|
|
}
|
|
|
|
// Left + Right sides
|
|
for (uint yd = 1; yd < 6u; yd++) {
|
|
ncplane_putwc_yx(plane, drawy + yd, drawx, 0x2502);
|
|
ncplane_putwc_yx(plane, drawy + yd, drawx + 12, 0x2502);
|
|
}
|
|
|
|
// Put level inside square
|
|
if (cell->level) {
|
|
struct string *level = int_string(1 << cell->level);
|
|
ncplane_putstr_yx(plane, drawy + 3, drawx + 6 - (level->length / 2), level->s);
|
|
free_string(level);
|
|
}
|
|
}
|
|
|
|
void _2048_draw_board(struct _2048_board *board, struct ncplane *plane, uint py, uint px) {
|
|
py -= 9;
|
|
for (uint y = 0; y < 4; y++)
|
|
for (uint x = 0; x < 4; x++)
|
|
_2048_draw_cell(board->cells[y][x], plane, py, px);
|
|
|
|
uint drawx = px / 2, drawy = py / 2;
|
|
|
|
// Draw intersections
|
|
ncplane_putwc_yx(plane, drawy - 8, drawx - 12, 0x252c);
|
|
ncplane_putwc_yx(plane, drawy - 8, drawx, 0x252c);
|
|
ncplane_putwc_yx(plane, drawy - 8, drawx + 12, 0x252c);
|
|
|
|
ncplane_putwc_yx(plane, drawy - 2, drawx - 24, 0x251c);
|
|
ncplane_putwc_yx(plane, drawy - 2, drawx - 12, 0x253c);
|
|
ncplane_putwc_yx(plane, drawy - 2, drawx, 0x253c);
|
|
ncplane_putwc_yx(plane, drawy - 2, drawx + 12, 0x253c);
|
|
ncplane_putwc_yx(plane, drawy - 2, drawx + 24, 0x2524);
|
|
|
|
ncplane_putwc_yx(plane, drawy + 4, drawx - 24, 0x251c);
|
|
ncplane_putwc_yx(plane, drawy + 4, drawx - 12, 0x253c);
|
|
ncplane_putwc_yx(plane, drawy + 4, drawx, 0x253c);
|
|
ncplane_putwc_yx(plane, drawy + 4, drawx + 12, 0x253c);
|
|
ncplane_putwc_yx(plane, drawy + 4, drawx + 24, 0x2524);
|
|
|
|
ncplane_putwc_yx(plane, drawy + 10, drawx - 24, 0x251c);
|
|
ncplane_putwc_yx(plane, drawy + 10, drawx - 12, 0x253c);
|
|
ncplane_putwc_yx(plane, drawy + 10, drawx, 0x253c);
|
|
ncplane_putwc_yx(plane, drawy + 10, drawx + 12, 0x253c);
|
|
ncplane_putwc_yx(plane, drawy + 10, drawx + 24, 0x2524);
|
|
|
|
ncplane_putwc_yx(plane, drawy + 16, drawx - 12, 0x2534);
|
|
ncplane_putwc_yx(plane, drawy + 16, drawx, 0x2534);
|
|
ncplane_putwc_yx(plane, drawy + 16, drawx + 12, 0x2534);
|
|
|
|
// Draw corners
|
|
ncplane_putwc_yx(plane, drawy - 8, drawx - 24, 0x250c);
|
|
ncplane_putwc_yx(plane, drawy - 8, drawx + 24, 0x2510);
|
|
ncplane_putwc_yx(plane, drawy + 16, drawx - 24, 0x2514);
|
|
ncplane_putwc_yx(plane, drawy + 16, drawx + 24, 0x2518);
|
|
}
|
|
|
|
void _2048_place_random(struct _2048_board *board) {
|
|
bool empty_spot_somewhere = false;
|
|
|
|
for (uint y = 0; y < 4; y++)
|
|
for (uint x = 0; x < 4; x++)
|
|
if (!board->cells[y][x]->level)
|
|
empty_spot_somewhere = true;
|
|
|
|
if (empty_spot_somewhere) {
|
|
uint x = rand() % 4, y = rand() % 4;
|
|
|
|
while (board->cells[y][x]->level) {
|
|
x = rand() % 4;
|
|
y = rand() % 4;
|
|
}
|
|
|
|
// 25% chance to be a 4, 75% chance to be a 2
|
|
board->cells[y][x]->level = (rand() % 4 == 3 ? 2 : 1);
|
|
}
|
|
}
|
|
|
|
void _2048_transpose(struct _2048_board *board) {
|
|
uint temp_board[4][4];
|
|
|
|
for (uint y = 0; y < 4; y++)
|
|
for (uint x = 0; x < 4; x++)
|
|
temp_board[y][x] = board->cells[y][x]->level;
|
|
|
|
for (uint y = 0; y < 4; y++)
|
|
for (uint x = 0; x < 4; x++)
|
|
board->cells[x][y]->level = temp_board[y][x];
|
|
}
|
|
|
|
void _2048_reverse(struct _2048_board *board) {
|
|
uint temp_board[4][4];
|
|
|
|
for (uint y = 0; y < 4; y++)
|
|
for (uint x = 0; x < 4; x++)
|
|
temp_board[y][x] = board->cells[y][x]->level;
|
|
|
|
for (uint y = 0; y < 4; y++)
|
|
for (uint x = 0; x < 4; x++)
|
|
board->cells[y][x]->level = temp_board[y][3 - x];
|
|
}
|
|
|
|
bool _2048_compress(struct _2048_board *board) {
|
|
bool boardChanged = false;
|
|
|
|
for (uint y = 0; y < 4; y++) {
|
|
uint p = 0;
|
|
uint cells[4] = { 0, 0, 0, 0 };
|
|
|
|
for (uint x = 0; x < 4; x++) {
|
|
if (board->cells[y][x]->level) {
|
|
cells[p] = board->cells[y][x]->level;
|
|
|
|
if (x != p)
|
|
boardChanged = true;
|
|
|
|
p++;
|
|
}
|
|
}
|
|
|
|
for (uint x = 0; x < 4; x++)
|
|
board->cells[y][x]->level = cells[x];
|
|
}
|
|
|
|
return boardChanged;
|
|
}
|
|
|
|
bool _2048_merge(struct _2048_board *board) {
|
|
bool boardChanged = false;
|
|
|
|
for (uint y = 0; y < 4; y++) {
|
|
for (uint x = 0; x < 3; x++) {
|
|
if (board->cells[y][x]->level == board->cells[y][x + 1]->level && board->cells[y][x]->level) {
|
|
board->cells[y][x]->level++;
|
|
board->cells[y][x + 1]->level = 0;
|
|
|
|
boardChanged = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
return boardChanged;
|
|
}
|
|
|
|
void _2048_move(struct _2048_board *board, enum _2048_direction dir) {
|
|
switch (dir) {
|
|
case _2048_left: break;
|
|
case _2048_right: _2048_reverse(board); break;
|
|
case _2048_up: _2048_transpose(board); break;
|
|
case _2048_down: _2048_transpose(board); _2048_reverse(board); break;
|
|
}
|
|
|
|
_2048_compress(board);
|
|
_2048_merge(board);
|
|
_2048_compress(board);
|
|
|
|
switch(dir) {
|
|
case _2048_left: break;
|
|
case _2048_right: _2048_reverse(board); break;
|
|
case _2048_up: _2048_transpose(board); break;
|
|
case _2048_down: _2048_reverse(board); _2048_transpose(board); break;
|
|
}
|
|
|
|
_2048_place_random(board);
|
|
}
|
|
|
|
enum _2048_gameState _2048_determineState(struct _2048_board *board) {
|
|
// GG, you got 2048
|
|
for (uint y = 0; y < 4; y++)
|
|
for (uint x = 0; x < 4; x++)
|
|
if (board->cells[y][x]->level > 10) // 2048
|
|
return _2048_reached;
|
|
|
|
// If there is any empty tile, its a normal game
|
|
for (uint y = 0; y < 4; y++)
|
|
for (uint x = 0; x < 4; x++)
|
|
if (!board->cells[y][x]->level)
|
|
return _2048_normal;
|
|
|
|
|
|
// If any move will create a new empty tile, its a normal game
|
|
for (uint y = 0; y < 3; y++)
|
|
for (uint x = 0; x < 3; x++)
|
|
if (board->cells[y][x]->level == board->cells[y + 1][x]->level ||
|
|
board->cells[y][x]->level == board->cells[y][x + 1]->level)
|
|
return _2048_normal;
|
|
|
|
for (uint i = 0; i < 3; i++)
|
|
if (board->cells[3][i]->level == board->cells[3][i + 1]->level ||
|
|
board->cells[i][3]->level == board->cells[i + 1][3]->level)
|
|
return _2048_normal;
|
|
|
|
|
|
// If all else fails, you lose.
|
|
return _2048_cant_move;
|
|
}
|