|
|
@ -1,13 +1,156 @@ |
|
|
|
#include "mode_snake.h" |
|
|
|
#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_init(void) |
|
|
|
{ |
|
|
|
{ |
|
|
|
//
|
|
|
|
//
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void mode_snake_start(void) |
|
|
|
void mode_snake_start(void) |
|
|
|
{ |
|
|
|
{ |
|
|
|
//
|
|
|
|
new_game(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
show_board(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void mode_snake_stop(void) |
|
|
|
void mode_snake_stop(void) |
|
|
|