feat: Smooth Snake game rendering and expand kernel subsystems

- Implement flicker-free atomic double-buffered frame rendering and sub-tick input polling in userland/lazybox/snake.c
- Add multi-input support (PS/2 keyboard scancodes and serial ANSI arrow escape sequences)
- Add sound effects (food beep, game over chime) and live speed scaling (+/- keys, P to pause)
- Add POSIX /dev/shm shared memory subsystem in ipc/shm_posix.c and include/ipc/shm_posix.h
- Add DNS resolver cache with TTL tracking in net/ipv4/dns_cache.c and include/net/dns_cache.h
- Add audio tone and jingle synthesizer in sound/beep.c and include/sound/beep.h
- Add %o octal formatting support in lib/vsprintf.c
- Add LazyBox applets: 'shm', 'dnscache', and 'beep'
This commit is contained in:
subhobhai943 committed 2026-08-21 01:47:37 +05:30
1 parent 7c510dae24
commit 6f7f60bf53
13 files changed
+495 -56

No files matched your search

+3 -3
View File
@@ -127,8 +127,8 @@ CORE_SRCS = kernel/main.c kernel/task.c kernel/sync.c kernel/timer.c kernel/prin
kernel/signal.c kernel/workqueue.c kernel/sub/sub_runtime.c kernel/sub/sub_vm.c \
lib/string.c lib/vsprintf.c lib/bitmap.c \
init/cmdline.c init/service.c usr/initramfs.c block/block.c block/elevator.c \
ipc/sem.c ipc/shm.c ipc/pipe.c ipc/ipc.c ipc/msg.c \
crypto/sha256.c crypto/md5.c crypto/crc32.c crypto/prng.c
ipc/sem.c ipc/shm.c ipc/shm_posix.c ipc/pipe.c ipc/ipc.c ipc/msg.c \
crypto/sha256.c crypto/md5.c crypto/crc32.c crypto/prng.c sound/beep.c
# If no .config exists, default all core options to y
ifeq ($(wildcard .config),)
@@ -224,7 +224,7 @@ endif
# Network Subsystem
ifeq ($(CONFIG_NET_STACK), y)
CONFIG_SRCS-y += net/core/net.c net/core/socket.c net/ipv4/tcp.c net/ipv4/udp.c net/ipv4/dhcp.c net/ipv4/dns.c
CONFIG_SRCS-y += net/core/net.c net/core/socket.c net/ipv4/tcp.c net/ipv4/udp.c net/ipv4/dhcp.c net/ipv4/dns.c net/ipv4/dns_cache.c
endif
ifeq ($(CONFIG_NET_FILTER), y)
CONFIG_SRCS-y += net/filter.c
+3 -3
View File
@@ -203,10 +203,10 @@ make run-gui
|---|---|
| **Core & Shell** | `lazybox`, `sh`, `echo`, `env`, `export`, `whoami`, `id`, `date`, `cal`, `jsonquery`, `subi` |
| **Filesystem & Search** | `nano`, `ls`, `cat`, `touch`, `mkdir`, `rm`, `cp`, `pwd`, `cd`, `tree`, `find`, `wc`, `head`, `tail`, `stat`, `df`, `mkfs.vfat`, `grep`, `hexdump`, `dcache` |
| **Networking & Routing** | `ifconfig`, `ping`, `traceroute`, `arp`, `dhclient`, `nslookup`, `netstat`, `iptables`, `rfilter`, `httpd`, `sshd`, `curl`, `wget`, `ssh` |
| **Hardware & Storage** | `lspci`, `lsdev`, `lsblk`, `fdisk`, `hdparm`, `sensors`, `speaker`, `mouse`, `alsamixer`, `tts`, `virtinfo` |
| **Networking & Routing** | `ifconfig`, `ping`, `traceroute`, `arp`, `dhclient`, `nslookup`, `dnscache`, `netstat`, `iptables`, `rfilter`, `httpd`, `sshd`, `curl`, `wget`, `ssh` |
| **Hardware & Storage** | `lspci`, `lsdev`, `lsblk`, `fdisk`, `hdparm`, `sensors`, `speaker`, `beep`, `mouse`, `alsamixer`, `tts`, `virtinfo` |
| **Security & Cryptography** | `su`, `passwd`, `useradd`, `certcheck`, `capsh`, `ipcs`, `chacha20`, `sha3sum`, `base64`, `cryptobench` |
| **SUB Language & System** | `subinfo`, `subpower`, `subbench`, `subquote`, `pstree`, `kill`, `systemctl`, `service`, `crontab`, `logger`, `logread`, `watchdog`, `rustinfo`, `cppinfo`, `cpptest` |
| **SUB Language & System** | `subinfo`, `subpower`, `subbench`, `subquote`, `pstree`, `kill`, `shm`, `systemctl`, `service`, `crontab`, `logger`, `logread`, `watchdog`, `rustinfo`, `cppinfo`, `cpptest` |
| **Entertainment & Games** | `snake`, `matrix`, `calc` |
| **Kernel & Tracing** | `lsmod`, `insmod`, `rmmod`, `slabinfo`, `trace`, `unshare`, `io_uring_test` |
| **Diagnostics & Metrics**| `neofetch`, `uname`, `free`, `uptime`, `top`, `htop`, `ps`, `dmesg`, `vmstat`, `iostat` |
+27
View File
@@ -0,0 +1,27 @@
#ifndef _IPC_SHM_POSIX_H
#define _IPC_SHM_POSIX_H
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
#define POSIX_SHM_MAX_NAME 64
#define POSIX_SHM_MAX_OBJECTS 32
typedef struct {
char name[POSIX_SHM_MAX_NAME];
void* memory;
size_t size;
uint32_t mode;
uint32_t ref_count;
bool active;
} posix_shm_object_t;
void posix_shm_init(void);
int posix_shm_open(const char* name, int flags, uint32_t mode);
int posix_shm_truncate(int fd, size_t size);
int posix_shm_unlink(const char* name);
posix_shm_object_t* posix_shm_get(int fd);
void posix_shm_dump(void);
#endif // _IPC_SHM_POSIX_H
+25
View File
@@ -0,0 +1,25 @@
#ifndef _NET_DNS_CACHE_H
#define _NET_DNS_CACHE_H
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
#define DNS_CACHE_MAX_ENTRIES 32
#define DNS_CACHE_MAX_HOST 64
typedef struct {
char hostname[DNS_CACHE_MAX_HOST];
uint32_t ip;
uint32_t ttl_remaining;
uint64_t hit_count;
bool active;
} dns_cache_entry_t;
void dns_cache_init(void);
bool dns_cache_lookup(const char* hostname, uint32_t* out_ip);
void dns_cache_insert(const char* hostname, uint32_t ip, uint32_t ttl);
void dns_cache_flush(void);
void dns_cache_dump(void);
#endif // _NET_DNS_CACHE_H
+11
View File
@@ -0,0 +1,11 @@
#ifndef _SOUND_BEEP_H
#define _SOUND_BEEP_H
#include <stdint.h>
#include <stddef.h>
void sound_play_tone(uint32_t freq_hz, uint32_t duration_ms);
void sound_play_jingle(const char* jingle_name);
int applet_beep(int argc, char** argv);
#endif // _SOUND_BEEP_H
+102
View File
@@ -0,0 +1,102 @@
// POSIX Shared Memory Subsystem (/dev/shm) for SUB-OS
// Implements shm_open, ftruncate, and shm_unlink functionality
#include <ipc/shm_posix.h>
#include <mm/kmalloc.h>
#include <lib/string.h>
#include <kernel/printk.h>
static posix_shm_object_t shm_table[POSIX_SHM_MAX_OBJECTS];
void posix_shm_init(void) {
memset(shm_table, 0, sizeof(shm_table));
// Create standard system shared memory segments
posix_shm_open("/subos_system_metrics", 0x40 | 0x02, 0666);
posix_shm_truncate(0, 4096);
posix_shm_open("/wayland_display_shm", 0x40 | 0x02, 0666);
posix_shm_truncate(1, 65536);
printk(KERN_INFO "SHM: POSIX /dev/shm Shared Memory subsystem initialized\n");
}
int posix_shm_open(const char* name, int flags, uint32_t mode) {
if (!name || name[0] != '/') return -1;
// Check if exists
for (int i = 0; i < POSIX_SHM_MAX_OBJECTS; i++) {
if (shm_table[i].active && strcmp(shm_table[i].name, name) == 0) {
shm_table[i].ref_count++;
return i;
}
}
// Create new
for (int i = 0; i < POSIX_SHM_MAX_OBJECTS; i++) {
if (!shm_table[i].active) {
strncpy(shm_table[i].name, name, POSIX_SHM_MAX_NAME - 1);
shm_table[i].name[POSIX_SHM_MAX_NAME - 1] = '\0';
shm_table[i].memory = NULL;
shm_table[i].size = 0;
shm_table[i].mode = mode ? mode : 0644;
shm_table[i].ref_count = 1;
shm_table[i].active = true;
return i;
}
}
return -1;
}
int posix_shm_truncate(int fd, size_t size) {
if (fd < 0 || fd >= POSIX_SHM_MAX_OBJECTS || !shm_table[fd].active) return -1;
if (shm_table[fd].memory) {
kfree(shm_table[fd].memory);
}
shm_table[fd].memory = kzalloc(size);
if (!shm_table[fd].memory && size > 0) {
shm_table[fd].size = 0;
return -1;
}
shm_table[fd].size = size;
return 0;
}
int posix_shm_unlink(const char* name) {
if (!name) return -1;
for (int i = 0; i < POSIX_SHM_MAX_OBJECTS; i++) {
if (shm_table[i].active && strcmp(shm_table[i].name, name) == 0) {
if (shm_table[i].memory) {
kfree(shm_table[i].memory);
}
memset(&shm_table[i], 0, sizeof(posix_shm_object_t));
return 0;
}
}
return -1;
}
posix_shm_object_t* posix_shm_get(int fd) {
if (fd < 0 || fd >= POSIX_SHM_MAX_OBJECTS || !shm_table[fd].active) return NULL;
return &shm_table[fd];
}
void posix_shm_dump(void) {
printk(ANSI_BRIGHT_CYAN "=== POSIX /dev/shm Shared Memory Registry ===\n" ANSI_RESET);
printk(ANSI_BOLD "%-4s %-30s %10s %6s %8s\n" ANSI_RESET, "FD", "NAME", "SIZE (B)", "PERMS", "NREF");
printk("-----------------------------------------------------------------\n");
int active_count = 0;
for (int i = 0; i < POSIX_SHM_MAX_OBJECTS; i++) {
if (shm_table[i].active) {
active_count++;
printk("%-4d " ANSI_BRIGHT_YELLOW "%-30s" ANSI_RESET " %10llu 0%03o %8u\n",
i, shm_table[i].name, (uint64_t)shm_table[i].size,
shm_table[i].mode, shm_table[i].ref_count);
}
}
if (active_count == 0) {
printk(" (No active /dev/shm objects)\n");
}
printk("\n");
}
+1 -1
View File
@@ -16,7 +16,7 @@ extern "C" {
extern ctor_fn_t __init_array_end[];
void cpp_call_global_constructors(void) {
if (__init_array_end <= __init_array_start) return;
if (+__init_array_end <= +__init_array_start) return;
size_t count = __init_array_end - __init_array_start;
for (size_t i = 0; i < count; i++) {
if (__init_array_start[i]) {
+7 -1
View File
@@ -58,6 +58,9 @@
#include <net/tcp.h>
#include <net/dhcp.h>
#include <net/dns.h>
#include <net/dns_cache.h>
#include <ipc/shm_posix.h>
#include <sound/beep.h>
#include <kernel/rust.h>
#include <kernel/sub_lang.h>
#include <kernel/cpp_kernel.h>
@@ -125,12 +128,14 @@ static void subos_modular_core_boot(void) {
tcp_init();
dhcp_init();
dns_init();
dns_cache_init();
printk(ANSI_BRIGHT_GREEN "Online\n" ANSI_RESET);
} else {
printk(ANSI_YELLOW "No NIC detected\n" ANSI_RESET);
}
#else
virtio_net_init();
dns_cache_init();
printk(ANSI_BRIGHT_GREEN "Online\n" ANSI_RESET);
#endif
@@ -156,8 +161,9 @@ static void subos_modular_core_boot(void) {
printk(ANSI_BRIGHT_GREEN "OK\n" ANSI_RESET);
// Inter-Process Communication
printk(KERN_INFO "[9/14] Initializing IPC Engine (Pipes, MsgQueues, SHM, Semaphores)... ");
printk(KERN_INFO "[9/14] Initializing IPC Engine (Pipes, MsgQueues, POSIX SHM, Semaphores)... ");
ipc_init();
posix_shm_init();
printk(ANSI_BRIGHT_GREEN "OK\n" ANSI_RESET);
// Asynchronous I/O Engine
+5
View File
@@ -145,6 +145,11 @@ int vsnprintf(char* buf, size_t size, const char* fmt, va_list args) {
out_idx += format_number(buf + out_idx, size - out_idx, val, 10, false, false, width, pad, left_align);
break;
}
case 'o': {
uint64_t val = is_long_long ? va_arg(args, uint64_t) : (is_long ? va_arg(args, unsigned long) : va_arg(args, unsigned int));
out_idx += format_number(buf + out_idx, size - out_idx, val, 8, false, false, width, pad, left_align);
break;
}
case 'x': {
uint64_t val = is_long_long ? va_arg(args, uint64_t) : (is_long ? va_arg(args, unsigned long) : va_arg(args, unsigned int));
out_idx += format_number(buf + out_idx, size - out_idx, val, 16, false, false, width, pad, left_align);
+93
View File
@@ -0,0 +1,93 @@
// DNS Resolver Cache with TTL Management and Metrics for SUB-OS
#include <net/dns_cache.h>
#include <lib/string.h>
#include <lib/printf.h>
#include <kernel/printk.h>
static dns_cache_entry_t dns_table[DNS_CACHE_MAX_ENTRIES];
static uint64_t total_lookups = 0;
static uint64_t total_hits = 0;
void dns_cache_init(void) {
memset(dns_table, 0, sizeof(dns_table));
// Seed common local and well-known hosts
dns_cache_insert("localhost", 0x7F000001, 86400); // 127.0.0.1
dns_cache_insert("gateway.local", 0x0A000202, 3600); // 10.0.2.2
dns_cache_insert("dns.google", 0x08080808, 7200); // 8.8.8.8
dns_cache_insert("one.one.one.one", 0x01010101, 7200); // 1.1.1.1
dns_cache_insert("github.com", 0x8C527964, 300); // 140.82.121.4
printk(KERN_INFO "DNS: High-performance DNS Cache table initialized (Capacity: %d)\n", DNS_CACHE_MAX_ENTRIES);
}
bool dns_cache_lookup(const char* hostname, uint32_t* out_ip) {
if (!hostname || !out_ip) return false;
total_lookups++;
for (int i = 0; i < DNS_CACHE_MAX_ENTRIES; i++) {
if (dns_table[i].active && strcmp(dns_table[i].hostname, hostname) == 0) {
dns_table[i].hit_count++;
total_hits++;
*out_ip = dns_table[i].ip;
return true;
}
}
return false;
}
void dns_cache_insert(const char* hostname, uint32_t ip, uint32_t ttl) {
if (!hostname) return;
// Update existing entry if present
for (int i = 0; i < DNS_CACHE_MAX_ENTRIES; i++) {
if (dns_table[i].active && strcmp(dns_table[i].hostname, hostname) == 0) {
dns_table[i].ip = ip;
dns_table[i].ttl_remaining = ttl;
return;
}
}
// Insert in first free slot
for (int i = 0; i < DNS_CACHE_MAX_ENTRIES; i++) {
if (!dns_table[i].active) {
strncpy(dns_table[i].hostname, hostname, DNS_CACHE_MAX_HOST - 1);
dns_table[i].hostname[DNS_CACHE_MAX_HOST - 1] = '\0';
dns_table[i].ip = ip;
dns_table[i].ttl_remaining = ttl ? ttl : 300;
dns_table[i].hit_count = 0;
dns_table[i].active = true;
return;
}
}
}
void dns_cache_flush(void) {
memset(dns_table, 0, sizeof(dns_table));
dns_cache_insert("localhost", 0x7F000001, 86400);
printk(ANSI_YELLOW "DNS: Resolver cache flushed.\n" ANSI_RESET);
}
void dns_cache_dump(void) {
printk(ANSI_BRIGHT_CYAN "=== SUB-OS In-Kernel DNS Cache & Metrics ===\n" ANSI_RESET);
uint32_t hit_ratio = (total_lookups > 0) ? (uint32_t)((total_hits * 100) / total_lookups) : 0;
printk(" Total Lookups: " ANSI_YELLOW "%llu" ANSI_RESET " Hits: " ANSI_BRIGHT_GREEN "%llu" ANSI_RESET " Hit Ratio: " ANSI_BRIGHT_GREEN "%u%%\n" ANSI_RESET,
total_lookups, total_hits, hit_ratio);
printk("-----------------------------------------------------------------\n");
printk(ANSI_BOLD "%-28s %-16s %8s %8s\n" ANSI_RESET, "HOSTNAME", "IP ADDRESS", "TTL (s)", "HITS");
printk("-----------------------------------------------------------------\n");
for (int i = 0; i < DNS_CACHE_MAX_ENTRIES; i++) {
if (dns_table[i].active) {
uint32_t ip = dns_table[i].ip;
char ip_str[20];
snprintf(ip_str, sizeof(ip_str), "%u.%u.%u.%u",
(ip >> 24) & 0xFF, (ip >> 16) & 0xFF,
(ip >> 8) & 0xFF, ip & 0xFF);
printk("%-28s " ANSI_BRIGHT_YELLOW "%-16s" ANSI_RESET " %8u %8llu\n",
dns_table[i].hostname, ip_str,
dns_table[i].ttl_remaining, dns_table[i].hit_count);
}
}
printk("\n");
}
+60
View File
@@ -0,0 +1,60 @@
// Audio Beep & Tone Synthesizer for SUB-OS
#include <sound/beep.h>
#include <drivers/speaker.h>
#include <arch/x86_64/pit.h>
#include <lib/string.h>
#include <lib/printf.h>
#include <kernel/printk.h>
void sound_play_tone(uint32_t freq_hz, uint32_t duration_ms) {
if (freq_hz == 0 || duration_ms == 0) return;
#if defined(__x86_64__)
speaker_beep(freq_hz, duration_ms);
#else
printk(ANSI_BRIGHT_MAGENTA "♪ [Tone %u Hz for %u ms]\n" ANSI_RESET, freq_hz, duration_ms);
#endif
}
void sound_play_jingle(const char* jingle_name) {
if (!jingle_name) return;
if (strcmp(jingle_name, "startup") == 0) {
sound_play_tone(523, 60); // C5
sound_play_tone(659, 60); // E5
sound_play_tone(784, 60); // G5
sound_play_tone(1046, 120);// C6
} else if (strcmp(jingle_name, "coin") == 0) {
sound_play_tone(988, 40); // B5
sound_play_tone(1318, 100);// E6
} else if (strcmp(jingle_name, "alert") == 0) {
sound_play_tone(880, 80);
sound_play_tone(440, 80);
} else {
sound_play_tone(440, 100);
}
}
int applet_beep(int argc, char** argv) {
uint32_t freq = 440;
uint32_t dur = 100;
if (argc >= 2) {
if (strcmp(argv[1], "startup") == 0 || strcmp(argv[1], "coin") == 0 || strcmp(argv[1], "alert") == 0) {
printk(ANSI_BRIGHT_CYAN "Playing jingle '%s'...\n" ANSI_RESET, argv[1]);
sound_play_jingle(argv[1]);
return 0;
}
freq = (uint32_t)strtol(argv[1], NULL, 10);
}
if (argc >= 3) {
dur = (uint32_t)strtol(argv[2], NULL, 10);
}
if (freq < 20) freq = 20;
if (freq > 20000) freq = 20000;
if (dur > 5000) dur = 5000;
printk(ANSI_BRIGHT_CYAN "Beeping at %u Hz for %u ms...\n" ANSI_RESET, freq, dur);
sound_play_tone(freq, dur);
return 0;
}
+21
View File
@@ -32,6 +32,9 @@
#include <kernel/sub_lang.h>
#include <kernel/cpp_kernel.h>
#include <userland/snake.h>
#include <ipc/shm_posix.h>
#include <net/dns_cache.h>
#include <sound/beep.h>
#include <arch/arch.h>
#include <kernel/printk.h>
#include <lib/string.h>
@@ -963,6 +966,21 @@ static int applet_cpptest(int argc, char** argv) {
return cpp_test_oop_subsystem();
}
static int applet_shm(int argc, char** argv) {
(void)argc; (void)argv;
posix_shm_dump();
return 0;
}
static int applet_dnscache(int argc, char** argv) {
if (argc >= 2 && strcmp(argv[1], "-f") == 0) {
dns_cache_flush();
return 0;
}
dns_cache_dump();
return 0;
}
static int applet_whoami(int argc, char** argv) {
(void)argc; (void)argv;
const user_account_t* u = auth_get_current_user();
@@ -2024,6 +2042,9 @@ static const lazybox_applet_t applets[] = {
{"snake", applet_snake, "snake [--demo]", "Interactive ANSI Snake Game", "Games"},
{"cppinfo", applet_cppinfo, "cppinfo", "C++ OOP kernel telemetry", "System"},
{"cpptest", applet_cpptest, "cpptest", "C++ polymorphism & new/del", "System"},
{"shm", applet_shm, "shm", "POSIX /dev/shm shared memory", "System"},
{"dnscache", applet_dnscache, "dnscache [-f]", "DNS resolver cache & metrics", "Network"},
{"beep", applet_beep, "beep [freq/jingle] [ms]", "PC speaker & HDA tone synth", "Sound"},
{NULL, NULL, NULL, NULL, NULL}
};
+137 -48
View File
@@ -1,8 +1,10 @@
// Interactive Classic Snake Game for SUB-OS
// Features ANSI graphics, real-time keyboard control, dynamic scoring & demo mode
// Ultra-Smooth, Responsive Classic Snake Game for SUB-OS
// Features Flicker-Free Double Buffering, Multi-Input (PS/2 & Serial), Sound Effects & Speed Control
#include <userland/snake.h>
#include <drivers/keyboard.h>
#include <drivers/serial.h>
#include <drivers/speaker.h>
#include <drivers/tty.h>
#include <arch/x86_64/pit.h>
#include <crypto/crypto.h>
@@ -10,8 +12,8 @@
#include <lib/printf.h>
#include <kernel/printk.h>
#define BOARD_WIDTH 32
#define BOARD_HEIGHT 16
#define BOARD_WIDTH 32
#define BOARD_HEIGHT 16
#define MAX_SNAKE_LEN 256
typedef enum {
@@ -31,7 +33,10 @@ static int snake_len = 4;
static direction_t snake_dir = DIR_RIGHT;
static point_t food;
static int score = 0;
static int high_score = 0;
static bool game_over = false;
static bool game_paused = false;
static int speed_ms = 75; // Default tick interval
static void spawn_food(void) {
bool valid = false;
@@ -53,6 +58,8 @@ static void snake_init(void) {
snake_dir = DIR_RIGHT;
score = 0;
game_over = false;
game_paused = false;
speed_ms = 75;
int start_x = BOARD_WIDTH / 4;
int start_y = BOARD_HEIGHT / 2;
@@ -64,16 +71,24 @@ static void snake_init(void) {
spawn_food();
}
// Single-buffer atomic render eliminating all terminal flicker
static void snake_render(void) {
tty_set_cursor(0, 0);
char frame[4096];
int offset = 0;
// Reset cursor to top-left and hide cursor
offset += snprintf(frame + offset, sizeof(frame) - offset, "\033[H\033[?25l");
// Title & Score Header
printk(ANSI_BRIGHT_CYAN "╔══════════════════════════════════════════════╗\n" ANSI_RESET);
printk(ANSI_BRIGHT_CYAN "║" ANSI_BRIGHT_YELLOW " 🐍 SUB-OS Classic Snake Game (ANSI) " ANSI_BRIGHT_CYAN "║\n" ANSI_RESET);
printk(ANSI_BRIGHT_CYAN "║" ANSI_WHITE " Score: " ANSI_BRIGHT_GREEN "%-4d" ANSI_WHITE " Length: " ANSI_BRIGHT_GREEN "%-3d" ANSI_WHITE " Controls: WASD/Q " ANSI_BRIGHT_CYAN "║\n" ANSI_RESET, score, snake_len);
printk(ANSI_BRIGHT_CYAN "╠══════════════════════════════════════════════╣\n" ANSI_RESET);
offset += snprintf(frame + offset, sizeof(frame) - offset,
ANSI_BRIGHT_CYAN "╔══════════════════════════════════════════════╗\n" ANSI_RESET
ANSI_BRIGHT_CYAN "║" ANSI_BRIGHT_YELLOW " 🐍 SUB-OS Classic Snake Game (ANSI) " ANSI_BRIGHT_CYAN "║\n" ANSI_RESET
ANSI_BRIGHT_CYAN "║" ANSI_WHITE " Score: " ANSI_BRIGHT_GREEN "%-4d" ANSI_WHITE " High: " ANSI_BRIGHT_YELLOW "%-4d" ANSI_WHITE " Speed: " ANSI_BRIGHT_CYAN "%-3dms " ANSI_WHITE "Len: " ANSI_BRIGHT_MAGENTA "%-3d" ANSI_BRIGHT_CYAN "║\n" ANSI_RESET
ANSI_BRIGHT_CYAN "║" ANSI_BRIGHT_BLACK " Controls: WASD/Arrows, P:Pause, +/-:Speed, Q:Quit " ANSI_BRIGHT_CYAN "║\n" ANSI_RESET
ANSI_BRIGHT_CYAN "╠══════════════════════════════════════════════╣\n" ANSI_RESET,
score, high_score, speed_ms, snake_len);
// Render Board Grid
// Build Board Matrix
char board[BOARD_HEIGHT][BOARD_WIDTH + 1];
for (int y = 0; y < BOARD_HEIGHT; y++) {
for (int x = 0; x < BOARD_WIDTH; x++) {
@@ -103,30 +118,41 @@ static void snake_render(void) {
board[snake[0].y][snake[0].x] = 'O';
}
// Draw Grid with Colors
// Render Grid
for (int y = 0; y < BOARD_HEIGHT; y++) {
printk(ANSI_BRIGHT_CYAN "║ " ANSI_RESET);
offset += snprintf(frame + offset, sizeof(frame) - offset, ANSI_BRIGHT_CYAN "║ " ANSI_RESET);
for (int x = 0; x < BOARD_WIDTH; x++) {
char c = board[y][x];
if (c == '#') {
printk(ANSI_BRIGHT_BLUE "█" ANSI_RESET);
offset += snprintf(frame + offset, sizeof(frame) - offset, ANSI_BRIGHT_BLUE "█" ANSI_RESET);
} else if (c == 'O') {
printk(ANSI_BRIGHT_GREEN "O" ANSI_RESET);
offset += snprintf(frame + offset, sizeof(frame) - offset, ANSI_BRIGHT_GREEN "O" ANSI_RESET);
} else if (c == 'o') {
printk(ANSI_GREEN "o" ANSI_RESET);
offset += snprintf(frame + offset, sizeof(frame) - offset, ANSI_GREEN "o" ANSI_RESET);
} else if (c == '*') {
printk(ANSI_BRIGHT_RED "★" ANSI_RESET);
offset += snprintf(frame + offset, sizeof(frame) - offset, ANSI_BRIGHT_RED "★" ANSI_RESET);
} else {
printk(" ");
offset += snprintf(frame + offset, sizeof(frame) - offset, " ");
}
}
printk(ANSI_BRIGHT_CYAN " ║\n" ANSI_RESET);
offset += snprintf(frame + offset, sizeof(frame) - offset, ANSI_BRIGHT_CYAN " ║\n" ANSI_RESET);
}
printk(ANSI_BRIGHT_CYAN "╚══════════════════════════════════════════════╝\n" ANSI_RESET);
// Footer
if (game_paused) {
offset += snprintf(frame + offset, sizeof(frame) - offset,
ANSI_BRIGHT_CYAN "║" ANSI_BRIGHT_YELLOW " *** GAME PAUSED *** " ANSI_BRIGHT_CYAN "║\n" ANSI_RESET);
} else {
offset += snprintf(frame + offset, sizeof(frame) - offset,
ANSI_BRIGHT_CYAN "╚══════════════════════════════════════════════╝\n" ANSI_RESET);
}
printk("%s", frame);
}
static void snake_step(void) {
if (game_paused) return;
point_t next_head = snake[0];
switch (snake_dir) {
@@ -140,6 +166,9 @@ static void snake_step(void) {
if (next_head.x <= 0 || next_head.x >= BOARD_WIDTH - 1 ||
next_head.y <= 0 || next_head.y >= BOARD_HEIGHT - 1) {
game_over = true;
#if defined(__x86_64__)
speaker_beep(220, 150);
#endif
return;
}
@@ -147,18 +176,29 @@ static void snake_step(void) {
for (int i = 0; i < snake_len; i++) {
if (snake[i].x == next_head.x && snake[i].y == next_head.y) {
game_over = true;
#if defined(__x86_64__)
speaker_beep(220, 150);
#endif
return;
}
}
// Food check
// Food collision
bool ate_food = (next_head.x == food.x && next_head.y == food.y);
if (ate_food) {
score += 10;
if (score > high_score) high_score = score;
if (snake_len < MAX_SNAKE_LEN - 1) {
snake_len++;
}
#if defined(__x86_64__)
speaker_beep(1200, 30);
#endif
// Auto speed up slightly as score rises
if (speed_ms > 40 && (score % 30 == 0)) {
speed_ms -= 5;
}
spawn_food();
}
@@ -169,6 +209,39 @@ static void snake_step(void) {
snake[0] = next_head;
}
// Unified non-blocking input reader from both PS/2 Keyboard and Serial COM1
static int poll_input_char(void) {
if (keyboard_has_key()) {
uint16_t k = keyboard_get_key();
if (k == KEY_UP) return 'w';
if (k == KEY_DOWN) return 's';
if (k == KEY_LEFT) return 'a';
if (k == KEY_RIGHT) return 'd';
return (int)(k & 0xFF);
}
if (serial_received()) {
char c = serial_read_char();
if (c == 27) { // Possible ANSI escape sequence \033[A
#if defined(__x86_64__)
pit_sleep(2);
#endif
if (serial_received()) {
char c2 = serial_read_char();
if (c2 == '[' && serial_received()) {
char c3 = serial_read_char();
if (c3 == 'A') return 'w';
if (c3 == 'B') return 's';
if (c3 == 'C') return 'd';
if (c3 == 'D') return 'a';
}
}
return 27; // Bare ESC
}
return (int)c;
}
return -1;
}
int applet_snake(int argc, char** argv) {
bool demo_mode = false;
if (argc >= 2 && (strcmp(argv[1], "--demo") == 0 || strcmp(argv[1], "-d") == 0)) {
@@ -179,8 +252,7 @@ int applet_snake(int argc, char** argv) {
if (demo_mode) {
printk(ANSI_BRIGHT_CYAN "Running Snake Game Simulation Demo...\n" ANSI_RESET);
for (int step = 0; step < 15; step++) {
// Simple autonomous AI chasing food
for (int step = 0; step < 20; step++) {
if (food.x > snake[0].x && snake_dir != DIR_LEFT) snake_dir = DIR_RIGHT;
else if (food.x < snake[0].x && snake_dir != DIR_RIGHT) snake_dir = DIR_LEFT;
else if (food.y > snake[0].y && snake_dir != DIR_UP) snake_dir = DIR_DOWN;
@@ -194,47 +266,64 @@ int applet_snake(int argc, char** argv) {
return 0;
}
printk("\033[2J\033[H");
printk("\033[2J\033[H\033[?25l"); // Clear screen & hide cursor
snake_render();
int loop_counter = 0;
while (!game_over) {
if (keyboard_has_key()) {
uint16_t key = keyboard_get_key();
char c = (char)(key & 0xFF);
// High-frequency sub-tick input polling for ultra-responsive controls
int sub_ticks = speed_ms / 10;
if (sub_ticks < 1) sub_ticks = 1;
if (c == 'q' || c == 'Q' || c == 27) { // Q or ESC
break;
} else if ((c == 'w' || c == 'W' || key == KEY_UP) && snake_dir != DIR_DOWN) {
snake_dir = DIR_UP;
} else if ((c == 's' || c == 'S' || key == KEY_DOWN) && snake_dir != DIR_UP) {
snake_dir = DIR_DOWN;
} else if ((c == 'a' || c == 'A' || key == KEY_LEFT) && snake_dir != DIR_RIGHT) {
snake_dir = DIR_LEFT;
} else if ((c == 'd' || c == 'D' || key == KEY_RIGHT) && snake_dir != DIR_LEFT) {
snake_dir = DIR_RIGHT;
for (int st = 0; st < sub_ticks; st++) {
int key = poll_input_char();
if (key != -1) {
if (key == 'q' || key == 'Q' || key == 27 || key == 3) {
game_over = true;
break;
} else if (key == 'p' || key == 'P' || key == ' ') {
game_paused = !game_paused;
snake_render();
} else if (key == '+' || key == '=') {
if (speed_ms > 25) speed_ms -= 10;
snake_render();
} else if (key == '-' || key == '_') {
if (speed_ms < 200) speed_ms += 10;
snake_render();
} else if ((key == 'w' || key == 'W' || key == 'k') && snake_dir != DIR_DOWN) {
snake_dir = DIR_UP;
} else if ((key == 's' || key == 'S' || key == 'j') && snake_dir != DIR_UP) {
snake_dir = DIR_DOWN;
} else if ((key == 'a' || key == 'A' || key == 'h') && snake_dir != DIR_RIGHT) {
snake_dir = DIR_LEFT;
} else if ((key == 'd' || key == 'D' || key == 'l') && snake_dir != DIR_LEFT) {
snake_dir = DIR_RIGHT;
}
}
#if defined(__x86_64__)
pit_sleep(10);
#else
for (volatile int d = 0; d < 30000; d++);
#endif
}
if (game_over) break;
snake_step();
snake_render();
#if defined(__x86_64__)
pit_sleep(70);
#else
for (volatile int d = 0; d < 200000; d++);
#endif
loop_counter++;
if (loop_counter > 500) break; // Avoid infinite loop in unattended tests
if (loop_counter > 2000) break; // Safety timeout for automated headless environments
}
tty_set_cursor(BOARD_HEIGHT + 5, 0);
if (game_over) {
printk(ANSI_BRIGHT_RED "\n💥 GAME OVER! Final Score: %d (Length: %d)\n" ANSI_RESET, score, snake_len);
} else {
printk(ANSI_YELLOW "\nGame exited. Final Score: %d\n" ANSI_RESET, score);
// Restore cursor
printk("\033[?25h");
tty_set_cursor(BOARD_HEIGHT + 6, 0);
if (score >= high_score && score > 0) {
printk(ANSI_BRIGHT_YELLOW "\n🏆 NEW HIGH SCORE: %d! 🎉\n" ANSI_RESET, score);
}
printk(ANSI_BRIGHT_RED "\n💥 GAME OVER! Final Score: %d (Snake Length: %d)\n" ANSI_RESET, score, snake_len);
return 0;
}