/******************************************************* * SAMD21 ARCADE BOARD - 6 GAMES IN 1 * * Lingdong Huang 2021 * *******************************************************/ #define N 6 const int LEDS[N] = {7, 6, 5, 4, 3, 2 }; const int BTNS[N] = {10,11,16,17,18,19}; int BTN_CURR[N] = {HIGH,HIGH,HIGH,HIGH,HIGH,HIGH}; int BTN_PREV[N] = {HIGH,HIGH,HIGH,HIGH,HIGH,HIGH}; const int DANCE_SEQ_L = 29; const bool DANCE_SEQ[DANCE_SEQ_L*N] = { 0,0,0,0,1,1, 0,0,0,1,1,0, 0,0,1,1,0,0, 0,1,1,0,0,0, 1,1,0,0,0,0, 0,1,1,0,0,0, 0,0,1,1,0,0, 0,0,0,1,1,0, 0,0,0,0,1,1, 0,0,0,1,1,0, 0,0,1,1,0,0, 0,1,0,0,1,0, 1,0,0,0,0,1, 0,1,0,0,1,0, 0,0,1,1,0,0, 0,1,1,1,1,0, 1,1,1,1,1,1, 1,0,1,0,1,0, 0,1,0,1,0,1, 1,0,1,0,1,0, 0,1,0,1,0,1, 1,0,1,0,1,0, 1,0,0,0,0,1, 0,1,0,0,1,0, 0,0,1,1,0,0, 0,1,0,0,1,0, 1,0,0,0,0,1, 1,1,0,0,1,1, 1,1,1,1,1,1 }; int mode = -1; /******************************************************* * Utils * *******************************************************/ void dance(){ for (int i = 0; i < DANCE_SEQ_L; i++){ for (int j = 0; j < N; j++){ digitalWrite(LEDS[j], DANCE_SEQ[i*N+j] ? HIGH: LOW); } delay(100); } } void flash_screen(){ for (int i = 0; i < N; i++) digitalWrite(LEDS[i],LOW); delay(60); for (int i = 0; i < N; i++) digitalWrite(LEDS[i],HIGH); delay(60); for (int i = 0; i < N; i++) digitalWrite(LEDS[i],LOW); delay(60); for (int i = 0; i < N; i++) digitalWrite(LEDS[i],HIGH); delay(60); for (int i = 0; i < N; i++) digitalWrite(LEDS[i],LOW); delay(60); for (int i = 0; i < N; i++) digitalWrite(LEDS[i],HIGH); delay(60); } void reveal_screen(int idx){ for (int i = 0; i < N; i++){ digitalWrite(LEDS[i],i == idx ? HIGH : LOW); } delay(50); for (int i = 1; i < N; i++){ int a = idx-i; int b = idx+i; if (0 <= a && a < N) digitalWrite(LEDS[a],HIGH); if (0 <= b && b < N) digitalWrite(LEDS[b],HIGH); delay(50); } } void blink_led(int i){ digitalWrite(LEDS[i],LOW); delay(60); digitalWrite(LEDS[i],HIGH); delay(60); digitalWrite(LEDS[i],LOW); delay(60); digitalWrite(LEDS[i],HIGH); delay(60); digitalWrite(LEDS[i],LOW); delay(60); digitalWrite(LEDS[i],HIGH); delay(60); } void game_over(){ flash_screen(); mode = -1; delay(500); gsel_init(); } void btn_flip(bool dir, int idx){ if (mode == -1){ gsel_input(dir,idx); }else if (mode == 0){ pong_input(dir,idx); }else if (mode == 1){ whac_input(dir,idx); }else if (mode == 2){ simo_input(dir,idx); }else if (mode == 3){ croc_input(dir,idx); }else if (mode == 4){ echo_input(dir,idx); }else if (mode == 5){ prgm_input(dir,idx); } } void btn_update(){ for (int i = 0; i < N; i++){ BTN_CURR[i] = digitalRead(BTNS[i]); if (BTN_CURR[i] == LOW && BTN_PREV[i] == HIGH){ btn_flip(true,i); } if (BTN_CURR[i] == HIGH && BTN_PREV[i] == LOW){ btn_flip(false,i); } BTN_PREV[i] = BTN_CURR[i]; } } /******************************************************* * Game Selection * *******************************************************/ void gsel_init(){ } void gsel_update(){ for (int i = 0; i < N; i++){ digitalWrite(LEDS[i],HIGH); } } void gsel_input(bool dir, int idx){ if (dir){ for (int i = 0; i < N; i++){ digitalWrite(LEDS[i],i == idx ? HIGH : LOW); } delay(100); // flash_screen(); reveal_screen(idx); delay(500); for (int i = 0; i < N; i++) digitalWrite(LEDS[i],LOW); mode = idx; if (mode == 0){ pong_init(); }else if (mode == 1){ whac_init(); }else if (mode == 2){ simo_init(); }else if (mode == 3){ croc_init(); }else if (mode == 4){ echo_init(); }else if (mode == 5){ prgm_init(); }else{ mode = -1; } } } /******************************************************* * 1D-Pong * *******************************************************/ float ball_x; float ball_v; const float ball_a = -1.05; int ball_xi; int pad_a; int pad_b; const int pad_max_hold = 50; const float ball_max_v = 1.0; void pong_init(){ ball_x = N-2; ball_v = -0.01; pad_a = 0; pad_b = 0; } void pong_update(){ ball_x += ball_v; if (ball_x < 1.5 && ball_v < 0 && pad_a){ ball_v *= ball_a; }else if (ball_x > N-2.5 && ball_v > 0 && pad_b){ ball_v *= ball_a; } if (abs(ball_v) > ball_max_v){ ball_v = copysign(ball_max_v,ball_v); } ball_xi = roundf(ball_x); if (ball_xi < 0 || ball_xi >= N){ game_over(); } for (int i = 0; i < N; i++){ if (i == ball_xi){ digitalWrite(LEDS[i],HIGH); }else{ digitalWrite(LEDS[i],LOW); } } if (ball_xi != 0) digitalWrite(LEDS[0 ],pad_a?HIGH:LOW); if (ball_xi != N-1) digitalWrite(LEDS[N-1],pad_b?HIGH:LOW); if (pad_a) pad_a ++; if (pad_b) pad_b ++; if (pad_a > pad_max_hold) pad_a = 0; if (pad_b > pad_max_hold) pad_b = 0; delay(10); } void pong_input(bool dir, int idx){ if (idx == 0){ pad_a = dir ? 1 : 0; }else if (idx == N-1){ pad_b = dir ? 1 : 0; } } /******************************************************* * Whac-an-LED * *******************************************************/ int mole_i; int timeout; int timeout_max; const int timeout_min = 10; void whac_init(){ mole_i = rand()%N; timeout_max = 200; timeout = timeout_max; } void whac_update(){ for (int i = 0; i < N; i++){ if (i == mole_i){ digitalWrite(LEDS[i],HIGH); }else{ digitalWrite(LEDS[i],LOW); } } timeout --; if (timeout == 0){ game_over(); } delay(10); } void whac_input(bool dir, int idx){ if (dir){ if (idx == mole_i){ int i; do{ i = rand()%N; }while (i == mole_i); mole_i = i; timeout_max -= 2; if (timeout_max < timeout_min) timeout_max = timeout_min; timeout = timeout_max; }else{ game_over(); } } } /******************************************************* * Simon * *******************************************************/ const int pattern_max_l = 64; int pattern[pattern_max_l] = {0}; int pattern_l; int simo_state; int simo_idx; unsigned long last_press_time; int last_press_btn; void simo_init(){ for (int i = 0; i < pattern_max_l; i++){ pattern[i]= rand()%N; } pattern_l = 1; simo_idx = 0; simo_state = 0; last_press_time = millis(); last_press_btn = -1; } void simo_update(){ if (simo_state == 0){ for (int i = 0; i < N; i++) digitalWrite(LEDS[i],LOW); for (int i = 0; i < pattern_l; i++){ delay(400); digitalWrite(LEDS[pattern[i]], HIGH); delay(500); digitalWrite(LEDS[pattern[i]], LOW); } for (int i = 0; i < N; i++) digitalWrite(LEDS[i],LOW); simo_state = 1; }else{ for (int i = 0; i < N; i++) digitalWrite(LEDS[i],BTN_CURR[i] ? LOW : HIGH); } } void simo_input(bool dir, int idx){ if (simo_state == 0){ return; } if (last_press_time - millis() < 200 && last_press_btn == idx){ return; } last_press_time = millis(); if (dir){ if (idx != pattern[simo_idx]){ blink_led(pattern[simo_idx]); game_over(); } simo_idx ++; if (simo_idx >= pattern_l){ reveal_screen(idx); delay(500); simo_idx = 0; pattern_l ++; if (pattern_l > pattern_max_l) pattern_l = pattern_max_l; simo_state = 0; } } } /******************************************************* * Croc Dentist * *******************************************************/ int tooth; int teeth[N] = {0}; void croc_init(){ tooth = rand()%N; for (int i = 0; i < N; i++){ digitalWrite(LEDS[i],HIGH); teeth[i] = 0; } } void croc_update(){ for (int i = 0; i < N; i++){ digitalWrite(LEDS[i], teeth[i] ? LOW : HIGH); } } void croc_input(int dir, int idx){ if (dir){ if (idx == tooth){ flash_screen(); game_over(); }else{ teeth[idx] = 1; } } } /******************************************************* * Echo * *******************************************************/ typedef struct _b_event_t{ char idx; bool dir; unsigned int t; } b_event_t; const int max_events = 1024; int n_events; b_event_t events[max_events] = {0}; int echo_mode; unsigned int echo_start_t; int playhead; int first_press_time; void echo_init(){ n_events = 0; echo_mode = 0; playhead = 0; last_press_time = millis(); first_press_time = -1; for (int i = 0; i < N; i++) digitalWrite(LEDS[i],LOW); } void echo_update(){ if (echo_mode == 0){ for (int i = 0; i < N; i++){ digitalWrite(LEDS[i], BTN_CURR[i] ? LOW : HIGH); } if (millis() - last_press_time > 3000 && n_events){ flash_screen(); flash_screen(); for (int i = 0; i < N; i++) digitalWrite(LEDS[i],LOW); echo_mode = 1; echo_start_t = millis(); } }else{ unsigned int t = millis() - echo_start_t; while (events[playhead].t < t){ digitalWrite(LEDS[events[playhead].idx], events[playhead].dir ? HIGH : LOW); playhead ++; if (playhead >= n_events){ playhead = 0; echo_start_t = millis(); break; } } } delay(10); } void echo_input(int dir, int idx){ if (echo_mode == 1){ game_over(); return; } if (n_events >= max_events){ return; } if (first_press_time < 0){ first_press_time = millis(); } last_press_time = millis(); events[n_events].idx = idx; events[n_events].dir = dir; events[n_events].t = millis()-first_press_time; n_events++; } /******************************************************* * Interpreter (Brainfuck variant) * *******************************************************/ const int max_prgm_l = 4096; const int max_mem = 256; char prgm[max_prgm_l] = {0}; char pmem[max_mem] = {0}; int prgm_mode; int prgm_len = 0; int code_ptr; int cell_ptr; int shft_key; #define CMD_NEXT 0 #define CMD_PREV 1 #define CMD_INCR 2 #define CMD_DECR 3 #define CMD_WRIT 4 //4+0 #define CMD_READ 5 //4+1 #define CMD_JMPL 6 //4+2 #define CMD_JMPR 7 //4+3 // run/exit 5 void prgm_init(){ prgm_mode = 0; code_ptr = 0; cell_ptr = 0; prgm_len = 0; shft_key = 0; for (int i = 0; i < max_mem; i++) pmem[i] = 0; for (int i = 0; i < N; i++) digitalWrite(LEDS[i],LOW); } void prgm_update(){ if (prgm_mode == 0){ for (int i = 0; i < N; i++) digitalWrite(LEDS[i],BTN_CURR[i] ? LOW : HIGH); }else{ if (prgm[code_ptr] == CMD_NEXT){ cell_ptr ++; code_ptr ++; }else if (prgm[code_ptr] == CMD_PREV){ cell_ptr --; code_ptr ++; }else if (prgm[code_ptr] == CMD_INCR){ pmem[cell_ptr] ++; code_ptr ++; }else if (prgm[code_ptr] == CMD_DECR){ pmem[cell_ptr] --; code_ptr ++; }else if (prgm[code_ptr] == CMD_WRIT){ for (int i = 0; i < N; i++){ digitalWrite( LEDS[i], ((pmem[cell_ptr]>>i) & 1 ) ? HIGH : LOW ); } code_ptr ++; }else if (prgm[code_ptr] == CMD_READ){ int x = 0; for (int i = N-1; i>=0; i--){ x = (x << 1) | (BTN_CURR[i] == HIGH ? 0 : 1); } pmem[cell_ptr] = x; code_ptr ++; }else if (prgm[code_ptr] == CMD_JMPL){ if (pmem[cell_ptr]){ code_ptr++; }else{ code_ptr ++; int lvl = 0; while (1){ if (prgm[code_ptr] == CMD_JMPL){ lvl++; } if (prgm[code_ptr] == CMD_JMPR){ if (lvl == 0){ break; } lvl--; } code_ptr ++; } code_ptr ++; } }else if (prgm[code_ptr] == CMD_JMPR){ if (pmem[cell_ptr]){ int lvl = 0; code_ptr --; while (1){ if (prgm[code_ptr] == CMD_JMPR){ lvl++; } if (prgm[code_ptr] == CMD_JMPL){ if (lvl == 0){ break; } lvl--; } code_ptr --; } code_ptr ++; }else{ code_ptr ++; } } if (code_ptr >= prgm_len){ delay(4000); game_over(); } } delay(10); } void prgm_input(int dir, int idx){ if (dir){ if (prgm_mode == 0){ if (idx == 5){ flash_screen(); prgm_mode = 1; return; }else if (idx == 4){ shft_key = 1; }else{ prgm[prgm_len]=(char)(idx + shft_key * 4); prgm_len ++; shft_key = 0; } }else{ if (idx == 5){ game_over(); } } } } /******************************************************* * Main * *******************************************************/ void setup() { for (int i = 0; i < N; i++) pinMode(LEDS[i], OUTPUT); for (int i = 0; i < N; i++) pinMode(BTNS[i], INPUT); dance(); gsel_init(); } void loop() { btn_update(); if (mode == -1){ gsel_update(); }else if (mode == 0){ pong_update(); }else if (mode == 1){ whac_update(); }else if (mode == 2){ simo_update(); }else if (mode == 3){ croc_update(); }else if (mode == 4){ echo_update(); }else if (mode == 5){ prgm_update(); } }