This is a really neat program! I'd love to just leave this running non-stop on my calculator
Your code is pretty neat and I was able to follow it pretty easily! However, there's always potential for cleaner code and this is what I noticed:
I highly suggest using constants to replace the many 'magic numbers' I found in your code. For example, instead of typing 40 everywhere, you could just type
#define HORIZ_CHARS 40. That way your code is more readable and if you ever need to change 40 to some other number, you only have to change it in one place.
I couldn't figure out what 128 means, I assume it's the maximum number of trails you want to display at once?
A small optimization you could make is changing all your int to uint8_t or int8_t where applicable. It's slightly faster and uses less memory.
I went ahead and applied these optimizations. Although you won't notice a major speed difference, (Mateo's optimizations will speed it up more than mine) it's slightly more readable.
Code: /*
the code rain from The Matrix, now on the CE
*/
#include <tice.h>
#include <graphx.h>
#include <keypadc.h>
#define COLORS_PER_THEME 9
#define HORIZ_CHARS 40
#define VERT_CHARS 24
#define CODE_DENSITY 4
#define MAX_TRAILS 128
#define NUM_OF_CHARS 80
uint8_t RED_THEME[] = { 0, 32, 64, 96, 128, 160, 192, 224, 255 };
uint8_t GREEN_THEME[] = { 0, 1, 2, 3, 4, 5, 6, 7, 255 };
uint8_t YELLOW_THEME[] = { 0, 65, 98, 131, 164, 197, 230, 231, 255 };
uint8_t BLUE_THEME[] = { 0, 8, 10, 16, 17, 19, 26, 28, 61, 255 };
uint8_t PURPLE_THEME[] = { 0, 41, 48, 50, 114, 122, 155, 189, 255 };
uint8_t TEAL_THEME[] = { 0, 10, 11, 12, 45, 87, 119, 183, 255 };
uint8_t MAGENTA_THEME[] = { 0, 72, 137, 144, 177, 217, 251, 253, 255 };
uint8_t colour_theme[COLORS_PER_THEME];
void set_colour_theme(uint8_t theme[]) {
for (uint8_t c = 0; c < COLORS_PER_THEME; c++) {
colour_theme[c] = theme[c];
}
}
// each character is 10 pixels tall and 8 pixels wide. i think.
// this makes the screen a 40 by 24 grid of tiles
typedef struct TILE {
char character;
int8_t value;
int8_t decrement;
} TILE;
TILE tiles[HORIZ_CHARS][VERT_CHARS];
void update_tile(TILE* t) {
if (t->value < 0) {
return;
}
t->value -= t->decrement;
if (t->value < (-1 * t->decrement)) {
t->value = 0; // sigh...
}
}
void draw_tile(TILE* t, uint24_t x, uint8_t y) {
if (t->value < 0) {
return;
}
gfx_SetTextFGColor(colour_theme[t->value]);
uint24_t draw_x = 8 * x;
uint8_t draw_y = 10 * y;
char draw_string[] = { t->character, '\0' };
gfx_PrintStringXY(draw_string, draw_x, draw_y);
}
char* available_characters = "1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnoprstuvwxyz#*+=-_@~?<>&(){}[];";
char get_random_char() {
uint8_t index = rand() % NUM_OF_CHARS;
return available_characters[index];
}
typedef struct TRAIL {
uint24_t x; uint8_t y;
bool used;
int8_t decrement;
} TRAIL;
TRAIL trails[MAX_TRAILS];
void add_trail(uint8_t x) {
for (uint8_t c = 0; c < MAX_TRAILS; c++) {
if (trails[c].used) continue;
TRAIL* t = &trails[c];
t->y = 0;
t->x = x;
t->used = true;
t->decrement = rand() % 2 + 1;
return;
}
}
void update_trail(TRAIL* t) {
if (t->y >= VERT_CHARS) {
t->used = false;
return;
}
t->y++;
TILE* tile = &tiles[t->x][t->y];
tile->character = get_random_char();
tile->decrement = t->decrement;
tile->value = 8;
}
int main() {
bool running = true;
gfx_Begin();
gfx_SetTextBGColor(0);
gfx_SetTextConfig(gfx_text_noclip);
gfx_SetMonospaceFont(8);
// set up the tiles
for (uint8_t x = 0; x < HORIZ_CHARS; x++) {
for (uint8_t y = 0; y < VERT_CHARS-1; y++) {
tiles[x][y].value = -1;
}
}
set_colour_theme(GREEN_THEME);
gfx_FillScreen(0);
do {
for (uint8_t c = 0; c < MAX_TRAILS; c++) {
update_trail(&trails[c]);
}
for (uint8_t x = 0; x < HORIZ_CHARS; x++) {
for (uint8_t y = 0; y < VERT_CHARS-1; y++) {
update_tile(&tiles[x][y]);
draw_tile(&tiles[x][y], x, y);
}
}
for (uint8_t n = 0; n < CODE_DENSITY; n++) {
uint8_t x = rand() % HORIZ_CHARS;
if (tiles[x][0].value < 0) {
add_trail(x);
}
}
kb_Scan();
running = !kb_IsDown(kb_KeyClear);
// oh great... a mountain of if statements, Yanderedev style...
// a necessary evil here?
// if only there was a more elegant way to doing this...
if (kb_IsDown(kb_Key1)) {
set_colour_theme(RED_THEME);
}
if (kb_IsDown(kb_Key2)) {
set_colour_theme(GREEN_THEME);
}
if (kb_IsDown(kb_Key3)) {
set_colour_theme(YELLOW_THEME);
}
if (kb_IsDown(kb_Key4)) {
set_colour_theme(BLUE_THEME);
}
if (kb_IsDown(kb_Key5)) {
set_colour_theme(PURPLE_THEME);
}
if (kb_IsDown(kb_Key6)) {
set_colour_theme(TEAL_THEME);
}
if (kb_IsDown(kb_Key7)) {
set_colour_theme(MAGENTA_THEME);
}
} while (running);
gfx_End();
return 0;
}
Again, great work! I had a lot of fun messing with the code and this is definitely something I'm keeping around!