From 463ebfa3e0ed1ba9f07a41c3ce80e2aa64983743 Mon Sep 17 00:00:00 2001 From: redfast00 Date: Wed, 3 Feb 2021 01:25:03 +0100 Subject: [PATCH] Deterministic modules --- docs/GETTING_STARTED.md | 13 +++++++++++-- docs/protocol.txt | 5 +++-- lib/obus_can.cpp | 19 ++++++++++++++++++- lib/obus_can.h | 8 +++++++- lib/obus_module.cpp | 4 ++++ src/controller/controller.ino | 5 ++++- 6 files changed, 47 insertions(+), 7 deletions(-) diff --git a/docs/GETTING_STARTED.md b/docs/GETTING_STARTED.md index b03a385..e6e0bac 100644 --- a/docs/GETTING_STARTED.md +++ b/docs/GETTING_STARTED.md @@ -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 diff --git a/docs/protocol.txt b/docs/protocol.txt index 176b8bc..12dfac9 100644 --- a/docs/protocol.txt +++ b/docs/protocol.txt @@ -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 diff --git a/lib/obus_can.cpp b/lib/obus_can.cpp index 69f5cd5..2e32f62 100644 --- a/lib/obus_can.cpp +++ b/lib/obus_can.cpp @@ -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); diff --git a/lib/obus_can.h b/lib/obus_can.h index 9ce9fe1..e8801a5 100644 --- a/lib/obus_can.h +++ b/lib/obus_can.h @@ -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); } diff --git a/lib/obus_module.cpp b/lib/obus_module.cpp index 1c9b075..87f0fd8 100644 --- a/lib/obus_module.cpp +++ b/lib/obus_module.cpp @@ -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); diff --git a/src/controller/controller.ino b/src/controller/controller.ino index 44407ef..ff2bb54 100644 --- a/src/controller/controller.ino +++ b/src/controller/controller.ino @@ -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 "); }