#include typedef unsigned char uint8_t; __sfr __at(0x70) UART_TX; __sfr __at(0x71) UART_RX; __sfr __at(0x72) UART_STATUS; #define GRID_W 20 #define GRID_H 10 #define MAX_LEN 20 #define OVF_PER_FRAME 60 #define DIR_UP 0 #define DIR_DOWN 1 #define DIR_LEFT 2 #define DIR_RIGHT 3 static uint8_t snake_x[MAX_LEN]; static uint8_t snake_y[MAX_LEN]; static uint8_t snake_len; static uint8_t direction; static uint8_t food_x; static uint8_t food_y; static uint8_t score; static volatile uint8_t ovf_count; void isr(void) __interrupt(0) { if (INTCON & 0x04) { ovf_count++; INTCON &= ~(uint8_t)0x04; } } static void uart_send(uint8_t b) { while (UART_STATUS & 0x01); UART_TX = b; } static void uart_puts(const char *s) { while (*s) uart_send((uint8_t)*s++); } static uint8_t uart_recv(void) { if (UART_STATUS & 0x02) return 0; return UART_RX; } static uint8_t uart_getc(void) { while (UART_STATUS & 0x02); return UART_RX; } static void handle_input(void) { uint8_t c = uart_recv(); if (c != 0x1B) return; if (uart_getc() != '[') return; switch (uart_getc()) { case 'A': if (direction != DIR_DOWN) direction = DIR_UP; break; case 'B': if (direction != DIR_UP) direction = DIR_DOWN; break; case 'C': if (direction != DIR_LEFT) direction = DIR_RIGHT; break; case 'D': if (direction != DIR_RIGHT) direction = DIR_LEFT; break; } } static void place_food(void) { food_x = (ovf_count * 7 + 3) % GRID_W; food_y = (ovf_count * 5 + 1) % GRID_H; } static void print_uint8(uint8_t v) { uint8_t started = 0; if (v >= 200) { uart_send('2'); v -= 200; started = 1; } else if (v >= 100) { uart_send('1'); v -= 100; started = 1; } { uint8_t t = '0'; while (v >= 10) { v -= 10; t++; } if (started || t != '0') uart_send(t); } uart_send(v + '0'); } static void render(void) { uint8_t x, y, i; uart_puts("\x1B[H"); /* top border */ uart_send('+'); for (x = 0; x < GRID_W; x++) uart_send('-'); uart_puts("+\r\n"); for (y = 0; y < GRID_H; y++) { uart_send('|'); for (x = 0; x < GRID_W; x++) { uint8_t ch = ' '; if (x == food_x && y == food_y) { ch = '*'; } else { for (i = 0; i < snake_len; i++) { if (snake_x[i] == x && snake_y[i] == y) { ch = (i == 0) ? 'O' : 'o'; break; } } } uart_send(ch); } uart_puts("|\r\n"); } /* bottom border */ uart_send('+'); for (x = 0; x < GRID_W; x++) uart_send('-'); uart_puts("+\r\n"); uart_puts("Score: "); print_uint8(score); uart_puts(" \r\n"); } /* returns 1 if alive, 0 if dead */ static uint8_t update(void) { uint8_t nx, ny, i; nx = snake_x[0]; ny = snake_y[0]; switch (direction) { case DIR_UP: if (ny == 0) return 0; ny--; break; case DIR_DOWN: if (ny == GRID_H-1) return 0; ny++; break; case DIR_LEFT: if (nx == 0) return 0; nx--; break; case DIR_RIGHT: if (nx == GRID_W-1) return 0; nx++; break; } /* self-collision (exclude tail: it moves away this step) */ for (i = 0; i < snake_len - 1; i++) { if (snake_x[i] == nx && snake_y[i] == ny) return 0; } if (nx == food_x && ny == food_y) { if (snake_len < MAX_LEN) snake_len++; score++; place_food(); } /* shift body back, add new head */ for (i = snake_len - 1; i > 0; i--) { snake_x[i] = snake_x[i-1]; snake_y[i] = snake_y[i-1]; } snake_x[0] = nx; snake_y[0] = ny; return 1; } void main(void) { uint8_t alive; OPTION_REG = 0x07; TMR0 = 0; INTCON = 0xa0; while (1) { /* init */ direction = DIR_RIGHT; snake_len = 3; score = 0; snake_x[0] = 5; snake_y[0] = 5; snake_x[1] = 4; snake_y[1] = 5; snake_x[2] = 3; snake_y[2] = 5; place_food(); uart_puts("\x1B[2J\x1B[H"); alive = 1; while (alive) { ovf_count = 0; while (ovf_count < OVF_PER_FRAME); handle_input(); alive = update(); render(); PORTA ^= 0x01; } uart_puts("\x1B[2J\x1B[H"); uart_puts("GAME OVER score: "); print_uint8(score); uart_puts("\r\npress any key\r\n"); uart_getc(); } }