improved the randomizer

master
Ondřej Hruška 7 years ago
parent a463765153
commit b69004db7b
  1. 2
      CMakeLists.txt
  2. 1
      Makefile
  3. 11
      README.md
  4. 24
      display.h
  5. 79
      game.c
  6. 9
      game.h
  7. 8
      leds.h
  8. 2
      main.c
  9. 74
      rng.c
  10. 16
      rng.h

@ -33,7 +33,7 @@ set(SOURCE_FILES
leds.c
leds.h
game.c
game.h)
game.h rng.c rng.h)
include_directories(lib
/usr/avr/include/)

@ -37,6 +37,7 @@ OBJS += lib/color.o
OBJS += display.o
OBJS += game.o
OBJS += leds.o
OBJS += rng.o
# Dirs with header files
INCL_DIRS = . lib/

@ -1,8 +1,11 @@
# Simon Says with Pro Mini
# Simon game with Pro Mini
Fun little project that has grown quite more than expected, at least on the hardware side.
Fun little project that has grown quite more than expected, at least on the
hardware side.
There will be a proper log for this later, with schematics etc.
There will be a proper log for this later, with schematics etc.
(update, 3 months later: or maybe not)
For now, you can have a peek and thank heavens you don't have to understand any of it xD
For now, you can have a peek and thank heavens you don't have to understand
any of it xD

@ -7,6 +7,15 @@
#include <stdint.h>
// AAA
// F B
// F B
// GGG
// E C
// E C
// DDD H
// individual segments
#define SEG_A _BV(0)
#define SEG_B _BV(1)
#define SEG_C _BV(2)
@ -16,6 +25,7 @@
#define SEG_G _BV(6)
#define SEG_H _BV(7)
// composed digits
#define DIGIT_0 (SEG_A|SEG_B|SEG_C|SEG_D|SEG_E|SEG_F)
#define DIGIT_1 (SEG_B|SEG_C)
#define DIGIT_2 (SEG_A|SEG_B|SEG_D|SEG_E|SEG_G)
@ -30,8 +40,22 @@
extern const uint8_t disp_digits[10];
extern volatile uint8_t disp_brightness; // place to globally store display brightness
/**
* Send raw digits to the display
* @param dig0 - segments of the left digit
* @param dig1 - segments of the right digit
*/
void display_show(uint8_t dig0, uint8_t dig1);
/**
* Show a number 0-99 on the display
* @param num - number to show
*/
void display_show_number(uint8_t num);
/**
* Init the PWM timer with automatic adjust from ADC level
*/
void setup_pwm(void);
#endif //FIRMWARE_DISPLAY_H

@ -10,11 +10,13 @@
#include <stdlib.h>
#include <avr/io.h>
#include <iopins.h>
#include <usart.h>
#include "lib/color.h"
#include "leds.h"
#include "lib/timebase.h"
#include "display.h"
#include "pinout.h"
#include "rng.h"
//region Colors
@ -72,13 +74,6 @@ const uint32_t dimwhite[4] = {C_DIMWHITE, C_DIMWHITE, C_DIMWHITE, C_DIMWHITE};
#define SUC_EFF_TIME 500
#define FAIL_EFF_TIME 1000
/** Sequence of colors to show. Seed is constant thorough a game.
* rng_state is used by rand_r() for building the sequence. */
uint32_t game_seed;
unsigned long game_rng_state;
uint8_t last_item;
uint8_t repeat_count;
/** Nr of revealed colors in sequence */
uint8_t game_revealed_n;
/** Nr of next color to replay/input */
@ -94,36 +89,6 @@ void show_screen()
leds_set(screen);
}
/** Prepare rng sequence for replay / test */
void reset_sequence()
{
game_rng_state = game_seed;
last_item = 99;
repeat_count = 0;
}
/** Get next item in the sequence */
uint8_t get_next_item()
{
uint8_t item;
while (1) {
item = (uint8_t) rand_r(&game_rng_state) & 0x03;
if (item == last_item) {
repeat_count++;
if (repeat_count < 2) {
goto suc;
}
} else {
last_item = item;
repeat_count = 0;
goto suc;
}
}
suc:
return item;
}
/** Enter state - callback for delayed state change */
void deferred_enter_state(void *state)
{
@ -144,7 +109,7 @@ void replay_callback(void *onOff)
screen[3] = C_DARK;
if (on) {
uint8_t color = get_next_item();
uint8_t color = rng_next_item();
game_replay_n++;
screen[color] = brt[color];
show_screen();
@ -208,6 +173,7 @@ void enter_state(enum GameState_enum state)
switch (state) {
case STATE_NEW_GAME:
usart_puts("State: new game\r\n");
// new game - idle state before new game is started
// all dimly lit
@ -215,28 +181,32 @@ void enter_state(enum GameState_enum state)
break;
case STATE_REPLAY:
usart_puts("State: replay\r\n");
game_replay_n = 0;
reset_sequence();
rng_restart();
// Start replay
replay_callback((void *) 1);
break;
case STATE_USER_INPUT:
usart_puts("State: repeat\r\n");
memcpy(screen, dim, sizeof(screen));
// Start entering & checking
game_repeat_n = 0;
reset_sequence();
rng_restart();
break;
case STATE_SUCCESS_EFFECT:
usart_puts("State: succ\r\n");
memcpy(screen, dim, sizeof(screen));
//suc_eff_callback((void *) 1);
schedule_task(suc_eff_callback, (void *) 1, 250, false);
break;
case STATE_FAIL_EFFECT:
usart_puts("State: fail\r\n");
memcpy(screen, dim, sizeof(screen));
//fail_eff_callback((void *) 1);
schedule_task(fail_eff_callback, (void *) 1, 250, false);
@ -249,15 +219,13 @@ void enter_state(enum GameState_enum state)
/** Prepare new sequence, using time for seed. */
void prepare_sequence()
{
game_seed = time_ms;
game_rng_state = game_seed;
last_item = 99;
repeat_count = 0;
rng_set_seed(time_ms);
rng_restart();
}
volatile uint16_t idle_cnt = 0;
/** Main function, called from MX-generated main.c */
/** game main function */
void game_main(void)
{
display_show(SEG_G, SEG_G); // two dashes...
@ -268,13 +236,13 @@ void game_main(void)
while (1) {
if (GameState == last_state) {
if (GameState == STATE_NEW_GAME) {
if (idle_cnt == 20 && !holding_new_game_button) {
// clear after 2 secs
if (idle_cnt == 25 && !holding_new_game_button) {
usart_puts("clear highscore display\r\n");
display_show(SEG_G, SEG_G);
}
if (idle_cnt == 3000) {
// Shut down after 5 mins
usart_puts("automatic shutdown\r\n");
screen[0] = C_CRIMSON;
screen[1] = C_CRIMSON;
screen[2] = C_CRIMSON;
@ -285,8 +253,9 @@ void game_main(void)
while(1); // wait for shutdown
}
} else {
if (idle_cnt > 150) {// 15 secs = stop game.
if (idle_cnt > 200) {
// reset state
usart_puts("game reset, user walked away\r\n");
enter_state(STATE_NEW_GAME);
show_screen();
display_show(SEG_G, SEG_G);
@ -316,12 +285,14 @@ void game_button_handler(uint8_t button, bool press)
switch (GameState) {
case STATE_NEW_GAME:
if (press) {
usart_puts("pressed a new-game button\r\n");
// feedback
display_show_number(0); // show 0
holding_new_game_button = true;
}
if (!press) { // released
usart_puts("game begins\r\n");
// user wants to start playing
prepare_sequence();
game_revealed_n = 1; // start with 1 revealed
@ -336,6 +307,7 @@ void game_button_handler(uint8_t button, bool press)
//enter_state(STATE_REPLAY);
}
break;
case STATE_USER_INPUT:
// Reset idle counter, so it doesn't cut off in the middle of input
idle_cnt = 0;
@ -349,16 +321,17 @@ void game_button_handler(uint8_t button, bool press)
} else {
// Button is released
// Verify correctness
uint8_t expected = get_next_item();
uint8_t expected = rng_next_item();
if (expected == button) {
// good!
usart_puts("good key!\r\n");
game_repeat_n++;
if (game_repeat_n == game_revealed_n) {
// repeated all, good work!
usart_puts("repeated all, good work!\r\n");
game_revealed_n++;
enter_state(STATE_SUCCESS_EFFECT);
}
} else {
usart_puts("oops bad key\r\n");
enter_state(STATE_FAIL_EFFECT);
}
}
@ -367,7 +340,7 @@ void game_button_handler(uint8_t button, bool press)
break;
default:
// discard button press, not expecting input now
usart_puts("discard button press, not expecting input now\r\n");
break;
}
}

@ -8,8 +8,17 @@
#include <stdint.h>
#include <stdbool.h>
/**
* Game entry point
*/
void game_main(void);
/**
* Button press handler
*
* @param button
* @param press
*/
void game_button_handler(uint8_t button, bool press);
#endif //FIRMWARE_GAME_H

@ -7,8 +7,16 @@
#include <stdint.h>
/**
* Set led colors
*
* @param new_leds - array of 4 ints for the 4 leds
*/
void leds_set(const uint32_t *new_leds);
/**
* Send the set colors to the neopixels
*/
void leds_show(void);
#endif //FIRMWARE_LEDS_H

@ -96,7 +96,7 @@ void task_check_shutdown_btn(void *unused) {
if (debo_get_pin(0) // 0 - first
&& !booting
&& (time_ms - time_pwr_pressed > 1000)) {
&& (time_ms - time_pwr_pressed > 250)) {
cli();
ws_no_cli_sei = true;

74
rng.c

@ -0,0 +1,74 @@
//
// Created by MightyPork on 2017/09/16.
//
#include "rng.h"
#include <stdint.h>
#include <stdlib.h>
/** Sequence of colors to show. Seed is constant thorough a game.
* rng_state is used by rand_r() for building the sequence. */
static uint32_t game_seed;
static unsigned long game_rng_state;
static uint8_t last_item;
static uint8_t repeat_count;
#define NUM_CNT 4
static uint16_t weights[NUM_CNT];
void rng_set_seed(uint32_t seed)
{
game_seed = seed;
}
void rng_restart(void)
{
game_rng_state = game_seed;
repeat_count = 0;
last_item = 99;
for (uint8_t i = 0; i < 4; i++) {
weights[i] = 1;
}
}
static uint8_t pick_do(void)
{
uint16_t total = 0;
for (uint8_t i = 0; i < 4; i++) {
total += weights[i];
}
uint16_t rn = rand_r(&game_rng_state) % total;
for (uint8_t i = 0; i < 4; i++) {
if (rn < weights[i]) {
// got our number
for (uint8_t j = 0; j < 4; j++) {
if (i == j) {
weights[j]/=2;
}
else weights[j]+=4;
}
return i;
}
rn -= weights[i];
}
return 0; // this never happens but keeps the compiler happy
}
uint8_t rng_next_item(void)
{
uint8_t item;
while (1) {
item = pick_do();
if (item == last_item) {
repeat_count++;
if (repeat_count < 2) return item;
} else {
last_item = item;
repeat_count = 0;
return item;
}
}
}

16
rng.h

@ -0,0 +1,16 @@
//
// Created by MightyPork on 2017/09/16.
//
#ifndef FIRMWARE_RNG_H
#define FIRMWARE_RNG_H
#include <stdint.h>
void rng_set_seed(uint32_t seed);
void rng_restart(void);
uint8_t rng_next_item(void);
#endif //FIRMWARE_RNG_H
Loading…
Cancel
Save