2048/src/2048.c
2023-10-18 18:42:43 -04:00

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;
}