Created
June 4, 2026 01:49
-
-
Save ehaliewicz/398624323bd993f9ff3c37388ad072d3 to your computer and use it in GitHub Desktop.
snake
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #include "exotique.h" | |
| #define CELL_SIZE 16 | |
| #define GAME_SIZE 32 | |
| #define MAX_SNAKE_LEN (GAME_SIZE*GAME_SIZE) | |
| /* required screen dim constants for exotique */ | |
| const int kScreenWidth = (CELL_SIZE*GAME_SIZE); | |
| const int kScreenHeight = (CELL_SIZE*GAME_SIZE); | |
| typedef struct { | |
| int x,y; | |
| } seg; | |
| seg segments[GAME_SIZE*GAME_SIZE]; | |
| int snake_len; | |
| int snake_start = 0; | |
| #define GREEN_IDX 0 | |
| #define RED_IDX 1 | |
| #define BROWN_IDX 2 | |
| #define UP 0 | |
| #define RIGHT 1 | |
| #define DOWN 2 | |
| #define LEFT 3 | |
| int frame = 0; | |
| int has_apple = 0; | |
| int apple_x = 0; | |
| int apple_y = 0; | |
| int cur_dir = LEFT; | |
| int growing = 0; | |
| /* | |
| just gonna steal doom's random array :) | |
| */ | |
| const unsigned char rnd_tbl[256] = { | |
| 0, 8, 109, 220, 222, 241, 149, 107, 75, 248, 254, 140, 16, 66 , | |
| 74, 21, 211, 47, 80, 242, 154, 27, 205, 128, 161, 89, 77, 36 , | |
| 95, 110, 85, 48, 212, 140, 211, 249, 22, 79, 200, 50, 28, 188 , | |
| 52, 140, 202, 120, 68, 145, 62, 70, 184, 190, 91, 197, 152, 224 , | |
| 149, 104, 25, 178, 252, 182, 202, 182, 141, 197, 4, 81, 181, 242 , | |
| 145, 42, 39, 227, 156, 198, 225, 193, 219, 93, 122, 175, 249, 0 , | |
| 175, 143, 70, 239, 46, 246, 163, 53, 163, 109, 168, 135, 2, 235 , | |
| 25, 92, 20, 145, 138, 77, 69, 166, 78, 176, 173, 212, 166, 113 , | |
| 94, 161, 41, 50, 239, 49, 111, 164, 70, 60, 2, 37, 171, 75 , | |
| 136, 156, 11, 56, 42, 146, 138, 229, 73, 146, 77, 61, 98, 196 , | |
| 135, 106, 63, 197, 195, 86, 96, 203, 113, 101, 170, 247, 181, 113 , | |
| 80, 250, 108, 7, 255, 237, 129, 226, 79, 107, 112, 166, 103, 241 , | |
| 24, 223, 239, 120, 198, 58, 60, 82, 128, 3, 184, 66, 143, 224 , | |
| 145, 224, 81, 206, 163, 45, 63, 90, 168, 114, 59, 33, 159, 95 , | |
| 28, 139, 123, 98, 125, 196, 15, 70, 194, 253, 54, 14, 109, 226 , | |
| 71, 17, 161, 93, 186, 87, 244, 138, 20, 52, 123, 251, 26, 36 , | |
| 17, 46, 52, 231, 232, 76, 31, 221, 84, 37, 216, 165, 212, 106 , | |
| 197, 242, 98, 43, 39, 175, 254, 145, 190, 84, 118, 222, 187, 136 , | |
| 120, 163, 236, 249 | |
| }; | |
| int rand_idx = 0; | |
| int rand() { | |
| return (rnd_tbl[(rand_idx++)&0xFF]); | |
| } | |
| void game_load(ExotiqueInterface *ei) { | |
| /* initialize palette */ | |
| ei->palette[GREEN_IDX] = 0x00FF00FF; | |
| ei->palette[RED_IDX] = 0xFF0000FF; | |
| ei->palette[BROWN_IDX] = 0x6F4E37FF; | |
| /* setup initial snake segment */ | |
| /* note, this is kind of a circular buffer (unless I copy snake elements forward/back in the segment list) */ | |
| snake_len = 1; | |
| snake_start = 0; | |
| segments[snake_start].x = GAME_SIZE/2; | |
| segments[snake_start].y = GAME_SIZE/2; | |
| frame = 0; | |
| has_apple = 0; | |
| rand_idx = 0; | |
| growing = 0; | |
| } | |
| int valid_pos(int x, int y) { | |
| int seg_idx; | |
| if(x < 0 || x >= GAME_SIZE) { | |
| return 0; | |
| } | |
| if(y < 0 || y >= GAME_SIZE) { | |
| return 0; | |
| } | |
| for(seg_idx = 0; seg_idx < snake_len; seg_idx++) { | |
| seg cur_seg = segments[(seg_idx + snake_start) % MAX_SNAKE_LEN]; | |
| if(cur_seg.x == x && cur_seg.y == y) { | |
| return 0; | |
| } | |
| } | |
| return 1; | |
| } | |
| int try_move_snake(int dx, int dy, int grow) { | |
| /* head of the snake is at snake_start + snake len */ | |
| int head_x = segments[(snake_start+snake_len-1)%MAX_SNAKE_LEN].x; | |
| int head_y = segments[(snake_start+snake_len-1)%MAX_SNAKE_LEN].y; | |
| if(valid_pos(head_x + dx, head_y + dy)) { | |
| /* move the segment at the end of the snake to the front, with a further left position */ | |
| if(grow) { | |
| segments[(snake_start+snake_len)%MAX_SNAKE_LEN].x = head_x+dx; | |
| segments[(snake_start+snake_len++)%MAX_SNAKE_LEN].y = head_y+dy; | |
| } else { | |
| snake_start++; | |
| segments[(snake_start+snake_len-1)%MAX_SNAKE_LEN].x = head_x+dx; | |
| segments[(snake_start+snake_len-1)%MAX_SNAKE_LEN].y = head_y+dy; | |
| } | |
| return 0; | |
| } else { | |
| /* can't move here, end game. ignore for now */ | |
| return 1; | |
| } | |
| } | |
| int snake_overlaps_apple() { | |
| int head_x = segments[(snake_start+snake_len-1)%MAX_SNAKE_LEN].x; | |
| int head_y = segments[(snake_start+snake_len-1)%MAX_SNAKE_LEN].y; | |
| return (head_x == apple_x && head_y == apple_y); | |
| } | |
| int game_step(int timer) { | |
| return (timer & 0xF) == 0xF; | |
| } | |
| void game_update(ExotiqueInterface *ei) { | |
| int failed_to_move = 0; | |
| if(ei->input->right) { | |
| cur_dir = RIGHT; | |
| } | |
| if(ei->input->left) { | |
| cur_dir = LEFT; | |
| } | |
| if(ei->input->up) { | |
| cur_dir = UP; | |
| } | |
| if(ei->input->down) { | |
| cur_dir = DOWN; | |
| } | |
| if(!game_step(frame++)) { | |
| /* hacky way to not step every frame :) */ | |
| return; | |
| } | |
| /* try to move the snake */ | |
| switch(cur_dir) { | |
| case LEFT: | |
| failed_to_move = try_move_snake(-1, 0, growing); | |
| break; | |
| case UP: | |
| failed_to_move = try_move_snake(0, -1, growing); | |
| break; | |
| case DOWN: | |
| failed_to_move = try_move_snake(0, 1, growing); | |
| break; | |
| case RIGHT: | |
| failed_to_move = try_move_snake(1, 0, growing); | |
| break; | |
| default: | |
| /* do nothing for now */ | |
| break; | |
| } | |
| if(failed_to_move) { | |
| /* reset game */ | |
| game_load(ei); | |
| return; | |
| } | |
| if(!has_apple) { | |
| /* pick random coordinate for apple that doesn't overlap snake */ | |
| int rx = rand()&31; | |
| int ry = rand()&31; | |
| while(!valid_pos(rx, ry)) { | |
| rx = rand()&31; | |
| ry = rand()&31; | |
| } | |
| has_apple = 1; | |
| apple_x = rx; | |
| apple_y = ry; | |
| } else if(snake_overlaps_apple()) { | |
| has_apple = 0; | |
| growing = 3; | |
| } else if (growing) { | |
| growing--; | |
| } | |
| } | |
| void draw_cell(ExotiqueInterface *ei, int x, int y, u8 pal_idx) { | |
| int dx,dy; | |
| for(dy = y * CELL_SIZE; dy < (y+1)*CELL_SIZE; dy++) { | |
| for(dx = x * CELL_SIZE; dx < (x+1)*CELL_SIZE; dx++) { | |
| ei->screen[dy*kScreenWidth + dx] = pal_idx; | |
| } | |
| } | |
| } | |
| void game_draw(ExotiqueInterface *ei) { | |
| /* draw background */ | |
| int x,y, snake_seg_idx; | |
| for(y = 0; y < kScreenHeight; y++) { | |
| for(x = 0; x < kScreenWidth; x++) { | |
| ei->screen[y*kScreenWidth + x] = BROWN_IDX; | |
| } | |
| } | |
| /* draw snake */ | |
| for(snake_seg_idx = 0; snake_seg_idx < snake_len; snake_seg_idx++) { | |
| seg snake_seg = segments[(snake_start+snake_seg_idx) % MAX_SNAKE_LEN]; | |
| draw_cell(ei, snake_seg.x, snake_seg.y, GREEN_IDX); | |
| } | |
| /* draw apple */ | |
| if(has_apple) { | |
| draw_cell(ei, apple_x, apple_y, RED_IDX); | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment