snake_compiler git · main
Snake compiled to a bare-metal RISC-V microcontroller
C 95.2% Markdown 3.3%git clone https://git.christianimmanuel.de/embedded/snake_compiler.gitwget https://git.christianimmanuel.de/embedded/snake_compiler/archive/snake_compiler.tar.gzinit
README.md | 63 ++++ process.png | Bin 0 -> 41809 bytes snake_SDL/editor/.main.c.swp | Bin 0 -> 28672 bytes snake_SDL/editor/Makefile | 16 ++ snake_SDL/editor/main.c | 545 +++++++++++++++++++++++++++++++++++ snake_SDL/editor/sdl3_move | Bin 0 -> 30912 bytes snake_SDL/export.sh | 3 + snake_SDL/make_compiler.sh | 10 + snake_SDL/src/.snake.h.swp | Bin 0 -> 12288 bytes snake_SDL/src/Makefile | 3 + snake_SDL/src/defaults.h | 58 ++++ snake_SDL/src/firmware.bin | Bin 0 -> 3952 bytes snake_SDL/src/firmware.elf | Bin 0 -> 10512 bytes snake_SDL/src/main.c | 137 +++++++++ snake_SDL/src/matrix.c | 209 ++++++++++++++ snake_SDL/src/matrix.h | 77 +++++ snake_SDL/src/snake.c | 158 ++++++++++ snake_SDL/src/snake.h | 48 ++++ snake_ascii_editor/main | Bin 0 -> 30384 bytes snake_ascii_editor/main.c | 664 +++++++++++++++++++++++++++++++++++++++++++ 20 files changed, 1991 insertions(+) diff --git a/README.md b/README.md new file mode 100644 index 0000000..1197095 --- /dev/null +++ b/README.md @@ -0,0 +1,63 @@ +# Guteseele Anhang + +In diesem Repo stehen zwei Projekte zu Verfügung: + +- snake\_SDL +- snake\_ascii\_editor + +## snake\_SDL + + +process.png + + + +Spiele Snake auf deinem X86 System um ein anderes Snake für RISK5 zu konfigurieren und dieses direkt zu flashen. + +### Ordner + +### editor + +Dies stellt den SDL Snake-Editor bereit welcher nach beenden des Spileles das folgende ausführt um Snake für den ESP zu kompilieren und flashen (siehe `main.c`) + +``` +make -C ../src/ clean && +make -C ../src/ build +EXTRA_CFLAGS='-DWALL_COLLISION=%d -DSNAKE_START_LENGTH=%d -DMAX_BRIGHTNESS=%d -DSNAKE_COLOR_R=%d -DSNAKE_COLOR_G=%d -DSNAKE_COLOR_B=%d' && +make -C ../src/ flash +``` + +### src + +Dies stellt ein funktionsfähiges Snake für den ESP32 und die "mdk-Baremetal" Variante dar. Dieses wird vom Editor genutzt um es zu konfigurieren. Es kann allerdings auch ohne diesen genutzt werden indem (nach bereitstellen der mdk variablen) das folgende ausgeführt wird: + +``` +make clean && +make build && +make flash +``` + + +### Funktionalität + +Der Ordner `editor` stellt den Quellcode für das Programm bereit. Dieses Programm erstell eine Abwandlung des Snake Spiels dar welches das endgültigen Snake spiel für den ESP konfiguriert. +Sollte die Snake im Editor in eine wand oder in sich selbts laufen beendet das Programm und startet den RISK-compile und ESP-Flash-Vorgang um das zuvor konfigurierte Snake auf den ESP zu flashen. + + +### Abhängigkeiten + +- SDL3 muss installiert sein sodass die `LDFLAG` `--libs sdl3` funktionabel ist +- [mdk](https://github.com/cpq/mdk) Umgebungsvariablen + +### Benutzung + +1. Verbinde den ESP mit deinem Rechner +1. erstelle die MDK Umgebungsvariablen (siehe [mdk](https://github.com/cpq/mdk)) +1. `cd editor` +1. `make` +1. `./sdl3_move ` + + +## snake\_ascii\_editor + +Hier kann über den Terminal eine Abwandlung von Snake gespielt werden, welche zum schreiben von c-Code genutzt werden kann. Die Idee war dieses Programm so zu scripten dass es Snake für den ESP programmiert. diff --git a/process.png b/process.png new file mode 100644 index 0000000..631756b Binary files /dev/null and b/process.png differ diff --git a/snake_SDL/editor/.main.c.swp b/snake_SDL/editor/.main.c.swp new file mode 100644 index 0000000..f2462d9 Binary files /dev/null and b/snake_SDL/editor/.main.c.swp differ diff --git a/snake_SDL/editor/Makefile b/snake_SDL/editor/Makefile new file mode 100644 index 0000000..ebf8d34 --- /dev/null +++ b/snake_SDL/editor/Makefile @@ -0,0 +1,16 @@ +CC := gcc +CFLAGS := -std=c11 -Wall -Wextra -O2 +LDFLAGS := $(shell pkg-config --cflags --libs sdl3) + +TARGET := sdl3_move +SRC := main.c + +all: $(TARGET) + +$(TARGET): $(SRC) + $(CC) $(CFLAGS) $(SRC) -o $(TARGET) $(LDFLAGS) + +clean: + rm -f $(TARGET) + +.PHONY: all clean diff --git a/snake_SDL/editor/main.c b/snake_SDL/editor/main.c new file mode 100644 index 0000000..b2821c9 --- /dev/null +++ b/snake_SDL/editor/main.c @@ -0,0 +1,545 @@ +// gcc snake.c -o snake_game $(pkg-config --cflags --libs sdl3) +#include <SDL3/SDL.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <pthread.h> + + +FILE *popen(const char *command, const char *type); +char *strdup(const char *s); +int pclose(FILE *stream); + +#define GRID_SIZE_X 40 +#define GRID_SIZE_Y 30 +#define TILE_SIZE 30 +#define WINDOW_SIZE_X TILE_SIZE*GRID_SIZE_X +#define WINDOW_SIZE_Y TILE_SIZE*GRID_SIZE_Y +#define MAX_SNAKE_LENGTH (GRID_SIZE_X* GRID_SIZE_Y) +#define MAX_BRIGHTNESS_STEPS 7 // Fixed number of brightness levels (0=off, 1-7=on) + +#define MATRIX_WIDTH 16 +#define MATRIX_HEIGHT 16 +#define LED_SIZE 30 +#define LED_SPACING 2 +#define MATRIX_WINDOW_WIDTH (MATRIX_WIDTH * (LED_SIZE + LED_SPACING) + LED_SPACING) +#define MATRIX_WINDOW_HEIGHT (MATRIX_HEIGHT * (LED_SIZE + LED_SPACING) + LED_SPACING) + +typedef struct { + int x, y; +} Point; + +typedef struct { + Point segments[MAX_SNAKE_LENGTH]; + int length; + Point direction; + Point next_direction; +} Snake; + +typedef struct { + Point pos; + SDL_Color color; + bool active; +} Button; + +typedef struct { + Point pos; // top-left tile position + int width; // width in tiles (should be 8: 1 for off + 7 for brightness levels) + int height; // height in tiles + SDL_Color base_color; // base color at full brightness + int brightness; // current brightness level (0 to MAX_BRIGHTNESS_STEPS) + bool last_overlap; +} Light; + +typedef struct { + SDL_Color matrix[MATRIX_HEIGHT][MATRIX_WIDTH]; + FILE *pipe; + bool running; + pthread_mutex_t mutex; +} MatrixData; + +void init_snake(Snake *snake) { + snake->length = 3; + snake->segments[0] = (Point){GRID_SIZE_X / 2 , GRID_SIZE_Y / 2}; + snake->segments[1] = (Point){GRID_SIZE_X / 2 - 1, GRID_SIZE_Y / 2}; + snake->segments[2] = (Point){GRID_SIZE_X / 2 - 2, GRID_SIZE_Y / 2}; + snake->direction = (Point){1, 0}; // moving right + snake->next_direction = (Point){1, 0}; +} + +int max_brightness = MAX_BRIGHTNESS_STEPS; + +void move_snake(Snake *snake, bool grow, bool* collided_with_wall) { + // Update direction + snake->direction = snake->next_direction; + + // Move body segments + if (!grow) { + for (int i = snake->length - 1; i > 0; i--) { + snake->segments[i] = snake->segments[i - 1]; + } + } else { + // Growing: shift all segments and add new head + for (int i = snake->length; i > 0; i--) { + snake->segments[i] = snake->segments[i - 1]; + } + snake->length++; + } + + // Move head + snake->segments[0].x += snake->direction.x; + snake->segments[0].y += snake->direction.y; + + if (snake->segments[0].x < 0 || + snake->segments[0].x >= GRID_SIZE_X || + snake->segments[0].y < 0 || + snake->segments[0].y >= GRID_SIZE_Y) + *collided_with_wall = true; +} + +bool check_button_collision(Snake *snake, Button *button) { + if (!button->active) return false; + return snake->segments[0].x == button->pos.x && + snake->segments[0].y == button->pos.y; +} + +void check_light_brightness(Light lights[3], Light *light, Snake *snake) { + Point head = snake->segments[0]; + bool currently_overlap = false; + + // Check if head is within light's vertical bounds + if (head.y >= light->pos.y && head.y < light->pos.y + light->height) { + // Check if head is within light's horizontal bounds + if (head.x >= light->pos.x && head.x < light->pos.x + light->width) { + currently_overlap = true; + + // Calculate relative position from left (0 = leftmost column) + int relative_x = head.x - light->pos.x; + + // Map column to brightness: column 0 = off (0), columns 1-7 = brightness 1-7 + int target_brightness = relative_x; + + // Clamp to valid range + if (target_brightness > MAX_BRIGHTNESS_STEPS) { + target_brightness = MAX_BRIGHTNESS_STEPS; + } + + // Set the brightness directly + light->brightness = target_brightness; + } + } + + light->last_overlap = currently_overlap; + + // Ensure at least one light is on + bool any_on = false; + for (int i = 0; i < 3; i++) { + if (lights[i].brightness > 0) { + any_on = true; + break; + } + } + if (!any_on) { + // If all off, force this light on at minimum brightness + light->brightness = 1; + } +} + +bool check_self_collision(Snake *snake) { + for (int i = 1; i < snake->length; i++) { + if (snake->segments[0].x == snake->segments[i].x && + snake->segments[0].y == snake->segments[i].y) { + return true; + } + } + return false; +} + +void render_snake(SDL_Renderer *ren, Snake *snake) { + for (int i = 0; i < snake->length; i++) { + SDL_FRect rect = { + snake->segments[i].x * TILE_SIZE, + snake->segments[i].y * TILE_SIZE, + TILE_SIZE - 2, + TILE_SIZE - 2 + }; + + // Head is brighter + if (i == 0) { + SDL_SetRenderDrawColor(ren, 0, 255, 0, 255); + } else { + SDL_SetRenderDrawColor(ren, 0, 200, 0, 255); + } + SDL_RenderFillRect(ren, &rect); + } +} + +void render_button(SDL_Renderer *ren, Button *button) { + if (!button->active) return; + + SDL_FRect rect = { + button->pos.x * TILE_SIZE + 2, + button->pos.y * TILE_SIZE + 2, + TILE_SIZE - 4, + TILE_SIZE - 4 + }; + + SDL_SetRenderDrawColor(ren, button->color.r, button->color.g, button->color.b, 255); + SDL_RenderFillRect(ren, &rect); +} + +void render_light(SDL_Renderer *ren, Light *light) { + // Draw each column of the light with visual indication + for (int col = 0; col < light->width; col++) { + SDL_FRect rect = { + (light->pos.x + col) * TILE_SIZE, + light->pos.y * TILE_SIZE, + TILE_SIZE, + light->height * TILE_SIZE + }; + + SDL_Color c = light->base_color; + + // Column 0 is "off", columns 1-7 show increasing brightness + if (col == 0) { + // Off column - very dark + c.r /= 10; + c.g /= 10; + c.b /= 10; + } else { + // Brightness columns - show the brightness level for that column + float intensity = (float)light->brightness / (float)MAX_BRIGHTNESS_STEPS; + c.r = (uint8_t)(c.r * intensity); + c.g = (uint8_t)(c.g * intensity); + c.b = (uint8_t)(c.b * intensity); + } + + // Highlight the current brightness level + if (col == light->brightness) { + SDL_SetRenderDrawColor(ren, light->base_color.r, light->base_color.g, light->base_color.b, 140); + SDL_RenderFillRect(ren, &rect); + rect.x += 2; + rect.y += 2; + rect.w -= 4; + rect.h -= 4; + } + + SDL_SetRenderDrawColor(ren, c.r, c.g, c.b, 255); + SDL_RenderFillRect(ren, &rect); + } +} + +void render_grid(SDL_Renderer *ren) { + SDL_SetRenderDrawColor(ren, 30, 30, 30, 255); + + // Vertical lines + for (int i = 0; i <= GRID_SIZE_X; i++) { + SDL_RenderLine(ren, i * TILE_SIZE, 0, i * TILE_SIZE, GRID_SIZE_Y * TILE_SIZE); + } + + // Horizontal lines + for (int i = 0; i <= GRID_SIZE_Y; i++) { + SDL_RenderLine(ren, 0, i * TILE_SIZE, GRID_SIZE_X * TILE_SIZE, i * TILE_SIZE); + } +} + +void* matrix_reader_thread(void* arg) +{ + MatrixData *md = (MatrixData*)arg; + char line[512]; + + enum { + WAIT_START, + READ_ROWS + } state = WAIT_START; + + int y = 0; + + while (md->running && md->pipe && fgets(line, sizeof(line), md->pipe)) { + + /* Always forward ESP output to terminal */ + fputs(line, stdout); + fflush(stdout); + + if (state == WAIT_START) { + if (strncmp(line, "## START MATRIX", 15) == 0) { + y = 0; + state = READ_ROWS; + } + continue; + } + + if (state == READ_ROWS) { + if (line[0] == '\n' || line[0] == '\r') + continue; + + char *tile = strtok(line, " \t\n\r"); + int x = 0; + + pthread_mutex_lock(&md->mutex); + while (tile && x < MATRIX_WIDTH) { + int r, g, b; + + if (sscanf(tile, "%d|%d|%d", &r, &g, &b) != 3) { + /* corrupted tile → resync */ + state = WAIT_START; + break; + } + + md->matrix[y][x].r = r * 255 / 7; + md->matrix[y][x].g = g * 255 / 7; + md->matrix[y][x].b = b * 255 / 7; + + tile = strtok(NULL, " \t\n\r"); + x++; + } + pthread_mutex_unlock(&md->mutex); + + if (state != WAIT_START) { + y++; + if (y >= MATRIX_HEIGHT) { + state = WAIT_START; + } + } + } + } + + return NULL; +} + +void render_matrix(SDL_Renderer *ren, MatrixData *data) { + pthread_mutex_lock(&data->mutex); + + for (int y = 0; y < MATRIX_HEIGHT; y++) { + for (int x = 0; x < MATRIX_WIDTH; x++) { + uint8_t r = data->matrix[y][x].r; + uint8_t g = data->matrix[y][x].g; + uint8_t b = data->matrix[y][x].b; + + SDL_FRect rect = { + LED_SPACING + x * (LED_SIZE + LED_SPACING), + LED_SPACING + y * (LED_SIZE + LED_SPACING), + LED_SIZE, + LED_SIZE + }; + + SDL_SetRenderDrawColor(ren, r, g, b, 255); + SDL_RenderFillRect(ren, &rect); + + if (r+g+b > 0) { + SDL_FRect highlight = { + rect.x + 2, + rect.y + 2, + rect.w / 3, + rect.h / 3 + }; + uint8_t hr = r + (255 - r) / 2; + uint8_t hg = g + (255 - g) / 2; + uint8_t hb = b + (255 - b) / 2; + SDL_SetRenderDrawColor(ren, hr, hg, hb, 200); + SDL_RenderFillRect(ren, &highlight); + } + } + } + + pthread_mutex_unlock(&data->mutex); +} + +int main(void) { + if (!SDL_Init(SDL_INIT_VIDEO)) { + SDL_Log("SDL_Init failed: %s", SDL_GetError()); + return 1; + } + + // Create control window + SDL_Window *control_win = SDL_CreateWindow("Snake Game - WASD to move", + WINDOW_SIZE_X, WINDOW_SIZE_Y, 0); + if (!control_win) { + SDL_Log("SDL_CreateWindow failed: %s", SDL_GetError()); + SDL_Quit(); + return 1; + } + + SDL_Renderer *control_ren = SDL_CreateRenderer(control_win, "opengl"); + if (!control_ren) { + SDL_Log("SDL_CreateRenderer failed: %s", SDL_GetError()); + SDL_DestroyWindow(control_win); + SDL_Quit(); + return 1; + } + + // Create matrix visualization window + + Snake snake; + init_snake(&snake); + + // Create buttons + Button buttons[5] = { + {{5 , 5}, {255, 0 , 0 , 255}, true}, + {{GRID_SIZE_X-6, 5}, {0 , 0 , 255, 255}, true}, + {{5 , GRID_SIZE_Y-6}, {255, 255, 0 , 255}, true}, + {{GRID_SIZE_X-6, GRID_SIZE_Y-6}, {255, 0 , 255, 255}, true}, + {{GRID_SIZE_X/2, GRID_SIZE_Y/2+5}, {0 , 255, 255, 255}, true} + }; + + // Create lights + int light_width = MAX_BRIGHTNESS_STEPS + 1; + int total_light_width = 3 * light_width + 2; + int start_x = (GRID_SIZE_X - total_light_width) / 2; + + Light lights[3] = { + {{start_x, 5}, light_width, 2, {255, 0, 0, 255}, 0, false}, + {{start_x + light_width + 1, 5}, light_width, 2, {0, 255, 0, 255}, 4, false}, + {{start_x + 2*(light_width + 1), 5}, light_width, 2, {0, 0, 255, 255}, 0, false} + }; + + bool running = true; + uint64_t last_move = SDL_GetTicks(); + const uint64_t move_delay = 250; + bool collided_with_wall = false; + + // Matrix data and thread + MatrixData matrix_data = {0}; + matrix_data.running = false; + pthread_mutex_init(&matrix_data.mutex, NULL); + + while (running) { + SDL_Event e; + while (SDL_PollEvent(&e)) { + if (e.type == SDL_EVENT_QUIT) { + running = false; + } + + if (e.type == SDL_EVENT_KEY_DOWN) { + switch (e.key.scancode) { + case SDL_SCANCODE_W: + if (snake.direction.y == 0) + snake.next_direction = (Point){0, -1}; + break; + case SDL_SCANCODE_S: + if (snake.direction.y == 0) + snake.next_direction = (Point){0, 1}; + break; + case SDL_SCANCODE_A: + if (snake.direction.x == 0) + snake.next_direction = (Point){-1, 0}; + break; + case SDL_SCANCODE_D: + if (snake.direction.x == 0) + snake.next_direction = (Point){1, 0}; + break; + default: + break; + } + } + } + + // Move snake at regular intervals + uint64_t now = SDL_GetTicks(); + if (now - last_move >= move_delay) { + bool grow = false; + + for (int i = 0; i < 5; i++) { + if (check_button_collision(&snake, &buttons[i])) { + buttons[i].active = false; + grow = true; + break; + } + } + + move_snake(&snake, grow, &collided_with_wall); + + for (int i = 0; i < 3; i++) { + check_light_brightness(lights, &lights[i], &snake); + } + + if (check_self_collision(&snake) || collided_with_wall) { + printf("Game Over! Length: %d\n", snake.length); + running = false; + } + + last_move = now; + } + + // Render control window + SDL_SetRenderDrawColor(control_ren, 0, 0, 0, 255); + SDL_RenderClear(control_ren); + render_grid(control_ren); + for (int i = 0; i < 3; i++) { + render_light(control_ren, &lights[i]); + } + for (int i = 0; i < 5; i++) { + render_button(control_ren, &buttons[i]); + } + render_snake(control_ren, &snake); + SDL_RenderPresent(control_ren); + + SDL_Delay(16); + } + + // Flash to ESP + system("export MDK=/usr/src/u_esp/baremetal/mdk"); + system("export ARCH=esp32c3"); + system("export PORT=/dev/ttyUSB0"); + + int red = 0; + int green = 0; + int blue = 0; + + if (lights[0].brightness > 0) red = lights[0].brightness; + if (lights[1].brightness > 0) green = lights[1].brightness; + if (lights[2].brightness > 0) blue = lights[2].brightness; + + char cmd[1024]; + snprintf(cmd, sizeof(cmd), + "make -C ../src/ clean && " + "make -C ../src/ build " + "EXTRA_CFLAGS='-DWALL_COLLISION=%d -DSNAKE_START_LENGTH=%d -DMAX_BRIGHTNESS=%d -DSNAKE_COLOR_R=%d -DSNAKE_COLOR_G=%d -DSNAKE_COLOR_B=%d' && " + "make -C ../src/ flash", + collided_with_wall, snake.length, max_brightness, red, green, blue); + system(cmd); + + // Now start the monitor and matrix reader thread + char monitor_cmd[512]; + snprintf(monitor_cmd, sizeof(monitor_cmd), + "make -C ../src/ monitor"); + + matrix_data.pipe = popen(monitor_cmd, "r"); + if (matrix_data.pipe) { + matrix_data.running = true; + + pthread_t reader_thread; + pthread_create(&reader_thread, NULL, matrix_reader_thread, &matrix_data); + + // Continue rendering until user closes + running = true; + while (running) { + SDL_Event e; + while (SDL_PollEvent(&e)) { + if (e.type == SDL_EVENT_QUIT) { + running = false; + } + } + + SDL_SetRenderDrawColor(control_ren, 10, 10, 10, 255); + SDL_RenderClear(control_ren); + render_matrix(control_ren, &matrix_data); + SDL_RenderPresent(control_ren); + + SDL_Delay(16); + } + + matrix_data.running = false; + pthread_join(reader_thread, NULL); + pclose(matrix_data.pipe); + } + + pthread_mutex_destroy(&matrix_data.mutex); + SDL_DestroyRenderer(control_ren); + SDL_DestroyWindow(control_win); + SDL_Quit(); + + return 0; +} diff --git a/snake_SDL/editor/sdl3_move b/snake_SDL/editor/sdl3_move new file mode 100755 index 0000000..ce178b9 Binary files /dev/null and b/snake_SDL/editor/sdl3_move differ diff --git a/snake_SDL/export.sh b/snake_SDL/export.sh new file mode 100644 index 0000000..333b226 --- /dev/null +++ b/snake_SDL/export.sh @@ -0,0 +1,3 @@ +export MDK=/usr/src/u_esp/baremetal/mdk # Points to MDK directory +export ARCH=esp32c3 # Valid choices: esp32 esp32c3 +export PORT=/dev/ttyUSB0 # Serial port for flashing diff --git a/snake_SDL/make_compiler.sh b/snake_SDL/make_compiler.sh new file mode 100644 index 0000000..4faadfb --- /dev/null +++ b/snake_SDL/make_compiler.sh @@ -0,0 +1,10 @@ +make -C src/ clean && +make -C src/ build EXTRA_CFLAGS="\ + -DWALL_COLLISION=0 \ + -DSNAKE_START_LENGTH=7 \ + -DMAX_BRIGHTNESS=7 \ + -DSNAKE_COLOR_R=0 \ + -DSNAKE_COLOR_G=7 \ + -DSNAKE_COLOR_B=0" && +make -C src/ flash && +make -C src/ monitor diff --git a/snake_SDL/src/.snake.h.swp b/snake_SDL/src/.snake.h.swp new file mode 100644 index 0000000..25255b9 Binary files /dev/null and b/snake_SDL/src/.snake.h.swp differ diff --git a/snake_SDL/src/Makefile b/snake_SDL/src/Makefile new file mode 100644 index 0000000..f621d81 --- /dev/null +++ b/snake_SDL/src/Makefile @@ -0,0 +1,3 @@ +SOURCES = main.c snake.c matrix.c + +include $(MDK)/$(ARCH)/build.mk diff --git a/snake_SDL/src/defaults.h b/snake_SDL/src/defaults.h new file mode 100644 index 0000000..2289f5e --- /dev/null +++ b/snake_SDL/src/defaults.h @@ -0,0 +1,58 @@ +#ifndef DEFAULTS_H +#define DEFAULTS_H + + + + +#define WS2812_PIN 8 +#define MATRIX_WIDTH 16 +#define MATRIX_HEIGHT 16 +#define NUM_PIXELS MATRIX_WIDTH*MATRIX_HEIGHT +#define MAX_SNAKE_LENGTH 1024 + +// down right left up +// #define PIN_BUTTON_LEFT 4 down +// #define PIN_BUTTON_DOWN 5 right +// #define PIN_BUTTON_UP 9 left +// #define PIN_BUTTON_RIGHT 10 up + +#define PIN_BUTTON_LEFT 4 +#define PIN_BUTTON_DOWN 5 +#define PIN_BUTTON_UP 9 +#define PIN_BUTTON_RIGHT 10 + + +#ifndef WALL_COLLISION +#define WALL_COLLISION 0 +#endif + +#ifndef SNAKE_START_LENGTH +#define SNAKE_START_LENGTH 2 +#endif + +#ifndef MAX_BRIGHTNESS +#define MAX_BRIGHTNESS 7 +#endif + +#ifndef SNAKE_COLOR_R +#define SNAKE_COLOR_R SNAKE_MAX_BRIGHTNESS +#endif + +#ifndef SNAKE_COLOR_G +#define SNAKE_COLOR_G 0 +#endif + +#ifndef SNAKE_COLOR_B +#define SNAKE_COLOR_B 0 +#endif + +#define SNAKE_COLOR ((Color){ SNAKE_COLOR_G, SNAKE_COLOR_R, SNAKE_COLOR_B }) + +#define GREEN ((Color){ MAX_BRIGHTNESS,0,0 }) +#define RED ((Color){ 0,MAX_BRIGHTNESS,0 }) +#define BLUE ((Color){ 0,0,MAX_BRIGHTNESS }) +#define BLACK ((Color){ 0,0,0 }) + + + +#endif diff --git a/snake_SDL/src/firmware.bin b/snake_SDL/src/firmware.bin new file mode 100644 index 0000000..caf2aac Binary files /dev/null and b/snake_SDL/src/firmware.bin differ diff --git a/snake_SDL/src/firmware.elf b/snake_SDL/src/firmware.elf new file mode 100755 index 0000000..37b4d65 Binary files /dev/null and b/snake_SDL/src/firmware.elf differ diff --git a/snake_SDL/src/main.c b/snake_SDL/src/main.c new file mode 100644 index 0000000..1744d08 --- /dev/null +++ b/snake_SDL/src/main.c @@ -0,0 +1,137 @@ +#include "defaults.h" + +#include "snake.h" +#include "matrix.h" + +int main(void) { + wdt_disable(); + gpio_output(WS2812_PIN); + + + static LEDMatrix m; + static Button b1, b2, b3, b4; + + matrix = &m; + + // Initialize buttons + Button* buttons[] = { &b1, &b2, &b3, &b4 }; + uint8_t pins[] = { PIN_BUTTON_LEFT, + PIN_BUTTON_DOWN, + PIN_BUTTON_UP, + PIN_BUTTON_RIGHT }; + uint64_t now; + uint64_t last_update; + uint64_t interval; + + for (int i = 0; i < 4; i++) { + delay_ms(10); + buttons[i]->pin = pins[i]; + gpio_input(buttons[i]->pin); + delay_ms(10); + buttons[i]->state_init = gpio_read(buttons[i]->pin); + delay_ms(10); + buttons[i]->state_prev = buttons[i]->state_init; + } + +main_menu: + printf("# GAME MENU!\n", MATRIX_WIDTH); + printf("Game Mode: %d\n", game_mode); + printf("Start Game: Button 1 && Button 4\n", MATRIX_WIDTH); + printf("End Game: Button 2 && Button 3\n", MATRIX_WIDTH); + printf("Prev Mode: Button 2\n", MATRIX_WIDTH); + printf("Next Mode: Button 3\n", MATRIX_WIDTH); + last_update = 0; + interval = 1000000; + for (;;) { + uint64_t time_diff = uptime_us() - last_update; + delay_ms(10); + if ( gpio_read(buttons[0]->pin) != buttons[0]->state_init && + gpio_read(buttons[3]->pin) != buttons[3]->state_init) { + goto start_game; + } else + if ( gpio_read(buttons[1]->pin) != buttons[1]->state_init && + gpio_read(buttons[2]->pin) != buttons[2]->state_init) { + goto end; + } else + if ( gpio_read(buttons[2]->pin) != buttons[2]->state_init ) { + game_mode = (game_mode + 1) % GAME_MODE_LAST; + } else + if ( gpio_read(buttons[1]->pin) != buttons[1]->state_init ) { + game_mode--; + game_mode = game_mode < 0 ? 0 : game_mode; + } + + delay_ms(10); + if (time_diff >= interval) { + last_update = uptime_us(); + } + } + + +start_game: + printf("# GAME START!\n", MATRIX_WIDTH); + matrix_Blink(3, 300); + + matrix_ClearAll(BLACK); + + Snake snake; + snake_Init(&snake, MATRIX_WIDTH/2, MATRIX_HEIGHT/2); + + now = uptime_us(); + last_update = now; + interval = 500000; + + + spawn_apple(); + + while (1) { + now = uptime_us(); + uint64_t time_diff = now - last_update; + bool is_next_step = time_diff >= interval; + + // INPUT + if ( gpio_read(buttons[0]->pin) != buttons[0]->state_init && + gpio_read(buttons[3]->pin) != buttons[3]->state_init) { + delay_ms(10); + goto start_game; + } + + for (int i = 0; i < 4; i++) { + bool curr = gpio_read(buttons[i]->pin); + if (curr != buttons[i]->state_prev) { + buttons[i]->state_prev = curr; + + switch (i) { + case 0: snake_SetDirection(&snake, DIR_LEFT); break; + case 1: snake_SetDirection(&snake, DIR_DOWN); break; + case 2: snake_SetDirection(&snake, DIR_UP); break; + case 3: snake_SetDirection(&snake, DIR_RIGHT); break; + } + } + } + + + if (!snake_Move(&snake, is_next_step, interval, time_diff)) { + break; + } + if (is_next_step) + last_update = now; + + // UPDATE + matrix_Update(); + + // RENDER + matrix_WriteLed(); + + if (is_next_step) + matrix_Print(); + + delay_ms(10); + } + + goto main_menu; + +end: + matrix_ClearAll(RED); + printf("# GAME END!\n", MATRIX_WIDTH); +} diff --git a/snake_SDL/src/matrix.c b/snake_SDL/src/matrix.c new file mode 100644 index 0000000..719ae43 --- /dev/null +++ b/snake_SDL/src/matrix.c @@ -0,0 +1,209 @@ +#include "matrix.h" + + + + +static uint8_t pixels_buffer[NUM_PIXELS * 3]; + +GameMode game_mode; + +Button* button_1; +Button* button_2; +Button* button_3; +Button* button_4; +LEDMatrix* matrix; + + + + +// Generate a pseudo-random number in [0, max) +int random_Int(int max) { + uint64_t t = uptime_us(); // get current microseconds + t ^= t >> 33; // mix bits + t *= 0xff51afd7ed558ccdULL; // arbitrary constant + t ^= t >> 33; + t *= 0xc4ceb9fe1a85ec53ULL; // arbitrary constant + t ^= t >> 33; + return (int)t % max; // limit to range +} + + + +void +matrix_Init(Color color) { + for (int y = 0; y < MATRIX_HEIGHT; y++) { + for (int x = 0; x < MATRIX_WIDTH; x++) { + matrix->pixels[y][x].color = color; + matrix->pixels[y][x].type = NONE; + } + } +} + +void +matrix_SetPixelColor(int x, int y, Color c) { + if (x < 0 || x >= MATRIX_WIDTH || y < 0 || y >= MATRIX_HEIGHT) return; + matrix->pixels[y][x].color = c; +} + + +void +matrix_ToMdkBuffer() { + for (int y = 0, yy = MATRIX_HEIGHT-1; y < MATRIX_HEIGHT; y++, yy--) { + int xx = 0; + bool is_un = (y+1)%2; + for (int x = is_un ? 0 : MATRIX_WIDTH-1; + is_un ? (x < MATRIX_WIDTH) + : (x >= 0) ; + xx++, + is_un ? x++ + : x--) { + int idx = (y * MATRIX_WIDTH + xx) * 3; + pixels_buffer[idx + 0] = matrix->pixels[yy][x].color.g; + pixels_buffer[idx + 1] = matrix->pixels[yy][x].color.r; + pixels_buffer[idx + 2] = matrix->pixels[yy][x].color.b; + } + } + /* + for (int y = 0; y < MATRIX_HEIGHT; y++) { + for (int x = 0; x < MATRIX_WIDTH; x++) { + int idx = (y * MATRIX_WIDTH + x) * 3; + pixels_buffer[idx + 0] = matrix->pixels[y][x].color.g; + pixels_buffer[idx + 1] = matrix->pixels[y][x].color.r; + pixels_buffer[idx + 2] = matrix->pixels[y][x].color.b; + } + } + */ +} + +void +matrix_WriteLed() { + matrix_ToMdkBuffer(); + ws2812_show(WS2812_PIN, pixels_buffer, NUM_PIXELS * 3); +} + +void +matrix_ClearAll(Color color) { + matrix_Init(color); + matrix_WriteLed(); +} + + + +void +matrix_Blink(int count, unsigned long delay_millis) { + Color green = {0, MAX_BRIGHTNESS, 0}; + Color black = {0, 0, 0}; + + for (int i = 0; i < count; i++) { + // Set all pixels to green + for (int y = 0; y < MATRIX_HEIGHT; y++) { + for (int x = 0; x < MATRIX_WIDTH; x++) { + matrix_SetPixelColor(x, y, green); + } + } + matrix_WriteLed(); + delay_ms(delay_millis); + + // Set all pixels to black + for (int y = 0; y < MATRIX_HEIGHT; y++) { + for (int x = 0; x < MATRIX_WIDTH; x++) { + matrix_SetPixelColor(x, y, black); + } + } + matrix_WriteLed(); + delay_ms(delay_millis); + } +} + + + + + +static inline Color +hsv_to_rgb_u8(uint8_t h, uint8_t s, uint8_t v) { + uint8_t region = h / 43; + uint8_t rem = (h - region * 43) * 6; + + uint8_t p = (uint8_t)(v * (255 - s)) >> 8; + uint8_t q = (uint8_t)(v * (255 - ((s * rem) >> 8))) >> 8; + uint8_t t = (uint8_t)(v * (255 - ((s * (255 - rem)) >> 8))) >> 8; + + switch (region) { + case 0: return (Color){ v, t, p }; + case 1: return (Color){ q, v, p }; + case 2: return (Color){ p, v, t }; + case 3: return (Color){ p, q, v }; + case 4: return (Color){ t, p, v }; + default:return (Color){ v, p, q }; + } +} + + + +void matrix_Print() { + printf("## START MATRIX\n", MATRIX_HEIGHT); + for (int y = 0; y < MATRIX_HEIGHT; y++) { + for (int x = 0; x < MATRIX_WIDTH; x++) + printf("%u|%u|%u ", + matrix->pixels[y][x].color.r, + matrix->pixels[y][x].color.g, + matrix->pixels[y][x].color.b); + //printf("%3d ", (matrix->pixels[y][x].color.r+matrix->pixels[y][x].color.g+matrix->pixels[y][x].color.b)); + //printf("%d ", matrix->pixels[y][x].type); + printf("\n\n", MATRIX_HEIGHT); + } + printf("\n", MATRIX_HEIGHT); +} + + + +void matrix_Update() { + static uint64_t last = 0; + static uint64_t now = 0; + static int counter = 0; + + if (last == 0) { + last = uptime_us(); + now = last; + } + + now = uptime_us(); + uint64_t interval = 50000; + + if (now - last > interval) { + last = now; + counter++; + } + + for (int y = 0; y < MATRIX_HEIGHT; y++) { + for (int x = 0; x < MATRIX_WIDTH; x++) { + + Color c = matrix->pixels[y][x].color; + + if (game_mode == FANCY) { + if (matrix->pixels[y][x].type == NONE) { + int cx = MATRIX_WIDTH / 2; + int cy = MATRIX_HEIGHT / 2; + + int dx = (uint16_t)(x - cx); + int dy = (uint16_t)(y - cy); + + uint16_t dist = (uint16_t)(dx*dx + dy*dy); + uint16_t maxd = (uint16_t)(cx*cx + cy*cy); + + uint8_t hue = (uint8_t)((dist * 255) / maxd); + + c = hsv_to_rgb_u8(hue, 255, 255); + + /* downscale to MAX_BRIGHTNESS */ + c.r = (uint8_t)(c.r + counter) % MAX_BRIGHTNESS; + c.g = (uint8_t)(c.g + counter) % MAX_BRIGHTNESS; + c.b = (uint8_t)(c.b + counter) % MAX_BRIGHTNESS; + } + } + + matrix_SetPixelColor(x, y, c); + } + } +} + diff --git a/snake_SDL/src/matrix.h b/snake_SDL/src/matrix.h new file mode 100644 index 0000000..a2d2ec2 --- /dev/null +++ b/snake_SDL/src/matrix.h @@ -0,0 +1,77 @@ +#ifndef MATRIX_H +#define MATRIX_H + +#include <mdk.h> +#include <stdint.h> +#include <stdbool.h> + + +#include "defaults.h" +#include "snake.h" + + +typedef enum GameMode_e { + NORMAL, + FANCY, + GAME_MODE_LAST, +} GameMode; + + +typedef enum PixelType_e { + NONE, + SNAKE, + APPLE, +} PixelType; + +typedef struct Pixel_s { + Color color; + PixelType type; +} Pixel; + +typedef struct LEDMatrix_s { + Pixel pixels[MATRIX_HEIGHT][MATRIX_WIDTH]; +} LEDMatrix; + +typedef struct { + int pin; + bool state_init; + bool state_prev; + bool state_curr; +} Button; + + +extern GameMode game_mode; + +extern LEDMatrix* matrix; +extern Button* button_1; +extern Button* button_2; +extern Button* button_3; +extern Button* button_4; + +int random_Int(int max); + +void +matrix_Init(Color color); + +void +matrix_SetPixelColor(int x, int y, Color c); + +void +matrix_ToMdkBuffer(); + +void +matrix_WriteLed(); + +void +matrix_ClearAll(Color color); + +void +matrix_Blink(int count, unsigned long delay_millis); + +void +matrix_Print(); + +void +matrix_Update(); + +#endif diff --git a/snake_SDL/src/snake.c b/snake_SDL/src/snake.c new file mode 100644 index 0000000..df92ec3 --- /dev/null +++ b/snake_SDL/src/snake.c @@ -0,0 +1,158 @@ +#include "snake.h" + +#include "matrix.h" + + +// Initialize snake with starting position +void snake_Init(Snake* snake, int start_x, int start_y) { + for (int i = 0; i < SNAKE_START_LENGTH; i++) { + snake->nodes[i].x = start_x - i; + snake->nodes[i].y = start_y - i; + snake->nodes[i].color = (i == 0) ? BLACK : SNAKE_COLOR; + } + + snake->head_index = SNAKE_START_LENGTH - 1; + snake->tail_index = 0; + snake->direction = DIR_RIGHT; + snake->length = SNAKE_START_LENGTH; + + printf("START SNAKE: %d %d, length %d\n", start_x, start_y, SNAKE_START_LENGTH); +} + +void snake_SetDirection(Snake* snake, Direction new_dir) { + if ((snake->direction == DIR_UP && new_dir == DIR_DOWN) || + (snake->direction == DIR_DOWN && new_dir == DIR_UP) || + (snake->direction == DIR_LEFT && new_dir == DIR_RIGHT) || + (snake->direction == DIR_RIGHT && new_dir == DIR_LEFT)) { + return; + } + snake->direction = new_dir; +} + + +static inline unsigned char rise_dt( + unsigned char v, + unsigned char target, + uint64_t diff, + uint64_t diff_max) +{ + if (v >= target) return v; + + uint32_t delta = (uint32_t)((diff * target) / diff_max); + if (delta == 0) delta = 1; + + uint32_t nv = v + delta; + return (nv > target) ? target : (unsigned char)nv; +} + +static inline unsigned char fall_dt( + unsigned char v, + uint64_t diff, + uint64_t diff_max) +{ + if (v == 0) return 0; + + uint32_t delta = (uint32_t)((diff * MAX_BRIGHTNESS) / diff_max); + if (delta == 0) delta = 1; + + return (delta >= v) ? 0 : (unsigned char)(v - delta); +} + +bool snake_Move(Snake* snake, bool is_next_step, uint64_t diff_max, uint64_t diff) { + SnakeTile* head = &snake->nodes[snake->head_index]; + SnakeTile* tail = &snake->nodes[snake->tail_index]; + + // FADE + Color sc = SNAKE_COLOR; + + head->color.r = rise_dt(head->color.r, sc.r, diff, diff_max); + head->color.g = rise_dt(head->color.g, sc.g, diff, diff_max); + head->color.b = rise_dt(head->color.b, sc.b, diff, diff_max); + + tail->color.r = fall_dt(tail->color.r, diff, diff_max); + tail->color.g = fall_dt(tail->color.g, diff, diff_max); + tail->color.b = fall_dt(tail->color.b, diff, diff_max); + + matrix->pixels[head->y][head->x].color = head->color; + matrix->pixels[tail->y][tail->x].color = tail->color; + + if (!is_next_step) + return true; + + // MOVE + int new_x = head->x; + int new_y = head->y; + + switch (snake->direction) { + case DIR_UP: new_y--; break; + case DIR_DOWN: new_y++; break; + case DIR_LEFT: new_x--; break; + case DIR_RIGHT: new_x++; break; + } + + if (WALL_COLLISION && + (new_x >= MATRIX_WIDTH || + new_x < 0 || + new_y >= MATRIX_HEIGHT || + new_y < 0)) { + printf("Collided with Wall %d %d %d %d!\n", new_x, new_y, MATRIX_WIDTH, MATRIX_HEIGHT); + return false; + } + + new_x = (new_x + MATRIX_WIDTH) % MATRIX_WIDTH; + new_y = (new_y + MATRIX_HEIGHT) % MATRIX_HEIGHT; + + int next = (snake->head_index + 1) % MAX_SNAKE_LENGTH; + SnakeTile* head_next = &snake->nodes[next]; + + head_next->x = new_x; + head_next->y = new_y; + head_next->color = (Color){0, 0, 0}; + + snake->head_index = next; + + bool grow = matrix->pixels[new_y][new_x].type == APPLE; + + matrix->pixels[new_y][new_x].type = SNAKE; + + if (!grow) { + snake->tail_index = (snake->tail_index + 1) % MAX_SNAKE_LENGTH; + } else { + spawn_apple(); + snake->length++; + } + + return !snake_IsColliding(snake); +} + +bool snake_IsColliding(Snake* snake) { + int idx = snake->tail_index; + int count = snake->length - 1; + + int hx = snake->nodes[snake->head_index].x; + int hy = snake->nodes[snake->head_index].y; + + while (count-- > 0) { + if (snake->nodes[idx].x == hx && snake->nodes[idx].y == hy) return true; + idx = (idx + 1) % MAX_SNAKE_LENGTH; + } + return false; +} + + + + + +void spawn_apple() { + int x, y; + + // Find an empty pixel + do { + x = random_Int(MATRIX_WIDTH); + y = random_Int(MATRIX_HEIGHT); + } while (matrix->pixels[y][x].type != NONE); + + // Set the apple pixel + matrix->pixels[y][x].type = APPLE; + matrix->pixels[y][x].color = (Color){0, MAX_BRIGHTNESS, 0}; +} diff --git a/snake_SDL/src/snake.h b/snake_SDL/src/snake.h new file mode 100644 index 0000000..4a94445 --- /dev/null +++ b/snake_SDL/src/snake.h @@ -0,0 +1,48 @@ +#ifndef SNAKE_H +#define SNAKE_H + +#include <stdbool.h> +#include <stdio.h> +#include <stdint.h> + +#include "defaults.h" + + +typedef struct { + unsigned char g; + unsigned char r; + unsigned char b; +} Color; + + +typedef enum { + DIR_UP, + DIR_DOWN, + DIR_LEFT, + DIR_RIGHT +} Direction; + +typedef struct { + int x; + int y; + Color color; + Color color_old; +} SnakeTile; + +typedef struct { + SnakeTile nodes[MAX_SNAKE_LENGTH]; + int head_index; + int tail_index; + Direction direction; + int length; +} Snake; + +// Function declarations +void snake_Init(Snake* snake, int start_x, int start_y); +void snake_SetDirection(Snake* snake, Direction new_dir); +bool snake_Move(Snake* snake, bool is_next_step, uint64_t diff_max, uint64_t diff); +bool snake_IsColliding(Snake* snake); + +void spawn_apple(); + +#endif // SNAKE_H diff --git a/snake_ascii_editor/main b/snake_ascii_editor/main new file mode 100755 index 0000000..0d1c1c5 Binary files /dev/null and b/snake_ascii_editor/main differ diff --git a/snake_ascii_editor/main.c b/snake_ascii_editor/main.c new file mode 100644 index 0000000..7c239ba --- /dev/null +++ b/snake_ascii_editor/main.c @@ -0,0 +1,664 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#ifdef _WIN32 +#include <conio.h> +#include <windows.h> +#define CLEAR "cls" +#else +#include <unistd.h> +#include <termios.h> +#include <fcntl.h> +#define CLEAR "clear" +int getch(void) { + struct termios o, n; + tcgetattr(STDIN_FILENO, &o); + n = o; + n.c_lflag &= ~(ICANON | ECHO); + tcsetattr(STDIN_FILENO, TCSANOW, &n); + int ch = getchar(); + tcsetattr(STDIN_FILENO, TCSANOW, &o); + return ch; +} +#endif +#define SIZE 16 +#define MAX_SNAKE 256 +#define MAX_VARS 10 +#define MAX_CODE 8192 +typedef struct { + int x, y; +} Point; +typedef enum { + NONE, VARIABLE, ARRAY, FUNCTION, MAIN_FUNC, STATEMENT +} EntityType; +typedef enum { + STATE_NORMAL, STATE_CREATE, STATE_SELECT_ENTITY, STATE_SET_VALUE, + STATE_SET_DIM, STATE_SET_ARRAY_SIZE, STATE_SELECT_VAR, STATE_SELECT_STATEMENT, + STATE_BUILD_EXPR, STATE_BUILD_IF_COND, STATE_BUILD_WHILE_COND, STATE_BUILD_DO_COND, STATE_SET_LOOP_INIT, + STATE_SET_LOOP_COND, STATE_SET_LOOP_INC +} GameState; +typedef enum { + STMT_NONE, STMT_IF, STMT_WHILE, STMT_FOR, STMT_DO_WHILE, STMT_ASSIGN, STMT_CALL, + STMT_BREAK, STMT_RETURN, STMT_BLOCK_END +} StatementType; +typedef enum { + BT_NONE, BT_IF, BT_WHILE, BT_FOR, BT_DO_WHILE, BT_FUNC, BT_MAIN +} BlockType; +typedef struct { + char name[32]; + char value[64]; + int used; +} Variable; +typedef struct { + char name[32]; + int dimensions; + int sizes[3]; + int used; +} Array; +typedef struct { + char name[32]; + char params[128]; + char body[512]; + int used; +} Function; +Point snake[MAX_SNAKE]; +int slen = 3; +int dx = 1, dy = 0; +char grid[SIZE][SIZE]; +char code[MAX_CODE] = ""; +GameState state = STATE_NORMAL; +EntityType creating = NONE; +StatementType current_stmt = STMT_NONE; +int selected_entity = 0; +Variable vars[MAX_VARS]; +Array arrs[MAX_VARS]; +Function funcs[MAX_VARS]; +int has_main = 0; +char current_value[128] = ""; +char current_value2[128] = ""; +char current_operation[8] = ""; +int setting_second_value = 0; +int current_dim_count = 0; +int indent_level = 0; +// For building complex statements +char expr_buffer[256] = ""; +char condition_buffer[256] = ""; +char loop_init[128] = ""; +char loop_cond[128] = ""; +char loop_inc[128] = ""; +BlockType block_stack[32]; +int block_level = 0; +#ifndef _WIN32 +int old_flags; +#endif +void add_indent() { + for (int i = 0; i < indent_level; i++) { + strcat(code, " "); + } +} +void init_game() { + snake[0].x = 3; snake[0].y = 8; + snake[1].x = 2; snake[1].y = 8; + snake[2].x = 1; snake[2].y = 8; + + for (int i = 0; i < MAX_VARS; i++) { + vars[i].used = 0; + arrs[i].used = 0; + funcs[i].used = 0; + } + memset(block_stack, 0, sizeof(block_stack)); + block_level = 0; +#ifndef _WIN32 + old_flags = fcntl(STDIN_FILENO, F_GETFL, 0); + fcntl(STDIN_FILENO, F_SETFL, old_flags | O_NONBLOCK); +#endif +} +void check_collision() { + int hx = snake[0].x; + int hy = snake[0].y; + + // Wrap through walls + if (hx < 0) hx = SIZE - 1; + if (hx >= SIZE) hx = 0; + if (hy < 0) hy = SIZE - 1; + if (hy >= SIZE) hy = 0; + snake[0].x = hx; + snake[0].y = hy; + + // Check special tiles + if (state == STATE_NORMAL) { + // Creation tiles + if (hx == 1 && hy == 2) { + state = STATE_CREATE; + creating = VARIABLE; + } else if (hx == 1 && hy == 4) { + state = STATE_CREATE; + creating = ARRAY; + } else if (hx == 1 && hy == 6) { + state = STATE_CREATE; + creating = FUNCTION; + } else if (hx == 1 && hy == 8) { + state = STATE_CREATE; + creating = MAIN_FUNC; + } else if (hx == 1 && hy == 10) { + state = STATE_SELECT_STATEMENT; + creating = STATEMENT; + } + // Print code + else if (hx == 15 && hy == 14) { + printf("\n=== GENERATED CODE ===\n%s\n=====================\n", code); + printf("Press any key to continue..."); + while (getch() != 10); + } + // Close block } + else if (hx == 15 && hy == 13) { + if (block_level > 0) { + BlockType bt = block_stack[--block_level]; + indent_level--; + add_indent(); + if (bt == BT_DO_WHILE) { + state = STATE_BUILD_DO_COND; + strcpy(condition_buffer, ""); + strcpy(current_value, ""); + strcpy(current_value2, ""); + strcpy(current_operation, ""); + setting_second_value = 0; + } else { + strcat(code, "}\n"); + } + } + } + } else if (state == STATE_CREATE) { + // Select entity (variables/arrays/functions 0-9) + if (hx == 3 && hy >= 2 && hy <= 11) { + selected_entity = hy - 2; + + if (creating == VARIABLE) { + if (!vars[selected_entity].used) { + sprintf(vars[selected_entity].name, "var%d", selected_entity); + vars[selected_entity].used = 1; + strcpy(vars[selected_entity].value, ""); + state = STATE_SET_VALUE; + strcpy(current_value, ""); + strcpy(current_value2, ""); + strcpy(current_operation, ""); + setting_second_value = 0; + } + } else if (creating == ARRAY) { + if (!arrs[selected_entity].used) { + sprintf(arrs[selected_entity].name, "arr%d", selected_entity); + arrs[selected_entity].used = 1; + arrs[selected_entity].dimensions = 0; + for (int i = 0; i < 3; i++) arrs[selected_entity].sizes[i] = 0; + + state = STATE_SET_DIM; + strcpy(current_value, ""); + current_dim_count = 0; + } + } else if (creating == FUNCTION) { + if (!funcs[selected_entity].used) { + sprintf(funcs[selected_entity].name, "func%d", selected_entity); + funcs[selected_entity].used = 1; + strcpy(funcs[selected_entity].params, ""); + strcpy(funcs[selected_entity].body, ""); + + add_indent(); + char line[256]; + sprintf(line, "void %s() {\n", funcs[selected_entity].name); + strcat(code, line); + indent_level++; + block_stack[block_level++] = BT_FUNC; + state = STATE_NORMAL; + } + } else if (creating == MAIN_FUNC) { + if (!has_main) { + has_main = 1; + add_indent(); + strcat(code, "int main() {\n"); + indent_level++; + block_stack[block_level++] = BT_MAIN; + state = STATE_NORMAL; + } + } + } + } else if (state == STATE_SELECT_STATEMENT) { + // Use selectors at col 3 + if (hx == 3 && hy >= 2 && hy <= 11) { + int stmt_idx = hy - 2; + StatementType stmts[] = {STMT_IF, STMT_WHILE, STMT_FOR, STMT_DO_WHILE, + STMT_ASSIGN, STMT_CALL, STMT_BREAK, STMT_RETURN, STMT_BLOCK_END}; + if (stmt_idx < 9) { + current_stmt = stmts[stmt_idx]; + + if (current_stmt == STMT_BREAK) { + add_indent(); + strcat(code, "break;\n"); + state = STATE_NORMAL; + } else if (current_stmt == STMT_RETURN) { + add_indent(); + strcat(code, "return "); + state = STATE_BUILD_EXPR; + strcpy(expr_buffer, ""); + strcpy(current_value, ""); + strcpy(current_value2, ""); + strcpy(current_operation, ""); + setting_second_value = 0; + } else if (current_stmt == STMT_BLOCK_END) { + if (block_level > 0) { + BlockType bt = block_stack[--block_level]; + indent_level--; + add_indent(); + if (bt == BT_DO_WHILE) { + state = STATE_BUILD_DO_COND; + strcpy(condition_buffer, ""); + strcpy(current_value, ""); + strcpy(current_value2, ""); + strcpy(current_operation, ""); + setting_second_value = 0; + } else { + strcat(code, "}\n"); + } + } + state = STATE_NORMAL; + } else if (current_stmt == STMT_IF) { + state = STATE_BUILD_IF_COND; + strcpy(condition_buffer, ""); + strcpy(current_value, ""); + strcpy(current_value2, ""); + strcpy(current_operation, ""); + setting_second_value = 0; + } else if (current_stmt == STMT_WHILE) { + state = STATE_BUILD_WHILE_COND; + strcpy(condition_buffer, ""); + strcpy(current_value, ""); + strcpy(current_value2, ""); + strcpy(current_operation, ""); + setting_second_value = 0; + } else if (current_stmt == STMT_FOR) { + state = STATE_SET_LOOP_INIT; + strcpy(loop_init, ""); + strcpy(loop_cond, ""); + strcpy(loop_inc, ""); + strcpy(current_value, ""); + strcpy(current_value2, ""); + strcpy(current_operation, ""); + setting_second_value = 0; + } else if (current_stmt == STMT_DO_WHILE) { + add_indent(); + strcat(code, "do {\n"); + indent_level++; + block_stack[block_level++] = BT_DO_WHILE; + state = STATE_NORMAL; + } else if (current_stmt == STMT_ASSIGN || current_stmt == STMT_CALL) { + state = STATE_BUILD_EXPR; + strcpy(expr_buffer, ""); + strcpy(current_value, ""); + strcpy(current_value2, ""); + strcpy(current_operation, ""); + setting_second_value = 0; + } + } + } + } else if (state == STATE_SET_DIM) { + // Set dimension count for array + if (hx >= 2 && hx <= 11 && hy == 14) { + int digit = hx - 2; + if (digit >= 1 && digit <= 3) { + arrs[selected_entity].dimensions = digit; + state = STATE_SET_ARRAY_SIZE; + strcpy(current_value, ""); + current_dim_count = 0; + } + } + } else if (state == STATE_SET_ARRAY_SIZE) { + // Set size for each dimension + if (hx >= 2 && hx <= 11 && hy == 14) { + int digit = hx - 2; + char d[2] = {digit + '0', '\0'}; + strcat(current_value, d); + } + // Confirm dimension size with S + else if (hx == 15 && hy == 10) { + if (strlen(current_value) > 0) { + arrs[selected_entity].sizes[current_dim_count] = atoi(current_value); + current_dim_count++; + strcpy(current_value, ""); + + // If all dimensions set, generate code + if (current_dim_count >= arrs[selected_entity].dimensions) { + add_indent(); + char line[128]; + sprintf(line, "int %s", arrs[selected_entity].name); + for (int i = 0; i < arrs[selected_entity].dimensions; i++) { + char dim[16]; + sprintf(dim, "[%d]", arrs[selected_entity].sizes[i]); + strcat(line, dim); + } + strcat(line, ";\n"); + strcat(code, line); + state = STATE_NORMAL; + } + } + } + } else if (state == STATE_SET_VALUE || state == STATE_BUILD_IF_COND || state == STATE_BUILD_WHILE_COND || state == STATE_BUILD_DO_COND || state == STATE_BUILD_EXPR || state == STATE_SET_LOOP_INIT || state == STATE_SET_LOOP_COND || state == STATE_SET_LOOP_INC) { + // Number tiles (0-9) + if (hx >= 2 && hx <= 11 && hy == 14) { + int digit = hx - 2; + char d[2] = {digit + '0', '\0'}; + if (setting_second_value) { + strcat(current_value2, d); + } else { + strcat(current_value, d); + } + } + // Variable selection area (col 4) + else if (hx == 4 && hy >= 2 && hy <= 11) { + int var_idx = hy - 2; + if (vars[var_idx].used) { + if (setting_second_value) { + strcat(current_value2, vars[var_idx].name); + } else { + strcat(current_value, vars[var_idx].name); + } + } + } + // Array selection with index (col 6) + else if (hx == 6 && hy >= 2 && hy <= 11) { + int arr_idx = hy - 2; + if (arrs[arr_idx].used) { + char arr_access[64]; + sprintf(arr_access, "%s[", arrs[arr_idx].name); + if (setting_second_value) { + strcat(current_value2, arr_access); + } else { + strcat(current_value, arr_access); + } + } + } + // Function call (col 11) + else if (hx == 11 && hy >= 2 && hy <= 11) { + int func_idx = hy - 2; + if (funcs[func_idx].used) { + char func_call[64]; + sprintf(func_call, "%s(", funcs[func_idx].name); + if (setting_second_value) { + strcat(current_value2, func_call); + } else { + strcat(current_value, func_call); + } + } + } + // Operations + else if (hx == 13 && hy >= 2 && hy <= 13) { + int idx = hy - 2; + if (idx < 12) { + const char *ops[] = {"+", "-", "*", "/", ">", "<", "==", "&&", ">=", "<=", "!=", "||"}; + if (setting_second_value && strlen(current_operation) > 0) { + char temp[256]; + sprintf(temp, " %s %s", current_operation, current_value2); + strcat(current_value, temp); + strcpy(current_value2, ""); + strcpy(current_operation, ""); + } + strcpy(current_operation, ops[idx]); + setting_second_value = 1; + } + } + // Special chars + else if (hx == 12 && hy >= 2 && hy <= 13) { + int idx = hy - 2; + if (idx < 12) { + const char *chars[] = {"[", "]", "(", ")", ",", "=", ";", ".", "!", "~", "&", "*"}; + char s[2] = {chars[idx][0], '\0'}; + if (setting_second_value) { + strcat(current_value2, s); + } else { + strcat(current_value, s); + } + } + } + // Confirm with set button + else if (hx == 15 && hy == 10) { + char line[256]; + char buffer[256] = ""; + if (strlen(current_operation) > 0 && strlen(current_value2) > 0) { + sprintf(buffer, "%s %s %s", current_value, current_operation, current_value2); + } else { + strcpy(buffer, current_value); + } + if (state == STATE_SET_VALUE) { + if (strlen(buffer) > 0) { + add_indent(); + sprintf(line, "int %s = %s;\n", vars[selected_entity].name, buffer); + strcat(code, line); + } else { + add_indent(); + sprintf(line, "int %s;\n", vars[selected_entity].name); + strcat(code, line); + } + state = STATE_NORMAL; + } else if (state == STATE_BUILD_IF_COND || state == STATE_BUILD_WHILE_COND || state == STATE_BUILD_DO_COND) { + strcpy(condition_buffer, buffer); + add_indent(); + if (state == STATE_BUILD_DO_COND) { + strcat(code, "} while ("); + strcat(code, condition_buffer); + strcat(code, ");\n"); + } else { + strcat(code, state == STATE_BUILD_IF_COND ? "if (" : "while ("); + strcat(code, condition_buffer); + strcat(code, ") {\n"); + indent_level++; + block_stack[block_level++] = state == STATE_BUILD_IF_COND ? BT_IF : BT_WHILE; + } + state = STATE_NORMAL; + } else if (state == STATE_SET_LOOP_INIT) { + strcpy(loop_init, buffer); + state = STATE_SET_LOOP_COND; + } else if (state == STATE_SET_LOOP_COND) { + strcpy(loop_cond, buffer); + state = STATE_SET_LOOP_INC; + } else if (state == STATE_SET_LOOP_INC) { + strcpy(loop_inc, buffer); + add_indent(); + sprintf(line, "for (%s; %s; %s) {\n", loop_init, loop_cond, loop_inc); + strcat(code, line); + indent_level++; + block_stack[block_level++] = BT_FOR; + state = STATE_NORMAL; + } else if (state == STATE_BUILD_EXPR) { + strcpy(expr_buffer, buffer); + add_indent(); + if (current_stmt == STMT_RETURN) { + sprintf(line, "return %s;\n", expr_buffer); + } else { + sprintf(line, "%s;\n", expr_buffer); + } + strcat(code, line); + state = STATE_NORMAL; + } + } + } + + // Undo button + if (hx == 15 && hy == 12) { + if (state == STATE_SET_VALUE || state == STATE_BUILD_IF_COND || state == STATE_BUILD_WHILE_COND || state == STATE_BUILD_DO_COND || + state == STATE_BUILD_EXPR || state == STATE_SET_LOOP_INIT || + state == STATE_SET_LOOP_COND || state == STATE_SET_LOOP_INC) { + if (setting_second_value && strlen(current_value2) > 0) { + strcpy(current_value2, ""); + } else if (strlen(current_value) > 0) { + strcpy(current_value, ""); + } else { + strcpy(current_operation, ""); + setting_second_value = 0; + } + } else if (state == STATE_SET_ARRAY_SIZE) { + strcpy(current_value, ""); + } + } +} +void move_snake() { + for (int i = slen - 1; i > 0; i--) { + snake[i] = snake[i - 1]; + } + snake[0].x += dx; + snake[0].y += dy; + check_collision(); +} +void draw() { + system(CLEAR); + + // Clear grid + for (int i = 0; i < SIZE; i++) { + for (int j = 0; j < SIZE; j++) { + grid[i][j] = '.'; + } + } + + // Mark creation tiles + grid[2][1] = 'V'; + grid[4][1] = 'A'; + grid[6][1] = 'F'; + grid[8][1] = 'M'; + grid[10][1] = 'S'; + + // Selection rows + for (int i = 2; i <= 11; i++) { + grid[i][3] = '0' + (i - 2); // Entity selection + if (vars[i - 2].used) grid[i][4] = 'V'; // Vars at col 4 + if (arrs[i - 2].used) grid[i][6] = 'A'; // Arrays at col 6 + if (funcs[i - 2].used) grid[i][11] = 'F'; // Functions at col 11 + } + + // Number tiles + for (int i = 2; i <= 11; i++) { + grid[14][i] = '0' + (i - 2); + } + + // Operation tiles + char display_ops[12] = {'+', '-', '*', '/', '>', '<', '=', '&', 'g', 'l', 'n', 'o'}; + for (int i = 0; i < 12; i++) { + if (2 + i < SIZE) grid[2 + i][13] = display_ops[i]; + } + + // Special character tiles + char display_chars[12] = {'[', ']', '(', ')', ',', '=', ';', '.', '!', '~', '&', '*'}; + for (int i = 0; i < 12; i++) { + if (2 + i < SIZE) grid[2 + i][12] = display_chars[i]; + } + + // Action tiles + grid[10][15] = 'S'; // Set + grid[12][15] = 'U'; // Undo + grid[13][15] = '}'; // Close block + grid[14][15] = 'P'; // Print + + // Draw snake + for (int i = 0; i < slen; i++) { + int x = snake[i].x; + int y = snake[i].y; + if (x >= 0 && x < SIZE && y >= 0 && y < SIZE) { + grid[y][x] = (i == 0) ? '@' : '#'; + } + } + + // Print grid + printf("╔"); + for (int i = 0; i < SIZE * 2; i++) printf("═"); + printf("╗\n"); + + for (int i = 0; i < SIZE; i++) { + printf("║"); + for (int j = 0; j < SIZE; j++) { + printf("%c ", grid[i][j]); + } + printf("║\n"); + } + + printf("╚"); + for (int i = 0; i < SIZE * 2; i++) printf("═"); + printf("╝\n"); + + printf("\nWASD=Move Q=Quit | Indent: %d\n", indent_level); + printf("State: "); + switch (state) { + case STATE_NORMAL: printf("NORMAL"); break; + case STATE_CREATE: printf("CREATING"); break; + case STATE_SELECT_STATEMENT: printf("SELECT STATEMENT"); break; + case STATE_SET_VALUE: printf("SET VALUE"); break; + case STATE_BUILD_IF_COND: + case STATE_BUILD_WHILE_COND: + case STATE_BUILD_DO_COND: printf("BUILD CONDITION"); break; + case STATE_BUILD_EXPR: printf("BUILD EXPRESSION"); break; + case STATE_SET_DIM: printf("SET DIMENSION"); break; + case STATE_SET_ARRAY_SIZE: printf("SET SIZE [%d/%d]", current_dim_count + 1, arrs[selected_entity].dimensions); break; + case STATE_SET_LOOP_INIT: printf("FOR INIT"); break; + case STATE_SET_LOOP_COND: printf("FOR CONDITION"); break; + case STATE_SET_LOOP_INC: printf("FOR INCREMENT"); break; + default: printf("UNKNOWN"); break; + } + printf("\n"); + + if (state == STATE_SET_VALUE || state == STATE_BUILD_IF_COND || state == STATE_BUILD_WHILE_COND || state == STATE_BUILD_DO_COND || + state == STATE_BUILD_EXPR || state == STATE_SET_LOOP_INIT || + state == STATE_SET_LOOP_COND || state == STATE_SET_LOOP_INC) { + printf("Val1: %s | Op: %s | Val2: %s\n", current_value, current_operation, current_value2); + printf("%s\n", setting_second_value ? "(2nd value)" : "(1st value)"); + } else if (state == STATE_SET_DIM) { + printf("Dimensions (1-3): %s\n", current_value); + } else if (state == STATE_SELECT_STATEMENT) { + printf("Selectors 0-9: 0=if 1=while 2=for 3=do-while 4=assign 5=call 6=break 7=return 8=}\n"); + } + + printf("\nLegend:\n"); + printf("V=Var A=Array F=Func M=Main S=Stmt | Col3=Select Col4=Vars Col6=Arrays\n"); + printf("Col11=Funcs | Bottom=0-9 | Right:[]()*+/><=&\n"); + printf("S=Set U=Undo }=Close P=Print\n"); +} +int main() { + init_game(); + char input; + + printf("Programming Snake - Write C code!\n"); + printf("Create variables, arrays, functions, and control structures.\n"); + printf("Press any key to start...\n"); + getch(); + + while (1) { + draw(); + + int ch; +#ifdef _WIN32 + if (_kbhit()) { + ch = getch(); + } else { + ch = -1; + } +#else + ch = getch(); +#endif + if (ch != -1) { + input = (char)ch; + if (input == 'q' || input == 'Q') break; + else if (input == 'w' || input == 'W') { dx = 0; dy = -1; } + else if (input == 's' || input == 'S') { dx = 0; dy = 1; } + else if (input == 'a' || input == 'A') { dx = -1; dy = 0; } + else if (input == 'd' || input == 'D') { dx = 1; dy = 0; } + move_snake(); + } +#ifdef _WIN32 + Sleep(150); +#else + usleep(150000); +#endif + } + +#ifndef _WIN32 + fcntl(STDIN_FILENO, F_SETFL, old_flags); +#endif + + printf("\n=== FINAL CODE ===\n%s\n==================\n", code); + return 0; +}