I have a working snake

This commit is contained in:
Андреев Григорий 2026-04-07 22:56:25 +03:00
parent 1c2208041b
commit d76095ca85
3 changed files with 67 additions and 33 deletions

1
.gitignore vendored
View File

@ -9,6 +9,7 @@ tools/*
user/*
!user/*.*
user/*.o
snake/snake
.idea
cmake-build-debug

Binary file not shown.

View File

@ -46,10 +46,14 @@ typedef enum {
snake_direction_bottom = 3,
} snake_direction_t;
#define WALKING_ANIM_TIME_SLOW 500
#define HATCHING_ANIM_TIME_SLOW 200
#define WALKING_ANIM_TIME_VERY_SLOW 600
#define HATCHING_ANIM_TIME_VERY_SLOW 400
#define WALKING_ANIM_TIME_SLOW 450
#define HATCHING_ANIM_TIME_SLOW 150
#define WALKING_ANIM_TIME_FAST 200
#define HATCHING_ANIM_TIME_FAST 80
#define HATCHING_ANIM_TIME_FAST 40
#define WEIRD_SOUND 400
typedef enum {
waiting_reason_step,
@ -68,6 +72,7 @@ typedef enum {
struct Snake {
bool is_space_pressed;
bool is_shift_pressed;
game_screen_t game_screen;
bool have_game;
/* from 0 to PLAYABLE_MAPS_COUNT - 1 */
@ -101,14 +106,15 @@ struct Snake {
/* This stuff regulates game flow and animation progress */
waiting_reason_t waiting_reason;
uint32_t waiting_time; // Measures how much time we already accumulated
bool is_time_sped_up;
int animation_speed_boost;
} snake = {1};
void init_snake() {
snake.is_space_pressed = false;
snake.is_shift_pressed = false;
snake.game_screen = game_screen_pause;
snake.have_game = false;
snake.selected_map_index = 0;
snake.selected_map_index = 6;
snake.gamemode_apples_count = SNAKE_GAMEMODE_DEFAULT_APPLES;
snake.r_eng = (RandomEngine){.state = time_ms()};
/* snake_head and snake_pupa and snake.world
@ -144,8 +150,10 @@ void start_snake_game() {
snake.world[tx][SNAKE_START_Y] = tile_snake_l;
}
snake.world[SNAKE_START_PUPA_X][SNAKE_START_Y] = tile_pupa_0_l;
snake.snake_head = (ivec2){SNAKE_START_HEAD_X, SNAKE_START_Y};
snake.snake_tail = (ivec2){SNAKE_START_PUPA_X, SNAKE_START_Y};
for (int app = 0; app < snake.gamemode_apples_count; app++) {
place_random_apple();
}
snake.new_snake_direction = snake.cur_snake_direction = snake_direction_left;
snake.is_dying = false;
@ -153,7 +161,7 @@ void start_snake_game() {
snake.waiting_reason = waiting_reason_step;
snake.waiting_time = 0;
snake.is_time_sped_up = false;
snake.animation_speed_boost = 0;
snake.puddle_sprite = -1;
}
@ -192,7 +200,7 @@ ivec2 world_walk_direction(ivec2 v, snake_direction_t dir) {
}
ivec2 get_puddle_center_for_contact(ivec2 head, snake_direction_t dir) {
ivec2 tile_pos = (ivec2){TILE_WIDTH * head.x, TILE_HEIGHT * head.y};
ivec2 tile_pos = (ivec2){TILE_WIDTH * head.x, TILE_WIDTH * head.y};
if (dir == snake_direction_left || dir == snake_direction_right) {
tile_pos.y += TILE_WIDTH / 2;
} else if (dir == snake_direction_bottom) {
@ -275,7 +283,7 @@ tile_t construct_snake_tile(snake_direction_t from, snake_direction_t to) {
check(false);
}
tile_t construct_head_snake_tile(snake_direction_t to) {
tile_t construct_straight_snake_tile(snake_direction_t to) {
return construct_snake_tile(get_opposite_direction(to), to);
}
@ -335,15 +343,12 @@ snake_direction_t get_zero_pupa_destination(tile_t tile) {
check(false);
}
/* takes a tile that can possibly become a tail. Either straight snake or pupa */
bool is_tail_pupa(tile_t tile) {
if (18 <= tile && tile <= 21)
return false;
bool is_tile_pupa(tile_t tile) {
if (tile == tile_pupa_0_r || tile == tile_pupa_0_l ||
tile == tile_pupa_0_b || tile == tile_pupa_0_t) {
return true;
}
check(false);
return false;
}
void clear_frame(uint8_t color) {
@ -471,12 +476,10 @@ tile_t correct_game_tile(tile_t x) {
}
void draw_game_world() {
if (!snake.have_game)
return;
for (int tx = 0; tx < WORLD_WIDTH; tx++) {
for (int ty = 0; ty < WORLD_HEIGHT; ty++) {
tile_t tt = correct_game_tile(snake.world[tx][ty]);
for (int ty = 0; ty <= WORLD_HEIGHT; ty++) {
int iy = ty < WORLD_HEIGHT ? ty : 0;
tile_t tt = correct_game_tile(snake.world[tx][iy]);
if (tt == tile_empty)
continue;
check((uint8_t)tt < TILE_SPRITES);
@ -494,6 +497,13 @@ void draw_game_world() {
void draw_game_text() {
StringBuilder hud = { 0 };
if (snake.game_screen == game_screen_gaming) {
if (snake.waiting_reason == waiting_reason_postmortum) {
StringBuilder_append_cstr(&hud, "YOU DIED ");
} else if (snake.is_dying) {
StringBuilder_append_cstr(&hud, "LOL ");
}
// StringBuilder_append_u32(&hud, snake.animation_speed_boost);
// StringBuilder_append_cstr(&hud, " ");
StringBuilder_append_u32(&hud, snake.score);
} else if (snake.game_screen == game_screen_pause) {
if (snake.have_game) {
@ -517,7 +527,7 @@ void draw_game_text() {
}
void draw_game_puddles() {
if (snake.puddle_sprite > 0) {
if (snake.puddle_sprite >= 0) {
const PuddleSprite* sprite = &sprite_data.puddle[snake.puddle_sprite];
draw_sprite(
snake.puddle_center.x - PUDDLE_WIDTH / 2,
@ -528,8 +538,10 @@ void draw_game_puddles() {
void draw_frame() {
clear_frame(226);
draw_game_puddles();
draw_game_world();
if (snake.have_game) {
draw_game_puddles();
draw_game_world();
}
draw_game_text();
}
@ -562,6 +574,11 @@ int handle_incoming_keycode_after_halt(uint8_t keycode) {
} else if (keycode == (KEYCODE_SPACE | 0x80)) {
snake.is_space_pressed = false;
}
if (keycode == KEYCODE_SHIFT) {
snake.is_shift_pressed = true;
} else if (keycode == (KEYCODE_SHIFT | 0x80)) {
snake.is_shift_pressed = false;
}
if (keycode == KEYCODE_Q && snake.game_screen != game_screen_gaming) {
return 1;
}
@ -611,7 +628,13 @@ void handle_time_difference(uint32_t time_diff) {
return;
snake.waiting_time += time_diff;
uint32_t target_required_time;
if (snake.is_time_sped_up) {
if (snake.animation_speed_boost == 1) {
if (snake.waiting_reason == waiting_reason_step) {
target_required_time = WALKING_ANIM_TIME_FAST;
} else {
target_required_time = HATCHING_ANIM_TIME_FAST;
}
} else if (snake.animation_speed_boost == 0) {
if (snake.waiting_reason == waiting_reason_step) {
target_required_time = WALKING_ANIM_TIME_SLOW;
} else {
@ -619,9 +642,9 @@ void handle_time_difference(uint32_t time_diff) {
}
} else {
if (snake.waiting_reason == waiting_reason_step) {
target_required_time = WALKING_ANIM_TIME_FAST;
target_required_time = WALKING_ANIM_TIME_VERY_SLOW;
} else {
target_required_time = HATCHING_ANIM_TIME_FAST;
target_required_time = HATCHING_ANIM_TIME_VERY_SLOW;
}
}
if (snake.waiting_time > target_required_time) {
@ -633,6 +656,7 @@ void handle_time_difference(uint32_t time_diff) {
/* Processing collisions */
if (tile_ahead == tile_apple) {
snake.ghost_apples[tile_pos_ahead.x][tile_pos_ahead.y] = 1;
snake.score += 5;
} else if (18 <= tile_ahead && tile_ahead <= 49) {
snake.waiting_reason = waiting_reason_postmortum;
snake.puddle_center = get_puddle_center_for_contact(
@ -652,7 +676,7 @@ void handle_time_difference(uint32_t time_diff) {
ivec2 old_head_pos = snake.snake_head;
if (tile_ahead == tile_empty || tile_ahead == tile_apple) {
set_tile(tile_pos_ahead,
construct_head_snake_tile(snake.new_snake_direction));
construct_straight_snake_tile(snake.new_snake_direction));
snake.snake_head = tile_pos_ahead;
}
/* Step 2 */
@ -661,7 +685,7 @@ void handle_time_difference(uint32_t time_diff) {
snake.cur_snake_direction = snake.new_snake_direction;
/* Steps 3... */
if (is_tail_pupa(get_tile(snake.snake_tail))) {
if (is_tile_pupa(get_tile(snake.snake_tail))) {
/* Step 3a */
snake_direction_t pupa_dir = get_zero_pupa_destination(get_tile(snake.snake_tail));
ivec2 penultimate_pos = world_walk_direction(snake.snake_tail, pupa_dir);
@ -675,20 +699,27 @@ void handle_time_difference(uint32_t time_diff) {
// check, this is important. This is the only place where we move pupa to a new pos
// we check for ghost apples. Clear ghost apples and enter
// p1-waiting state (instead of remaining in step awaiting state)
if (snake.snake_tail.x == snake.snake_head.x &&
snake.snake_tail.y == snake.snake_head.y
) {
snake.waiting_reason = waiting_reason_postmortum;
return;
}
if (snake.ghost_apples[penultimate_pos.x][penultimate_pos.y]) {
snake.ghost_apples[penultimate_pos.x][penultimate_pos.y] = 0;
syscall(SYS_set_beep, 5000);
syscall(SYS_set_beep, WEIRD_SOUND);
snake.waiting_reason = waiting_reason_p1;
/* Nothing changed on screen because we need to wait even
* for the first stage of hatching.
* We could go to p1 immediately, though */
}
} else {
snake_direction_t tail_dir = get_snake_destination(get_tile(snake.snake_tail));
set_tile(snake.snake_tail, construct_zero_pupa(tail_dir));
syscall(SYS_set_beep, 3000);
syscall(SYS_set_beep, WEIRD_SOUND);
snake.waiting_reason = waiting_reason_m1;
}
@ -707,11 +738,11 @@ void handle_time_difference(uint32_t time_diff) {
} else if (snake.waiting_reason == waiting_reason_cross) {
/* Finished pupa hatching */
snake_direction_t tail_dir = get_zero_pupa_destination(get_tile(snake.snake_tail));
set_tile(snake.snake_tail, construct_zero_pupa(tail_dir));
set_tile(snake.snake_tail, construct_straight_snake_tile(tail_dir));
syscall(SYS_set_beep, 0);
snake.waiting_reason = waiting_reason_step;
} else if (snake.waiting_reason == waiting_reason_m1) {
snake.waiting_reason = waiting_reason_cross;
snake.waiting_reason = waiting_reason_zero_pupa;
} else if (snake.waiting_reason == waiting_reason_zero_pupa) {
/* Finished pupa regrowing */
syscall(SYS_set_beep, 0);
@ -722,7 +753,7 @@ void handle_time_difference(uint32_t time_diff) {
void after_awake_action(uint32_t time_diff) {
handle_time_difference(time_diff);
snake.is_time_sped_up = false;
snake.animation_speed_boost = 0;
draw_frame();
syscall(SYS_swap_frame, (uintptr_t)frame);
}
@ -741,7 +772,9 @@ int main(void) {
}
/* As an additional benefit, we can check if is_space_pressed here */
if (snake.is_space_pressed) {
snake.is_time_sped_up = true;
snake.animation_speed_boost = 1;
} else if (snake.is_shift_pressed) {
snake.animation_speed_boost = -1;
}
/* snake.is_time_sped_up will be relevant when checking animation end */