kolibrios/programs/games/2048/board.c

544 lines
13 KiB
C
Raw Permalink Normal View History

#include "board.h"
#include "config.h"
__u8 board_need_config = true;
rect base_cell = {0};
tile null_tile = {0};
struct {
rect draw; // background rect
rect cell_map[BOARD_MAP_SIZE]; // background cells array
tile tile_map[BOARD_MAP_SIZE]; // tiles array
__u16 empty_index[BOARD_MAP_SIZE];// empty cells indexes
__u16 empty_count; // empty cells count
__u32 score;
__u32 highscore;
} board = {0};
// Get tile index for row and column
__u16 board_index(__u16 row, __u16 column) {
return column + row * BOARD_COUNT;
}
// Get tile position (as point with eow and column) for index
point board_position(__u16 index) {
point p = {
.x = index % BOARD_COUNT,
.y = index / BOARD_COUNT
};
return p;
}
// Calculate cell rect for row and column
rect position2cell(point p) {
rect c = {0};
c.width = base_cell.width;
c.height = base_cell.height;
c.x = board.draw.x + BOARD_SPACING + p.x * (c.width + BOARD_SPACING);
c.y = board.draw.y + BOARD_SPACING + p.y * (c.height + BOARD_SPACING);
return c;
}
// Update information about empty cells
void board_update_empty_info();
// Represent tile array as pointers array
void board_to_tempboard(tile* temp[]);
// Fill tile array with tiles from pointers array
void board_from_tempboard(tile* temp[], __u8 forward);
// Move tile inside a pointer array
void tempboard_move_tile(tile* temp[], __u16 from, __u16 to);
// Merge tiles inside a pointer array
void tempboard_merge_tile(tile* temp[], __u16 from, __u16 to);
// Random number generator
__u32 random_u32(__u32 max);
void board_init(rect* r)
{
// seed for random number generator
srand(__menuet__getsystemclock());
__u16 cell_size = (r->width - BOARD_SPACING * (BOARD_COUNT + 1)) / BOARD_COUNT;
base_cell.width = cell_size;
base_cell.height = cell_size;
null_tile.value = 0;
null_tile.animate = false;
null_tile.ani_step = ANI_APPEAR_STEP;
null_tile.merged = false;
board.score = 0;
board.draw = *r;
canvas_init(r);
canvas_fill(BOARD_BG_COLOR);
__u16 i = 0;
for (i = 0; i < BOARD_MAP_SIZE; i++)
{
board.cell_map[i] = position2cell(board_position(i));
board.tile_map[i] = null_tile;
}
__u8 loaded = false;
__u8 empty_config = true;
if (board_need_config)
{
board_need_config = false;
config_state state = {0};
loaded = config_load(&state);
if(loaded)
{
board.score = state.score;
board.highscore = state.highscore;
i = 0;
for (i = 0; i < BOARD_MAP_SIZE; i++)
{
if (state.value_map[i])
{
empty_config = false;
board_add_tile(state.value_map[i],i);
}
}
}
}
if (!loaded || empty_config)
{
i = 0;
for (i = 0; i < START_COUNT; i++)
{
board_add_random_tile();
}
}
board_redraw();
}
void board_delete()
{
config_state state = {0};
state.score = board.score;
state.highscore = board.highscore;
int i = 0;
for (i = 0; i < BOARD_MAP_SIZE; i++)
state.value_map[i] = board.tile_map[i].value;
config_save(&state);
canvas_delete();
}
void board_redraw()
{
__u16 i = 0;
__u8 animate = false;
__u8 last_animate = false;
do
{
canvas_fill(BOARD_BG_COLOR);
for (i = 0; i < BOARD_MAP_SIZE; i++)
{
canvas_draw_rect(&board.cell_map[i],CELL_COLOR);
}
animate = false;
last_animate = false;
for (i = 0; i < BOARD_MAP_SIZE; i++)
{
tile* t = &board.tile_map[i];
last_animate = tile_draw(t);
if (last_animate)
{
animate = last_animate;
}
}
canvas_paint();
if (animate)
{
__menuet__delay100(ANI_DELAY);
}
}
while (animate);
}
__u8 board_up()
{
__u8 moved = false;
__u16 row = 0;
__u16 column = 0;
__u16 ind = 0;
__u16 preind = 0;
tile* indtile = 0;
tile* pretile = 0;
tile* temp_board[BOARD_MAP_SIZE] = {0};
board_to_tempboard(temp_board);
for (column = 0; column < BOARD_COUNT; column++)
{
for (row = 0; row < BOARD_COUNT; row++)
{
if (row > 0)
{
ind = board_index(row,column);
indtile = temp_board[ind];
if (indtile)
{
preind = board_index(row - 1,column);
pretile = temp_board[preind];
if (!pretile)
{
moved = true;
tempboard_move_tile(temp_board,ind,preind);
row = 0;
}
else if (tile_mergeable(indtile,pretile))
{
moved = true;
board.score += indtile->value * 2;
if (board.score > board.highscore)
board.highscore = board.score;
tempboard_merge_tile(temp_board,ind,preind);
row = 0;
}
}
}
}
}
board_from_tempboard(temp_board,true);
return moved;
}
__u8 board_down()
{
__u8 moved = false;
__u16 row = 0;
__u16 column = 0;
__u16 ind = 0;
__u16 preind = 0;
tile* indtile = 0;
tile* pretile = 0;
tile* temp_board[BOARD_MAP_SIZE] = {0};
board_to_tempboard(temp_board);
for (column = 0; column < BOARD_COUNT; column++)
{
row = BOARD_COUNT;
while (row--)
{
if ((BOARD_COUNT - row) > 1)
{
ind = board_index(row,column);
indtile = temp_board[ind];
if (indtile)
{
preind = board_index(row + 1,column);
pretile = temp_board[preind];
if (!pretile)
{
moved = true;
tempboard_move_tile(temp_board,ind,preind);
row = BOARD_COUNT;
}
else if (tile_mergeable(indtile,pretile))
{
moved = true;
board.score += indtile->value * 2;
if (board.score > board.highscore)
board.highscore = board.score;
tempboard_merge_tile(temp_board,ind,preind);
row = BOARD_COUNT;
}
}
}
}
}
board_from_tempboard(temp_board,false);
return moved;
}
__u8 board_left()
{
__u8 moved = false;
__u16 row = 0;
__u16 column = 0;
__u16 ind = 0;
__u16 preind = 0;
tile* indtile = 0;
tile* pretile = 0;
tile* temp_board[BOARD_MAP_SIZE] = {0};
board_to_tempboard(temp_board);
for (row = 0; row < BOARD_COUNT; row++)
{
for (column = 0; column < BOARD_COUNT; column++)
{
if (column > 0)
{
ind = board_index(row,column);
indtile = temp_board[ind];
if (indtile)
{
preind = board_index(row,column - 1);
pretile = temp_board[preind];
if (!pretile)
{
moved = true;
tempboard_move_tile(temp_board,ind,preind);
column = 0;
}
else if (tile_mergeable(indtile,pretile))
{
moved = true;
board.score += indtile->value * 2;
if (board.score > board.highscore)
board.highscore = board.score;
tempboard_merge_tile(temp_board,ind,preind);
column = 0;
}
}
}
}
}
board_from_tempboard(temp_board,true);
return moved;
}
__u8 board_right()
{
__u8 moved = false;
__u16 row = 0;
__u16 column = 0;
__u16 ind = 0;
__u16 preind = 0;
tile* indtile = 0;
tile* pretile = 0;
tile* temp_board[BOARD_MAP_SIZE] = {0};
board_to_tempboard(temp_board);
for (row = 0; row < BOARD_COUNT; row++)
{
column = BOARD_COUNT;
while (column--)
{
if ((BOARD_COUNT - column) > 1)
{
ind = board_index(row,column);
indtile = temp_board[ind];
if (indtile)
{
preind = board_index(row,column + 1);
pretile = temp_board[preind];
if (!pretile)
{
moved = true;
tempboard_move_tile(temp_board,ind,preind);
column = BOARD_COUNT;
}
else if (tile_mergeable(indtile,pretile))
{
moved = true;
board.score += indtile->value * 2;
if (board.score > board.highscore)
board.highscore = board.score;
tempboard_merge_tile(temp_board,ind,preind);
column = BOARD_COUNT;
}
}
}
}
}
board_from_tempboard(temp_board,false);
return moved;
}
__u8 board_add_random_tile()
{
board_update_empty_info();
if (board.empty_count)
{
__u16 rnd_av = random_u32(board.empty_count);
rnd_av = board.empty_index[rnd_av];
__u32 rnd_value = (random_u32(10) < 9) ? 2 : 4;
board_add_tile(rnd_value,rnd_av);
}
return board.empty_count;
}
void board_add_tile(__u32 value, __u16 index)
{
tile* av_tile = &board.tile_map[index];
av_tile->value = value;
av_tile->animate = true;
av_tile->ani_step = ANI_APPEAR_STEP;
av_tile->transition = position2cell(board_position(index));
av_tile->cell.x = av_tile->transition.x + base_cell.width / 2;
av_tile->cell.y = av_tile->transition.y + base_cell.height / 2;
av_tile->cell.width = 0;
av_tile->cell.height = 0;
}
__u8 board_has_moves()
{
__u16 ind = 0;
__u16 next = 0;
__u16 step = 0;
__u16 pos = 0;
for (step = 0; step < BOARD_COUNT; step++)
{
for (pos = 0; pos < BOARD_COUNT; pos++)
{
// check horizontal
ind = board_index(step,pos);
next = board_index(step,pos + 1);
if (!board.tile_map[ind].value ||
(((pos + 1) < BOARD_COUNT) &&
(!board.tile_map[next].value ||
(board.tile_map[ind].value == board.tile_map[next].value)
)
)
)
return true;
// check vertical
ind = board_index(pos,step);
next = board_index(pos + 1,step);
if (!board.tile_map[ind].value ||
(((pos + 1) < BOARD_COUNT) &&
(!board.tile_map[next].value ||
(board.tile_map[ind].value == board.tile_map[next].value)
)
)
)
return true;
}
}
return false;
}
__u32 board_score()
{
return board.score;
}
__u32 board_highscore()
{
return board.highscore;
}
void board_update_empty_info()
{
board.empty_count = 0;
__u16 i = 0;
for (i = 0; i < BOARD_MAP_SIZE; i++)
{
if (!board.tile_map[i].value)
{
board.empty_index[board.empty_count] = i;
board.empty_count++;
}
}
}
void board_to_tempboard(tile* temp[])
{
__u16 ind = 0;
for (ind = 0; ind < BOARD_MAP_SIZE; ind++)
{
tile* bt = &board.tile_map[ind];
if (bt->value)
{
temp[ind] = bt;
}
}
}
void board_from_tempboard(tile *temp[], __u8 forward)
{
__u16 ind = 0;
if (forward)
{
for (ind = 0; ind < BOARD_MAP_SIZE; ind++)
{
tile* bt = &board.tile_map[ind];
tile* tt = temp[ind];
if (tt)
{
*bt = *tt;
bt->transition = position2cell(board_position(ind));
}
else
{
*bt = null_tile;
}
}
}
else
{
ind = BOARD_MAP_SIZE;
while (ind--)
{
tile* bt = &board.tile_map[ind];
tile* tt = temp[ind];
if (tt)
{
*bt = *tt;
bt->transition = position2cell(board_position(ind));
}
else
{
*bt = null_tile;
}
}
}
}
void tempboard_move_tile(tile* temp[], __u16 from, __u16 to)
{
temp[to] = temp[from];
temp[to]->animate = true;
temp[to]->ani_step = ANI_MOVE_STEP;
temp[from] = 0;
}
void tempboard_merge_tile(tile* temp[], __u16 from, __u16 to)
{
temp[from]->merged = true;
temp[from]->merged_rect = temp[to]->cell;
tempboard_move_tile(temp,from,to);
}
__u32 random_u32(__u32 max)
{
return ((rand() * 1.0) / RAND_MAX) * max;
}