Nimbin[12]?Embedded / snake_compiler / commits / 0f82526

snake_compiler git · main

Snake compiled to a bare-metal RISC-V microcontroller

riscv embedded bare-metal c snake · first commit 2026-01-18 · last commit 2026-01-18 (8 months ago) · synced 3 days ago · upstream: git.ide3.de/hsnr/compilerbau/snake_compiler

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.gz

init

Christian Immanuel · 2026-01-18 14:13 · 0f8252620ef1825db1e48ff8fea594b69c615c60

 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
+![Process](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;
+}