working conway

multi-mode
Ondřej Hruška 9 years ago
parent d4e386daa9
commit 7fe3863f1b
  1. 21
      project/dotmatrix.c
  2. 3
      project/dotmatrix.h
  3. 2
      project/hw_init.c
  4. 13
      project/main.c
  5. 2
      project/mode_audio.c
  6. 456
      project/mode_life.c
  7. 5
      project/scrolltext.c
  8. 15
      project/utils/timebase.c
  9. 3
      project/utils/timebase.h

@ -87,7 +87,7 @@ bool dmtx_get(DotMatrix_Cfg* disp, int32_t x, int32_t y)
uint8_t *cell = cell_ptr(disp, x, y, &xd);
if (cell == NULL) return 0;
return (bool)(*cell & (1 << xd));
return (*cell >> xd) & 1;
}
@ -108,8 +108,23 @@ void dmtx_set(DotMatrix_Cfg* disp, int32_t x, int32_t y, bool bit)
if (cell == NULL) return;
if (bit) {
*cell |= bit << xd;
*cell |= 1 << xd;
} else {
*cell &= ~(bit << xd);
*cell &= ~(1 << xd);
}
}
void dmtx_set_block(DotMatrix_Cfg* disp, int32_t startX, int32_t startY, uint32_t *data_rows, uint32_t width, uint16_t height)
{
for (uint32_t y = 0; y < height; y++) {
uint32_t row = data_rows[y];
for (uint32_t x = 0; x < width; x++) {
int xx = startX + (int)x;
int yy = startY + (int)y;
bool val = (row >> (width - x - 1)) & 1;
dmtx_set(disp, xx, yy, val);
}
}
}

@ -50,6 +50,9 @@ void dmtx_set(DotMatrix_Cfg* disp, int32_t x, int32_t y, bool bit);
/** Get a single bit */
bool dmtx_get(DotMatrix_Cfg* disp, int32_t x, int32_t y);
/** Set a block using array of row data */
void dmtx_set_block(DotMatrix_Cfg* disp, int32_t startX, int32_t startY, uint32_t *data_rows, uint32_t width, uint16_t height);
/** Toggle a single bit */
void dmtx_toggle(DotMatrix_Cfg* disp, int32_t x, int32_t y);

@ -61,7 +61,7 @@ static void conf_irq_prios(void)
static void conf_subsystems(void)
{
// task scheduler subsystem
timebase_init(15, 15);
timebase_init(20, 20);
// event and task queues
queues_init(30, 30);

@ -48,17 +48,21 @@ static void switch_mode(void *unused)
}
activate_mode();
// discard buffer
uint8_t x;
while(com_rx(gamepad_iface, &x));
}
#define SCROLL_STEP 20
#define SCROLL_STEP 22
static void activate_mode(void)
{
// --- Audio FFT mode ---
if (app_mode == MODE_AUDIO) {
info("MODE: Audio");
scrolltext("Audio FFT", SCROLL_STEP);
scrolltext("AUDIO", SCROLL_STEP);
mode_audio_start();
} else {
@ -69,7 +73,7 @@ static void activate_mode(void)
if (app_mode == MODE_LIFE) {
info("MODE: Life");
scrolltext("Game of Life", SCROLL_STEP);
scrolltext("CONWAY", SCROLL_STEP);
mode_life_start();
} else {
@ -80,7 +84,7 @@ static void activate_mode(void)
if (app_mode == MODE_SNAKE) {
info("MODE: Snake");
scrolltext("Snake", SCROLL_STEP);
scrolltext("SNAKE", SCROLL_STEP);
mode_snake_start();
} else {
@ -146,6 +150,7 @@ static void gamepad_rx(ComIface *iface)
case 'I': // Select pressed
tq_post(switch_mode, NULL);
break;
case 'i': // Select released
break;

@ -163,7 +163,7 @@ static void audio_capture_done(void* unused)
// normalize
dmtx_clear(dmtx);
float factor = (1.0f/bin_count)*0.2f;
float factor = (1.0f/bin_count)*0.3f;
for(int i = 0; i < bin_count-1; i+=2) {
bins[i] *= factor;
bins[i+1] *= factor;

@ -1,21 +1,469 @@
#include "mode_life.h"
#include "utils/timebase.h"
#include "com/debug.h"
#include "dotmatrix.h"
static bool life_enabled = false;
static void show_board(bool do_show);
static void apply_paint(void);
#define BOARD_W 16
#define BOARD_H 16
#define SIZEOF_BOARD (BOARD_H*sizeof(uint32_t))
// 32-bit for easy blit
static bool is_orig_board = true; // marks that currently displayed board is the orig board to reset back to.
static uint32_t board_orig[BOARD_H];
static uint32_t board_prev[BOARD_H];
static uint32_t board[BOARD_H];
static task_pid_t task_gametick; // game update tick
static task_pid_t task_movetick; // repeated move
static task_pid_t task_start_moveticks; // scheduled task to start moveticks after a delay
static task_pid_t task_movepress; // move on press (delayed to allow for diagonals)
static task_pid_t task_blink; // periodic blink task
static task_pid_t task_blink2; // scheduled 'unblink' task
static ms_time_t step_time = 200; // game step time
#define BLINKLEN 350 // blink on time (total)
#define BLINKLEN_ONE 300 // blink on time for '1'
#define BLINKLEN_ZERO 10 // blink on time for '0'
#define MOVE_TIME 100 // mouse move
#define MOVE_START_TIME 700
static int cursorX = BOARD_W / 2 - 1;
static int cursorY = BOARD_H / 2 - 1;
static bool running = false;
static bool painting_1 = false;
static bool painting_0 = false;
static bool modA_down = false;
static bool modB_down = false;
// X+,X-,Y+,Y-
typedef enum {
DIR_NONE = 0b0000,
// straight
DIR_N = 0b0010,
DIR_S = 0b0001,
DIR_E = 0b1000,
DIR_W = 0b0100,
// diagonals
DIR_NE = DIR_N | DIR_E,
DIR_NW = DIR_N | DIR_W,
DIR_SE = DIR_S | DIR_E,
DIR_SW = DIR_S | DIR_W,
} MoveDir;
static MoveDir active_dir = DIR_NONE;
// ---
static void board_set(int x, int y, bool color)
{
if (x < 0 || y < 0 || x >= BOARD_W || y >= BOARD_H) return;
uint32_t mask = 1 << (BOARD_W - x - 1);
if (color) {
board[y] |= mask;
} else {
board[y] &= ~mask;
}
}
static bool board_get(int x, int y)
{
// wrap
while (x < 0) x += BOARD_W;
while (y < 0) y += BOARD_H;
while (x >= BOARD_W) x -= BOARD_W;
while (y >= BOARD_H) y -= BOARD_H;
return (board[y] >> (BOARD_W - x - 1)) & 1;
}
static bool board_prev_get(int x, int y)
{
// wrap
while (x < 0) x += BOARD_W;
while (y < 0) y += BOARD_H;
while (x >= BOARD_W) x -= BOARD_W;
while (y >= BOARD_H) y -= BOARD_H;
return (board_prev[y] >> (BOARD_W - x - 1)) & 1;
}
static void move_cursor(void)
{
if (active_dir & DIR_N) {
cursorY++;
} else if (active_dir & DIR_S) {
cursorY--;
}
if (active_dir & DIR_E) {
cursorX++;
} else if (active_dir & DIR_W) {
cursorX--;
}
// clamp
if (cursorX >= BOARD_W) cursorX = BOARD_W - 1;
if (cursorX < 0) cursorX = 0;
if (cursorY >= BOARD_H) cursorY = BOARD_H - 1;
if (cursorY < 0) cursorY = 0;
// apply paint if active
apply_paint();
show_board(true);
dmtx_set(dmtx, cursorX, cursorY, 1);
abort_scheduled_task(task_blink2); // abort unblink task FIXME ???
}
static void apply_paint(void)
{
if (painting_0) {
board_set(cursorX, cursorY, 0);
}
if (painting_1) {
board_set(cursorX, cursorY, 1);
}
}
/** start periodic move */
static void start_moveticks_cb(void *unused)
{
(void)unused;
if (!life_enabled) return;
task_start_moveticks = 0;
enable_periodic_task(task_movetick, true);
}
/** Perform one game step */
static void gametick_cb(void *unused)
{
(void)unused;
if (!life_enabled) return;
dbg("Game tick!");
memcpy(board_prev, board, SIZEOF_BOARD);
for (int i = 0; i < BOARD_W; i++) {
for (int j = 0; j < BOARD_H; j++) {
int count = 0;
// Above
count += board_prev_get(i - 1, j - 1);
count += board_prev_get(i, j - 1);
count += board_prev_get(i + 1, j - 1);
// Sides
count += board_prev_get(i - 1, j);
count += board_prev_get(i + 1, j);
// Below
count += board_prev_get(i - 1, j + 1);
count += board_prev_get(i, j + 1);
count += board_prev_get(i + 1, j + 1);
bool at = board_prev_get(i, j);
if (at) {
// live cell
board_set(i, j, count == 2 || count == 3);
} else {
board_set(i, j, count == 3);
}
}
}
show_board(true);
}
/** Move cursor one step in active direction */
static void movetick_cb(void *unused)
{
(void)unused;
if (!life_enabled) return;
move_cursor();
}
static void blink_cb(void *arg)
{
if (!life_enabled) return; // pending leftover...
uint32_t which = (uint32_t)arg;
if (which == 0) {
// first
show_board(false);
bool at = board_get(cursorX, cursorY);
dmtx_set(dmtx, cursorX, cursorY, 1);
dmtx_show(dmtx);
// schedule unblink
schedule_task(blink_cb, (void*)1, at ? BLINKLEN_ONE : BLINKLEN_ZERO, true);
} else {
show_board(false);
dmtx_set(dmtx, cursorX, cursorY, 0);
dmtx_show(dmtx);
}
}
// ---
static void show_board(bool do_show)
{
dmtx_set_block(dmtx, 0, 0, board, 16, 16);
if (do_show) dmtx_show(dmtx);
}
// ---
void mode_life_init(void)
{
//
// prepare tasks
task_gametick = add_periodic_task(gametick_cb, NULL, step_time, true);
task_movetick = add_periodic_task(movetick_cb, NULL, MOVE_TIME, true);
task_blink = add_periodic_task(blink_cb, 0, BLINKLEN, true);
// stop all - we may not be starting this mode just yet
enable_periodic_task(task_gametick, false);
enable_periodic_task(task_movetick, false);
enable_periodic_task(task_blink, false);
}
void mode_life_start(void)
{
//
life_enabled = true;
enable_periodic_task(task_blink, true);
// if (running) {
// enable_periodic_task(task_gametick, true);
// } else {
// enable_periodic_task(task_blink, true);
// }
}
void mode_life_stop(void)
{
//
enable_periodic_task(task_gametick, false);
enable_periodic_task(task_movetick, false);
enable_periodic_task(task_blink, false);
abort_scheduled_task(task_blink2);
abort_scheduled_task(task_movepress);
abort_scheduled_task(task_start_moveticks);
painting_1 = painting_0 = false;
modA_down = modB_down = false;
running = false; // stop
life_enabled = false;
}
static void toggle_game(bool run)
{
if (run) {
running = true;
enable_periodic_task(task_movetick, false);
enable_periodic_task(task_blink, false);
abort_scheduled_task(task_movepress);
abort_scheduled_task(task_start_moveticks);
// Go!!
enable_periodic_task(task_gametick, true);
} else {
running = false;
enable_periodic_task(task_gametick, false);
enable_periodic_task(task_blink, true);
}
show_board(true);
}
/** do move (had ample time to press both arrows for diagonal) */
static void movepress_cb(void *unused)
{
(void)unused;
if (!life_enabled) return;
move_cursor();
task_movepress = 0;
task_start_moveticks = schedule_task(start_moveticks_cb, NULL, MOVE_START_TIME, true);
}
void mode_life_btn(char key)
{
//
// Common for run / stop mode
switch(key) {
case 'A': modA_down = true; break;
case 'a': modA_down = false; break;
case 'B': modB_down = true; break;
case 'b': modB_down = false; break;
case 'K':
// Reset btn
if (modA_down) {
// total reset
dbg("Reset to blank");
memset(board_orig, 0, SIZEOF_BOARD);
memset(board, 0, SIZEOF_BOARD);
toggle_game(false);
is_orig_board = true;
} else {
// reset to orig
dbg("Reset to original");
memcpy(board, board_orig, SIZEOF_BOARD);
toggle_game(false);
is_orig_board = true;
}
break;
}
if (running) {
switch (key) {
case 'X': // slower
if (step_time < 1000) {
step_time += 50;
set_periodic_task_interval(task_gametick, step_time);
}
break;
case 'Y': // faster
if (step_time > 50) {
step_time -= 50;
set_periodic_task_interval(task_gametick, step_time);
}
break;
case 'J': // stop
dbg("STOP game!");
toggle_game(false);
break;
}
} else {
bool want_move = false;
bool clear_moveticks = false;
bool want_apply_paint = false;
// Groups
switch (key) {
case 'U':
case 'D':
case 'R':
case 'L':
want_move = true;
break;
case 'u':
case 'd':
case 'r':
case 'l':
clear_moveticks = true;
break;
case 'Y': // AA
case 'X': // BB
want_apply_paint = true;
break;
}
// Individual effects
switch (key) {
// --- ARROW DOWN ---
case 'U':
active_dir &= ~DIR_S;
active_dir |= DIR_N;
break;
case 'D':
active_dir &= ~DIR_N;
active_dir |= DIR_S;
break;
case 'R':
active_dir &= ~DIR_W;
active_dir |= DIR_E;
break;
case 'L':
active_dir &= ~DIR_E;
active_dir |= DIR_W;
break;
// --- ARROW UP ---
case 'u': active_dir &= ~DIR_N; break;
case 'd': active_dir &= ~DIR_S; break;
case 'r': active_dir &= ~DIR_E; break;
case 'l': active_dir &= ~DIR_W; break;
// --- Paint BTN ---
case 'Y': painting_1 = true; break;
case 'y': painting_1 = false; break;
case 'X': painting_0 = true; break;
case 'x': painting_0 = false; break;
// --- Control ---
case 'J':
dbg("Start game!");
// starting
if (is_orig_board) {
// save
memcpy(board_orig, board, SIZEOF_BOARD);
}
is_orig_board = false;
toggle_game(true);
break;
}
if (clear_moveticks) {
enable_periodic_task(task_movetick, false);
abort_scheduled_task(task_start_moveticks);
}
if (want_apply_paint) {
apply_paint();
}
if (want_move) {
if (task_movepress == 0) {
task_movepress = schedule_task(movepress_cb, NULL, 10, true);
}
}
}
}

@ -3,6 +3,7 @@
#include "font.h"
#include "com/debug.h"
static void printtext(const char *text, int x, int y)
{
dmtx_clear(dmtx);
@ -42,7 +43,7 @@ static void printtext(const char *text, int x, int y)
totalX++;
}
totalX++; // gap
totalX+= 2; // gap
}
dmtx_show(dmtx);
}
@ -51,7 +52,7 @@ void scrolltext(const char *text, ms_time_t step)
{
(void)step;
for (int i = 0; i < (int)strlen(text)*FONT_WIDTH + 15; i++) {
for (int i = 0; i < (int)strlen(text)*(FONT_WIDTH+1) + 15; i++) {
if (i > 0) delay_ms(step);
printtext(text, 15-i, 4);

@ -189,6 +189,21 @@ bool reset_periodic_task(task_pid_t pid)
}
bool set_periodic_task_interval(task_pid_t pid, ms_time_t interval)
{
if (pid == PID_NONE) return false;
for (size_t i = 0; i < periodic_slot_count; i++) {
periodic_task_t *task = &periodic_tasks[i];
if (task->pid != pid) continue;
task->interval_ms = interval;
return true;
}
return false;
}
/** Remove a periodic task. */
bool remove_periodic_task(task_pid_t pid)
{

@ -67,6 +67,9 @@ bool is_periodic_task_enabled(task_pid_t pid);
/** Reset timer for a task */
bool reset_periodic_task(task_pid_t pid);
/** Set inteval */
bool set_periodic_task_interval(task_pid_t pid, ms_time_t interval);
// --- Future -------------------------------------------------

Loading…
Cancel
Save