#define SSD1306_128X32 #include "ch32v003fun.h" #include #include "ssd1306_i2c.h" #include "ssd1306.h" #include "data.h" #define PIN_LED PC4 #define PIN_BTN PC7 // #define JUMP_POWER 48 // #define GRAVITY 3 #define JUMP_POWER 36 #define GRAVITY 2 #define SCROLL_SPEED 1 #define FRAME_DELAY 1000 / 60 #define SCORE_DIGITS 4 #define RUN_ANIM_SPEED 4 #define PLAYER_X 16 #define Y_PRECISION 4 u32 time = 0; u16 score = 0; u16 highscore = 0; s16 player_y = 0; s16 player_vel = 0; #define MAX_OBSTACLES 16 u8 obstacle_positions[MAX_OBSTACLES]; u8 obstacle_types[MAX_OBSTACLES]; // todo u8 obstacle_count = 0; u8 is_running = 1; u8 time_to_obstacle = 0; uint32_t lfsr = 1; // PRNG state u8 rand8(); void update(); void render(); void restart(); int main() { SystemInit(); funGpioInitAll(); funPinMode(PIN_LED, GPIO_CFGLR_OUT_10Mhz_PP); funPinMode(PIN_BTN, GPIO_CFGLR_IN_PUPD); ssd1306_i2c_init(); ssd1306_init(); restart(); while (1) { ssd1306_refresh(); if (is_running) update(); render(); time += 1; if (funDigitalRead(PIN_BTN)) { if (is_running) { if (player_y == 0 && player_vel == 0) { player_vel = JUMP_POWER; } } else restart(); } if (is_running == 0) { funDigitalWrite(PIN_LED, (time >> 2) & 1); } else { funDigitalWrite(PIN_LED, FUN_LOW); } Delay_Ms(FRAME_DELAY); } } void restart() { lfsr = SysTick->CNT; is_running = 1; player_vel = 0; player_y = 0; obstacle_count = 0; time_to_obstacle = 0; if (highscore < score) highscore = score; score = 0; } void remove_obstacle() { if (obstacle_count == 0) return; for (u8 i = 1; i < obstacle_count; i++) { obstacle_positions[i - 1] = obstacle_positions[i]; obstacle_types[i - 1] = obstacle_types[i]; } obstacle_count--; } void spawn_obstacle(u8 pos) { if (obstacle_count >= MAX_OBSTACLES) return; obstacle_positions[obstacle_count] = pos; obstacle_types[obstacle_count] = rand8() % OBSTACLE_TYPES; obstacle_count++; } void update() { score += (time & 4) != 0; if (player_vel != 0 || player_y != 0) { player_vel -= GRAVITY; player_y += player_vel; if (player_y < 0) { player_y = 0; player_vel = 0; } } for (u8 i = 0; i < obstacle_count; i++) { obstacle_positions[i] -= SCROLL_SPEED; u8 type = obstacle_types[i]; u8 dist_x = gfx_abs((s16)obstacle_positions[i] - (s16)PLAYER_X); if (dist_x < obstacle_widths[type] && (player_y >> Y_PRECISION) < obstacle_heights[type]) { is_running = 0; } } if (obstacle_count > 0 && obstacle_positions[0] == 0) { remove_obstacle(); } if (time_to_obstacle < 5) { u8 count = 1 + (rand8() & rand8() & 1) + (rand8() & rand8() & rand8() & rand8() & 1); for (u8 i = 0; i < count; i++) { spawn_obstacle(128 + i * 8); } time_to_obstacle = (rand8() & 31) + 50; } time_to_obstacle -= 1; } void render_digit(u8 digit, u8 x, u8 y) { const u16 digit_font[] = { 0b111101101101111, // 0 0b010110010010111, // 1 0b111001111100111, // 2 0b111001111001111, // 3 0b101101111001001, // 4 0b111100111001111, // 5 0b111100111101111, // 6 0b111001001001001, // 7 0b111101111101111, // 8 0b111101111001111, // 9 }; u16 pixels = digit_font[digit]; ssd1306_drawPixel(x + 0, y + 0, (pixels >> 14) & 1); ssd1306_drawPixel(x + 1, y + 0, (pixels >> 13) & 1); ssd1306_drawPixel(x + 2, y + 0, (pixels >> 12) & 1); ssd1306_drawPixel(x + 0, y + 1, (pixels >> 11) & 1); ssd1306_drawPixel(x + 1, y + 1, (pixels >> 10) & 1); ssd1306_drawPixel(x + 2, y + 1, (pixels >> 9) & 1); ssd1306_drawPixel(x + 0, y + 2, (pixels >> 8) & 1); ssd1306_drawPixel(x + 1, y + 2, (pixels >> 7) & 1); ssd1306_drawPixel(x + 2, y + 2, (pixels >> 6) & 1); ssd1306_drawPixel(x + 0, y + 3, (pixels >> 5) & 1); ssd1306_drawPixel(x + 1, y + 3, (pixels >> 4) & 1); ssd1306_drawPixel(x + 2, y + 3, (pixels >> 3) & 1); ssd1306_drawPixel(x + 0, y + 4, (pixels >> 2) & 1); ssd1306_drawPixel(x + 1, y + 4, (pixels >> 1) & 1); ssd1306_drawPixel(x + 2, y + 4, (pixels >> 0) & 1); } void render_number(u32 s, u8 x, u8 y, u8 digits) { u8 painter_x = x + digits * 4; u32 val = s; for (int i = 0; i < digits; i++) { u8 digit = val % 10; render_digit(digit, painter_x, y); val /= 10; painter_x -= 4; } } void render() { ssd1306_setbuf(0); ssd1306_drawFastHLine(0, 31, 128, 1); render_number(score, 0, 0, SCORE_DIGITS); render_number(highscore, 0, 6, SCORE_DIGITS); // render_number(obstacle_count, 20, 0, 2); // render_number(abs(player_vel), 30, 0, 2); // render_number(time_to_obstacle, 20, 6, 3); for (u8 i = 0; i < obstacle_count; i++) { u8 type = obstacle_types[i]; ssd1306_drawImage(obstacle_positions[i], 31 - 8, obstacle_sprites[type], 8, 8, 0); } if (time & RUN_ANIM_SPEED && is_running) ssd1306_drawImage(PLAYER_X, 31 - (player_y >> Y_PRECISION) - 8, soweli_a, 8, 8, 0); else ssd1306_drawImage(PLAYER_X, 31 - (player_y >> Y_PRECISION) - 8, soweli_b, 8, 8, 0); } /* White Noise Generator State */ #define NOISE_BITS 8 #define NOISE_MASK ((1 << NOISE_BITS) - 1) #define NOISE_POLY_TAP0 31 #define NOISE_POLY_TAP1 21 #define NOISE_POLY_TAP2 1 #define NOISE_POLY_TAP3 0 /* * random byte generator, taken from ch32v003fun examples */ uint8_t rand8(void) { uint8_t bit; uint32_t new_data; for (bit = 0; bit < NOISE_BITS; bit++) { new_data = ((lfsr >> NOISE_POLY_TAP0) ^ (lfsr >> NOISE_POLY_TAP1) ^ (lfsr >> NOISE_POLY_TAP2) ^ (lfsr >> NOISE_POLY_TAP3)); lfsr = (lfsr << 1) | (new_data & 1); } return lfsr & NOISE_MASK; }