2020-08-20 17:47:32 +02:00
|
|
|
#include "shared.hpp"
|
|
|
|
#include "obus_can.hpp"
|
2020-08-10 18:02:17 +02:00
|
|
|
|
2020-08-18 16:51:51 +02:00
|
|
|
|
2020-08-10 18:02:17 +02:00
|
|
|
#define STATE_INACTIVE 0
|
|
|
|
#define STATE_HELLO 1
|
|
|
|
#define STATE_GAME 2
|
|
|
|
|
2020-08-20 17:47:32 +02:00
|
|
|
#define OBUS_MAX_STRIKES 3 // Number of strikes allowed until game over
|
|
|
|
#define OBUS_GAME_DURATION 10 // Duration of the game in seconds
|
2020-08-10 18:02:17 +02:00
|
|
|
|
2020-08-10 22:40:04 +02:00
|
|
|
|
2020-08-20 17:47:32 +02:00
|
|
|
#define OBUS_GAME_DURATION_MS ((uint32_t) OBUS_GAME_DURATION*1000)
|
|
|
|
|
2020-08-10 18:02:17 +02:00
|
|
|
|
|
|
|
uint8_t state = STATE_INACTIVE;
|
2020-08-10 22:40:04 +02:00
|
|
|
struct module connected_modules_ids[OBUS_MAX_MODULES];
|
2020-08-10 21:23:09 +02:00
|
|
|
uint8_t nr_connected_modules;
|
2020-08-20 17:47:32 +02:00
|
|
|
uint8_t strikes;
|
2020-08-10 21:23:09 +02:00
|
|
|
|
2020-08-10 23:26:45 +02:00
|
|
|
// Bit vectors for checking if game is solved or not
|
|
|
|
uint8_t unsolved_puzzles[32]; // 256 bits
|
|
|
|
|
2020-08-10 22:40:04 +02:00
|
|
|
// TIMERS
|
2020-08-20 17:47:32 +02:00
|
|
|
uint32_t hello_round_start;
|
|
|
|
uint32_t game_start;
|
|
|
|
uint32_t last_update;
|
2020-08-10 18:02:17 +02:00
|
|
|
|
2020-08-20 17:47:32 +02:00
|
|
|
struct module this_module = {
|
|
|
|
.type = OBUS_TYPE_CONTROLLER,
|
|
|
|
.id = OBUS_CONTROLLER_ID
|
2020-08-18 17:52:33 +02:00
|
|
|
};
|
|
|
|
|
2020-08-10 23:26:45 +02:00
|
|
|
|
2020-08-10 18:02:17 +02:00
|
|
|
void setup() {
|
|
|
|
Serial.begin(9600);
|
2020-08-20 17:47:32 +02:00
|
|
|
obuscan_init();
|
2020-08-10 23:26:45 +02:00
|
|
|
|
2020-08-18 17:52:33 +02:00
|
|
|
state = STATE_INACTIVE;
|
2020-08-10 22:40:04 +02:00
|
|
|
}
|
|
|
|
|
2020-08-10 23:26:45 +02:00
|
|
|
|
|
|
|
uint8_t check_solved() {
|
2020-08-18 16:15:09 +02:00
|
|
|
uint8_t solved = 1;
|
2020-08-20 17:47:32 +02:00
|
|
|
for (uint8_t i = 0; i < 32; i++) {
|
2020-08-18 16:15:09 +02:00
|
|
|
if (unsolved_puzzles != 0) {
|
|
|
|
solved = 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return solved;
|
2020-08-10 23:26:45 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void add_module_to_bit_vector(uint8_t module_id) {
|
2020-08-18 16:15:09 +02:00
|
|
|
uint8_t byte_index = module_id >> 3;
|
|
|
|
uint8_t bit_index = module_id & 0x07;
|
|
|
|
unsolved_puzzles[byte_index] |= 0x1 << bit_index;
|
2020-08-10 23:26:45 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void solve_module_in_bit_vector(uint8_t module_id) {
|
2020-08-18 16:15:09 +02:00
|
|
|
uint8_t byte_index = module_id >> 3;
|
|
|
|
uint8_t bit_index = module_id & 0x07;
|
|
|
|
unsolved_puzzles[byte_index] &= ~(0x1 << bit_index);
|
2020-08-10 23:26:45 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-08-10 21:23:09 +02:00
|
|
|
void start_hello() {
|
|
|
|
state = STATE_HELLO;
|
2020-08-18 16:15:09 +02:00
|
|
|
hello_round_start = millis();
|
|
|
|
nr_connected_modules = 0;
|
|
|
|
|
|
|
|
// Zero bit vectors
|
2020-08-20 17:47:32 +02:00
|
|
|
for (uint8_t i = 0; i < 32; i++) {
|
2020-08-18 16:15:09 +02:00
|
|
|
unsolved_puzzles[i] = 0;
|
|
|
|
}
|
2020-08-10 21:23:09 +02:00
|
|
|
|
2020-08-20 17:47:32 +02:00
|
|
|
struct obus_message msg = obuscan_msg_c_hello(this_module);
|
|
|
|
obuscan_send(&msg);
|
2020-08-10 21:23:09 +02:00
|
|
|
|
2020-08-20 17:47:32 +02:00
|
|
|
Serial.println(F("Start of discovery round"));
|
2020-08-10 18:02:17 +02:00
|
|
|
}
|
|
|
|
|
2020-08-10 23:26:45 +02:00
|
|
|
|
2020-08-10 21:23:09 +02:00
|
|
|
void send_ack() {
|
2020-08-20 17:47:32 +02:00
|
|
|
struct obus_message msg = obuscan_msg_c_ack(this_module);
|
|
|
|
obuscan_send(&msg);
|
2020-08-10 21:23:09 +02:00
|
|
|
}
|
|
|
|
|
2020-08-10 23:26:45 +02:00
|
|
|
|
2020-08-10 20:46:41 +02:00
|
|
|
void receive_hello() {
|
2020-08-20 17:47:32 +02:00
|
|
|
struct obus_message msg;
|
|
|
|
uint32_t current_time = millis();
|
2020-08-18 16:15:09 +02:00
|
|
|
|
2020-08-20 17:47:32 +02:00
|
|
|
if (obuscan_receive(&msg)) {
|
|
|
|
if (msg.msg_type == OBUS_MSGTYPE_M_HELLO) {
|
2020-08-18 16:15:09 +02:00
|
|
|
Serial.print("Registered module ");
|
2020-08-20 17:47:32 +02:00
|
|
|
Serial.println(full_module_id(msg.from));
|
|
|
|
connected_modules_ids[nr_connected_modules] = msg.from;
|
2020-08-18 16:15:09 +02:00
|
|
|
nr_connected_modules++;
|
|
|
|
|
2020-08-20 17:47:32 +02:00
|
|
|
if (msg.from.type == OBUS_TYPE_PUZZLE) {
|
|
|
|
add_module_to_bit_vector(full_module_id(msg.from));
|
2020-08-18 16:15:09 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
send_ack();
|
|
|
|
Serial.println("ACK");
|
|
|
|
}
|
2020-08-20 17:47:32 +02:00
|
|
|
} else if (current_time - hello_round_start > OBUS_DISC_DURATION_MS) {
|
2020-08-18 16:15:09 +02:00
|
|
|
Serial.println("End of discovery round");
|
2020-08-18 16:51:51 +02:00
|
|
|
initialize_game();
|
2020-08-18 16:15:09 +02:00
|
|
|
}
|
2020-08-10 20:46:41 +02:00
|
|
|
}
|
|
|
|
|
2020-08-10 23:26:45 +02:00
|
|
|
|
2020-08-10 22:40:04 +02:00
|
|
|
void initialize_game() {
|
2020-08-20 17:47:32 +02:00
|
|
|
strikes = 0;
|
2020-08-18 16:15:09 +02:00
|
|
|
game_start = millis();
|
2020-08-10 22:40:04 +02:00
|
|
|
|
2020-08-20 17:47:32 +02:00
|
|
|
last_update = game_start;
|
2020-08-18 16:51:51 +02:00
|
|
|
state = STATE_GAME;
|
|
|
|
|
2020-08-18 16:15:09 +02:00
|
|
|
Serial.println("Game started");
|
2020-08-20 17:47:32 +02:00
|
|
|
|
|
|
|
send_game_update(OBUS_MSGTYPE_C_GAMESTART, OBUS_GAME_DURATION_MS);
|
2020-08-10 22:40:04 +02:00
|
|
|
}
|
|
|
|
|
2020-08-10 23:26:45 +02:00
|
|
|
|
2020-08-10 22:40:04 +02:00
|
|
|
void receive_module_update() {
|
2020-08-20 17:47:32 +02:00
|
|
|
struct obus_message msg;
|
|
|
|
|
|
|
|
if (obuscan_receive(&msg)) {
|
|
|
|
|
|
|
|
switch (msg.msg_type) {
|
|
|
|
case OBUS_MSGTYPE_M_STRIKE:
|
|
|
|
// TODO check idempotency ID
|
|
|
|
strikes++;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OBUS_MSGTYPE_M_SOLVED:
|
|
|
|
solve_module_in_bit_vector(full_module_id(msg.from));
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
Serial.print(F("W Ignoring msg "));
|
|
|
|
Serial.println(msg.msg_type);
|
|
|
|
break;
|
2020-08-18 16:15:09 +02:00
|
|
|
}
|
2020-08-20 17:47:32 +02:00
|
|
|
|
2020-08-18 16:15:09 +02:00
|
|
|
}
|
2020-08-10 22:40:04 +02:00
|
|
|
}
|
|
|
|
|
2020-08-10 23:26:45 +02:00
|
|
|
|
2020-08-20 17:47:32 +02:00
|
|
|
void send_game_update(uint8_t msg_type, uint32_t time_left) {
|
|
|
|
Serial.print(F("Send "));
|
|
|
|
Serial.print(msg_type);
|
|
|
|
Serial.print(F(": "));
|
|
|
|
Serial.print(time_left);
|
|
|
|
Serial.print(F(", "));
|
|
|
|
Serial.print(strikes);
|
|
|
|
Serial.print(F("/"));
|
|
|
|
Serial.println(OBUS_MAX_STRIKES);
|
2020-08-18 16:15:09 +02:00
|
|
|
|
2020-08-20 17:47:32 +02:00
|
|
|
struct obus_message msg = obuscan_msg_c_payld_gamestatus(
|
|
|
|
this_module, false, msg_type, time_left, strikes, OBUS_MAX_STRIKES);
|
|
|
|
obuscan_send(&msg);
|
2020-08-10 23:26:45 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-08-10 22:40:04 +02:00
|
|
|
void game_loop() {
|
2020-08-20 17:47:32 +02:00
|
|
|
uint32_t current_time = millis();
|
|
|
|
uint32_t time_elapsed = current_time - game_start;
|
|
|
|
uint32_t time_left =
|
|
|
|
OBUS_GAME_DURATION_MS < time_elapsed ? 0 : OBUS_GAME_DURATION_MS - time_elapsed;
|
|
|
|
// We cannot check for '<= 0' in an uint type so we check the terms prior to subtraction
|
2020-08-18 16:15:09 +02:00
|
|
|
|
|
|
|
receive_module_update();
|
|
|
|
|
|
|
|
if (check_solved()) {
|
|
|
|
Serial.println("Game solved");
|
2020-08-20 17:47:32 +02:00
|
|
|
send_game_update(OBUS_MSGTYPE_C_SOLVED, time_left);
|
2020-08-18 16:15:09 +02:00
|
|
|
state = STATE_INACTIVE;
|
|
|
|
return;
|
2020-08-20 17:47:32 +02:00
|
|
|
} else if (time_left == 0) {
|
2020-08-18 16:51:51 +02:00
|
|
|
Serial.println("Time's up");
|
2020-08-20 17:47:32 +02:00
|
|
|
send_game_update(OBUS_MSGTYPE_C_TIMEOUT, time_left);
|
2020-08-18 16:15:09 +02:00
|
|
|
state = STATE_INACTIVE;
|
|
|
|
return;
|
2020-08-20 17:47:32 +02:00
|
|
|
} else if (strikes >= OBUS_MAX_STRIKES) {
|
2020-08-18 16:15:09 +02:00
|
|
|
Serial.println("Strikeout");
|
2020-08-20 17:47:32 +02:00
|
|
|
send_game_update(OBUS_MSGTYPE_C_STRIKEOUT, time_left);
|
2020-08-18 16:15:09 +02:00
|
|
|
state = STATE_INACTIVE;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-08-20 17:47:32 +02:00
|
|
|
if (last_update + OBUS_UPDATE_INTERVAL <= current_time) {
|
|
|
|
send_game_update(OBUS_MSGTYPE_C_STATE, time_left);
|
2020-08-18 16:15:09 +02:00
|
|
|
last_update = current_time;
|
|
|
|
}
|
2020-08-10 22:40:04 +02:00
|
|
|
}
|
|
|
|
|
2020-08-10 23:26:45 +02:00
|
|
|
|
2020-08-10 18:02:17 +02:00
|
|
|
void loop() {
|
2020-08-18 16:51:51 +02:00
|
|
|
switch (state) {
|
|
|
|
case STATE_INACTIVE:
|
|
|
|
start_hello();
|
|
|
|
break;
|
|
|
|
|
|
|
|
case STATE_HELLO:
|
|
|
|
receive_hello();
|
|
|
|
break;
|
|
|
|
|
|
|
|
case STATE_GAME:
|
2020-08-18 16:15:09 +02:00
|
|
|
game_loop();
|
2020-08-18 16:51:51 +02:00
|
|
|
break;
|
2020-08-18 16:15:09 +02:00
|
|
|
}
|
2020-08-10 18:02:17 +02:00
|
|
|
}
|