diff --git a/project/main.c b/project/main.c index 18b77db..00af0f5 100644 --- a/project/main.c +++ b/project/main.c @@ -106,7 +106,10 @@ int main(void) mode_life_init(); mode_snake_init(); - mode_audio_start(); + app_mode = MODE_SNAKE; + mode_snake_start(); + + //mode_audio_start(); ms_time_t last; while (1) { diff --git a/project/main.h b/project/main.h index ad6719a..f30a7c5 100644 --- a/project/main.h +++ b/project/main.h @@ -15,4 +15,8 @@ #define STR_HELPER(x) #x #define STR(x) STR_HELPER(x) +// screen size +#define SCREEN_W 16 +#define SCREEN_H 16 + #endif // MAIN_H diff --git a/project/mode_audio.c b/project/mode_audio.c index dff3bd4..e8db375 100644 --- a/project/mode_audio.c +++ b/project/mode_audio.c @@ -34,7 +34,7 @@ static void boot_animation(void) dmtx_clear(dmtx); // Boot animation (for FFT) - for (int i = 0; i < 16; i++) { + for (int i = 0; i < SCREEN_W; i++) { dmtx_set(dmtx, i, 0, 1); dmtx_show(dmtx); delay_ms(20); @@ -149,7 +149,7 @@ static void audio_capture_done(void* unused) dmtx_clear(dmtx); float factor = (1.0f / bin_count) * 0.25f; - for (int i = 0; i < bin_count; i++) { + for (int i = 0; i < MIN(bin_count, SCREEN_W) + 1; i++) { // +1 because bin 0 is always 0 bins[i] *= factor; for (int j = 0; j < 1 + floorf(bins[i]); j++) { diff --git a/project/mode_life.c b/project/mode_life.c index b641bcc..53f3128 100644 --- a/project/mode_life.c +++ b/project/mode_life.c @@ -9,8 +9,8 @@ static bool life_enabled = false; static void show_board(bool do_show); static void apply_paint(void); -#define BOARD_W 16 -#define BOARD_H 16 +#define BOARD_W SCREEN_W +#define BOARD_H SCREEN_H #define SIZEOF_BOARD (BOARD_H*sizeof(uint32_t)) diff --git a/project/mode_snake.c b/project/mode_snake.c index 8a2d2a5..3f54650 100644 --- a/project/mode_snake.c +++ b/project/mode_snake.c @@ -1,13 +1,156 @@ #include "mode_snake.h" +#include "com/debug.h" +#include "dotmatrix.h" + +#define BOARD_W SCREEN_W +#define BOARD_H SCREEN_H + +/** Snake movement direction */ +typedef enum { + NORTH, + SOUTH, + EAST, + WEST +} Direction; + +typedef enum { + CELL_EMPTY, + CELL_BODY, + CELL_FOOD, + CELL_WALL +} CellType; + +/** Board cell */ +typedef struct __attribute__((packed)) { + CellType type : 2; // Cell type + Direction dir : 2; // direction the head moved to from this tile, if body = 1 +} Cell; + +/** Wall tile, invariant, used for out-of-screen coords */ +static const Cell WALL_TILE = {CELL_WALL, 0}; +static const Cell EMPTY_TILE = {CELL_EMPTY, 0}; + +/** Game board */ +static Cell board[BOARD_H][BOARD_W]; + +typedef struct { + int x; + int y; +} Coord; + +/** Snake coords */ +static Coord head; +static Coord tail; +static Direction head_dir; + +/** blinking to visually change 'color' */ +static bool wall_pwm_on = 1; +static bool body_pwm_on = 1; +static bool head_pwm_on = 1; +static bool food_pwm_on = 1; + +/** Get board cell at coord (x,y) */ +static Cell *cell_at_xy(int x, int y) +{ + if (x < 0 || x >= BOARD_W || y < 0 || y >= BOARD_H ) { + return &WALL_TILE; // discards const - OK + } + + return &board[y][x]; +} + +/** Get board cell at coord */ +static Cell *cell_at(Coord *coord) +{ + return cell_at_xy(coord->x, coord->y); +} + +/** Place food in a random empty tile */ +static void place_food(void) +{ + Coord food; + + // 1000 tries - timeout in case board is full of snake (?) + for (int i = 0; i < 1000; i++) { + food.x = rand() % BOARD_W; + food.y = rand() % BOARD_H; + + Cell *cell = cell_at(&food); + if (cell->type == CELL_EMPTY) { + cell->type = CELL_FOOD; + + dbg("Food at [%d, %d]", food.x, food.y); + break; + } + } +} + +/** Clear the board and prepare for a new game */ +static void new_game(void) +{ + // randomize (we can assume it takes random time before the user switches to the Snake mode) + srand(SysTick->VAL); + + // Erase the board + memset(board, 0, sizeof(board)); + + // Place initial snake + + tail.x = BOARD_W/2-5; + tail.y = BOARD_H/2; + + head.x = tail.x + 4; + head.y = tail.y; + + for (int x = tail.x; x < head.x; x++) { + board[tail.y][x].type = CELL_BODY; + } + + place_food(); +} + +static void show_board(void) +{ + dmtx_clear(dmtx); + + for (int x = 0; x < BOARD_W; x++) { + for (int y = 0; y < BOARD_H; y++) { + Cell *cell = cell_at_xy(x, y); + + bool set = 0; + + switch (cell->type) { + case CELL_EMPTY: set = 0; break; + case CELL_BODY: set = body_pwm_on; break; + case CELL_FOOD: + dbg("Food found @ [%d, %d]", x, y); + set = food_pwm_on; + break; + + case CELL_WALL: set = wall_pwm_on; break; + } + + if (x == head.x && y == head.y) set = head_pwm_on; + + dmtx_set(dmtx, x, y, set); + } + } + + dmtx_show(dmtx); +} + void mode_snake_init(void) { // } + void mode_snake_start(void) { - // + new_game(); + + show_board(); } void mode_snake_stop(void)