Deterministic modules

This commit is contained in:
redfast00 2021-02-03 01:25:03 +01:00
parent f98868d4f3
commit 463ebfa3e0
No known key found for this signature in database
GPG key ID: 5946E0E34FD0553C
6 changed files with 47 additions and 7 deletions

View file

@ -93,10 +93,19 @@ same effect as just resetting the microcontroller, so if your state is too hard
- The setup code, initializing your microcontroller and setting the type and id of the module with `obus_module::setup`
- The main loop code. This should call the `loopPuzzle` function frequently so that all CAN packets can be handled.
If you are using calls to `delay()`, try to replace them with a timer (a variable that keeps track of when something should happen). That way the loop can continue executing, without being stuck in the `delay()` function.
- A call to the `obus_module::solve` function
- A description for the expert of how to defuse the module in `doc/index.md` of your module folder
- The `callback_game_start`, `callback_game_stop` and `callback_info` functions. These can be empty.
- The `callback_game_start` and `callback_game_stop` functions. These can be empty.
Some tips:
- If you are using calls to `delay()`, try to replace them with a timer (a variable that keeps track of when something should happen).
That way the function that handles CAN messages (`loopPuzzle`) can continue executing, without the microcontroller being stuck in the `delay()` function.
The `loopPuzzle` function will automatically put itself into an error state if it has been too long since it has been called. This is meant to make
writing correct code easier: that way you discover your code needs to be rewritten easily instead of having to figure out that and why messages are dropped.
- Every time you play the game, it should be different (otherwise the defuser could just memorize what to do, this would be no fun for the experts).
To accomplish this, you can use the `random()` function. You don't need to seed it (we recommend against it), because the OBUS framework already
seeds this for you every game. That way, we can replay games for debugging if needed.
## More advanced puzzle modules

View file

@ -68,8 +68,9 @@ Types for controller:
- 7 info start
[ X B B B B B B B ]
--------------
reserved
-------- -----
↓ reserved
random seed
- 8-255 reserved

View file

@ -48,6 +48,9 @@ uint8_t payload_type(uint8_t module_type, uint8_t module_id, uint8_t msg_type) {
case OBUS_MSGTYPE_C_STRIKEOUT:
return OBUS_PAYLDTYPE_GAMESTATUS;
case OBUS_MSGTYPE_C_INFOSTART:
return OBUS_PAYLDTYPE_INFOSTART;
default:
return false;
break;
@ -142,13 +145,22 @@ bool receive(struct message *msg) {
case OBUS_PAYLDTYPE_MODULEADDR:
{
if (receive_frame.can_dlc < 3) {
Serial.println(F("W Received illegal count msg: payload <3"));
Serial.println(F("W Received illegal moduleaddr msg: payload <3"));
return false;
}
msg->payload_address.type = receive_frame.data[1];
msg->payload_address.id = receive_frame.data[2];
}
break;
case OBUS_PAYLDTYPE_INFOSTART:
{
if (receive_frame.can_dlc < 5) {
Serial.println(F("W Received illegal infostart msg: payload <5"));
return false;
}
msg->infostart.seed = unpack_4b_into_u32(&(receive_frame.data[1]));
}
break;
default:
Serial.println(F("W Couldn't determine payload type"));
return false;
@ -206,6 +218,11 @@ void send(struct message *msg) {
send_frame.data[2] = msg->payload_address.id;
break;
case OBUS_PAYLDTYPE_INFOSTART:
pack_u32_into_4b(&(send_frame.data[1]), msg->gamestatus.time_left);
length = 5;
break;
default:
Serial.print(F("E Unknown payload type "));
Serial.println(pyld_type);

View file

@ -36,6 +36,7 @@
#define OBUS_PAYLDTYPE_COUNT 2
#define OBUS_PAYLDTYPE_INFO 3
#define OBUS_PAYLDTYPE_MODULEADDR 4
#define OBUS_PAYLDTYPE_INFOSTART 5
#define OBUS_PAYLD_INFO_MAXLEN (OBUS_MSG_LENGTH - 1)
@ -57,6 +58,9 @@ struct payld_infomessage {
uint8_t len;
uint8_t data[OBUS_PAYLD_INFO_MAXLEN];
};
struct payld_infostart {
uint32_t seed;
};
struct message {
@ -69,6 +73,7 @@ struct message {
uint8_t count;
struct payld_infomessage infomessage;
struct module payload_address;
struct payld_infostart infostart;
};
};
@ -208,9 +213,10 @@ inline void send_c_timeout(
/**
* Send a controller "info start" OBUS message
*/
inline void send_c_infostart(struct module from) {
inline void send_c_infostart(struct module from, uint32_t seed) {
assert(from.type == OBUS_TYPE_CONTROLLER);
struct message msg = _msg(from, false, OBUS_MSGTYPE_C_INFOSTART);
msg.infostart.seed = seed;
send(&msg);
}

View file

@ -165,6 +165,9 @@ bool loopPuzzle(obus_can::message* message, void (*callback_game_start)(uint8_t
case OBUS_MSGTYPE_C_STATE:
callback_state(message->gamestatus.time_left, message->gamestatus.strikes, message->gamestatus.max_strikes, message->gamestatus.puzzle_modules_left);
break;
case OBUS_MSGTYPE_C_INFOSTART:
randomSeed(message->infostart.seed);
break;
default:
break;
}
@ -192,6 +195,7 @@ bool loopInfo(obus_can::message* message, int (*info_generator)(uint8_t*)) {
switch (message->msg_type) {
case OBUS_MSGTYPE_C_INFOSTART:
{
randomSeed(message->infostart.seed);
uint8_t info_message[OBUS_PAYLD_INFO_MAXLEN];
int len = info_generator(info_message);
obus_can::send_i_infomessage(this_module, info_message, len);

View file

@ -19,6 +19,9 @@
#define OBUS_GAME_DURATION_MS ((uint32_t) OBUS_GAME_DURATION*1000)
#define OBUS_INFO_DURATION_MS ((uint32_t) OBUS_INFO_DURATION*1000)
// This should be changed every run, but for development it's useful to pin this
#define OBUS_RANDOM_SEED 42
#define DIVIDE_CEIL(dividend, divisor) ((dividend + (divisor - 1)) / divisor)
#define MAX_AMOUNT_PUZZLES (UINT8_MAX + 1) // The ID of a puzzle is a zero-based uint8
@ -104,7 +107,7 @@ void solve_puzzle_in_bit_vector(uint8_t module_id) {
void start_info() {
state = STATE_INFO;
info_round_start = millis();
obus_can::send_c_infostart(this_module);
obus_can::send_c_infostart(this_module, OBUS_RANDOM_SEED);
Serial.println(F(" Start of info round"));
tm.displayText("InFO ");
}