diff --git a/lib/Cross/Gun/Atmega328pHal.cpp b/lib/Cross/Gun/Atmega328pHal.cpp index 16121bd..01f0ba0 100644 --- a/lib/Cross/Gun/Atmega328pHal.cpp +++ b/lib/Cross/Gun/Atmega328pHal.cpp @@ -14,7 +14,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -#include +#include #include #include #include diff --git a/lib/Domain/Contactor/Contactor.hpp b/lib/Domain/Contactor/Contactor.hpp index 00fd659..f3f4308 100644 --- a/lib/Domain/Contactor/Contactor.hpp +++ b/lib/Domain/Contactor/Contactor.hpp @@ -16,6 +16,11 @@ */ #pragma once +/** + * Designed to manage hardware buttons and their different behaviour: + * - short press + * - long press + */ class Contactor { static const int LONG_PRESS = 2000; diff --git a/lib/Domain/Game.cpp b/lib/Domain/Game.cpp deleted file mode 100644 index 407ad75..0000000 --- a/lib/Domain/Game.cpp +++ /dev/null @@ -1,69 +0,0 @@ -/* - * - * Copyright (c) 2023 Aurélien Labrosse - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -#include - -// 5 rounds per player -static const uint8_t TOTAL_ROUNDS = 20; - -Game::Game() { - currentRound = 0; - currentPlayer = &players[0]; -} - -void Game::recordSucceededShoot() { - currentPlayer->recordSucceededShoot(); -} - -/** - * End current player round, and initiate next player round. - * Note: loop on player 4 - */ -void Game::nextRound() { - - currentPlayer->endRound(); - - currentRound++; - uint8_t playerId = currentPlayer->id; - auto nextPlayerId = 0; - if (playerId < (PLAYER_COUNT - 1)) { - nextPlayerId = playerId + 1; - } - currentPlayer = &players[nextPlayerId]; - currentPlayer->startRound(); -} - -bool Game::isFinished() { - // -1 because rounds counts from 0 to 19 - return currentRound > TOTAL_ROUNDS - 1; -} - -void Game::changeCurrentPlayerTo(uint8_t playerIndex) { - if (playerIndex < PLAYER_COUNT) { - currentPlayer->endRound(); - currentPlayer = &players[playerIndex]; - currentPlayer->startRound(); - } -} - -void Game::reset() { - for (Player &player : players) { - player.reset(); - } - currentRound = 0; - currentPlayer = &players[0]; - currentPlayer->startRound(); -} \ No newline at end of file diff --git a/lib/Domain/Game.hpp b/lib/Domain/Game.hpp deleted file mode 100644 index 7c13833..0000000 --- a/lib/Domain/Game.hpp +++ /dev/null @@ -1,66 +0,0 @@ -/* - * - * Copyright (c) 2023 Aurélien Labrosse - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -#pragma once - -#include -#include - -#include - -/** - * A game has 4 players - */ -class Game { - -public: - static const uint8_t PLAYER_COUNT = 4; - - // clang-format off - Player players[PLAYER_COUNT] = { - Player(0), - Player(1), - Player(2), - Player(3) - }; - // clang-format on - - uint8_t currentRound; - Player *currentPlayer; - - Game(); - - void recordSucceededShoot(); - - /** - * Change current player - * playerIndex shall be [0;3] - */ - void changeCurrentPlayerTo(uint8_t playerIndex); - - /** - * End current player round, and initiate next player round - */ - void nextRound(); - - /** - * Return true when all rounds have been played. A game stay finished until - * restart. - */ - bool isFinished(); - - void reset(); -}; diff --git a/lib/Domain/Game/Basic5/Basic5.cpp b/lib/Domain/Game/Basic5/Basic5.cpp new file mode 100644 index 0000000..b93bd1f --- /dev/null +++ b/lib/Domain/Game/Basic5/Basic5.cpp @@ -0,0 +1,22 @@ + +/* + * + * Copyright (c) 2023 Aurélien Labrosse + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include + +Player *Basic5::getCurrentPlayer() { + return &this->player; +} diff --git a/lib/Domain/Game/Basic5/Basic5.hpp b/lib/Domain/Game/Basic5/Basic5.hpp new file mode 100644 index 0000000..a4a3a7e --- /dev/null +++ b/lib/Domain/Game/Basic5/Basic5.hpp @@ -0,0 +1,46 @@ +/* + * + * Copyright (c) 2023 Aurélien Labrosse + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once + +#include +#include + +/** + * This game has a single player, who have unlimited + * ammunition to shot the five targets. + */ +class Basic5 : public IGame { + + IGameUi *ui; + Player player; + +public: + Basic5(IGameUi *ui) : player(0) { this->ui = ui; } + + ~Basic5() {} + + Player *getCurrentPlayer(); + + void restart() { + ui->restart(); + player.restart(); + } + + void changeCurrentPlayerTo(uint8_t playerId) { + // only one player + } +}; \ No newline at end of file diff --git a/lib/Domain/TargetHost/BTEGui.cpp b/lib/Domain/Game/Basic5/Basic5Ui.cpp similarity index 76% rename from lib/Domain/TargetHost/BTEGui.cpp rename to lib/Domain/Game/Basic5/Basic5Ui.cpp index c56c550..e2393c5 100644 --- a/lib/Domain/TargetHost/BTEGui.cpp +++ b/lib/Domain/Game/Basic5/Basic5Ui.cpp @@ -1,3 +1,4 @@ + /* * * Copyright (c) 2023 Aurélien Labrosse @@ -15,7 +16,7 @@ * along with this program. If not, see . */ -#include +#include #if not defined(NATIVE) #include @@ -23,41 +24,58 @@ static char stringBuffer[64]; +static const char *TARGET_TO_WHITE = "*%cR255G255B255*"; +static const char *TARGET_TO_BLACK = "**%cR0G0B0*"; + +void Basic5Ui::restart() { sendUIDescriptionToBluetoothElectronics(); } +void Basic5Ui::displayTarget(const ITarget &target) {} +void Basic5Ui::displayPlayer(const Player &player) {} + /** * Indirection to easy switch implementation * and does not pollute main code */ -void BTEGui::_output(const char *message) { -#if not defined(NATIVE) - Serial.println(stringBuffer); -#else +void Basic5Ui::_output(const char *message) { + +#if defined(NATIVE) out << message << "\n"; +#else + Serial.println(message); #endif } -void BTEGui::updateTarget(ITarget* target) { +void Basic5Ui::log(const char *message) { + sprintf(stringBuffer, "%s", message); + _output(stringBuffer); +} +void Basic5Ui::log(uint8_t value) { + sprintf(stringBuffer, "%u", value); + _output(stringBuffer); +} + +/* +void updateTarget(ITarget *target) { char letter = TARGET_APP_LETTERS[(uint8_t)target]; - sprintf(stringBuffer, "*%cR255G255B255*", letter); + sprintf(stringBuffer, TARGET_TO_WHITE, letter); _output(stringBuffer); } -void BTEGui::resetTargets() { - targetState = 0; +void resetTargets() { for (char letter : TARGET_APP_LETTERS) { - sprintf(stringBuffer, "*%cR0G0B0*", letter); + sprintf(stringBuffer, TARGET_TO_BLACK, letter); _output(stringBuffer); } } -void BTEGui::setCurrentPlayer(uint8_t playerId) { +void setCurrentPlayer(uint8_t playerId) { if (playerId < 4) { sprintf(stringBuffer, "*M%s*", PLAYER_COLORS[playerId]); _output(stringBuffer); } } -void BTEGui::displayPlayerInfo(const Player &player) { +void displayPlayerInfo(const Player &player) { uint8_t baseIndex = player.id * 2; char totalLetter = PLAYER_DATA_APP_LETTERS[baseIndex]; @@ -70,18 +88,10 @@ void BTEGui::displayPlayerInfo(const Player &player) { unsigned(player.getTotalHitCount())); _output(stringBuffer); } - -void BTEGui::log(const char *message) { - sprintf(stringBuffer, "%s", message); - _output(stringBuffer); -} -void BTEGui::log(uint8_t value) { - sprintf(stringBuffer, "%u", value); - _output(stringBuffer); -} +*/ #if not defined(NATIVE) -static void sendApplication() { +void sendUIDescriptionToBluetoothElectronics() { Serial.println(F("*.kwl")); Serial.println(F("clear_panel()")); Serial.println(F("set_grid_size(21,10)")); @@ -129,19 +139,16 @@ static void sendApplication() { Serial.println(F("*")); } #else -static void sendApplication() {} -#endif - -void BTEGui::restart() { - sendApplication(); - resetTargets(); +void Basic5Ui::sendUIDescriptionToBluetoothElectronics() { + _output("Sent UI description over serial link"); } +#endif -const char *BTEGui::PLAYER_COLORS[] = {"Yellow", "Green", "Red", "Blue"}; -const char BTEGui::TARGET_APP_LETTERS[5] = {'A', 'Z', 'E', 'R', 'T'}; -const char BTEGui::PLAYER_DATA_APP_LETTERS[8] = { +const char *Basic5Ui::PLAYER_COLORS[] = {"Yellow", "Green", "Red", "Blue"}; +const char Basic5Ui::TARGET_APP_LETTERS[5] = {'A', 'Z', 'E', 'R', 'T'}; +const char Basic5Ui::PLAYER_DATA_APP_LETTERS[8] = { 'Q', 'W', // player 1 'S', 'X', // player 2 'D', 'C', // player 3 'F', 'V' // player 4 -}; \ No newline at end of file +}; diff --git a/lib/Domain/TargetHost/BTEGui.hpp b/lib/Domain/Game/Basic5/Basic5Ui.hpp similarity index 77% rename from lib/Domain/TargetHost/BTEGui.hpp rename to lib/Domain/Game/Basic5/Basic5Ui.hpp index c24aa8a..4369a53 100644 --- a/lib/Domain/TargetHost/BTEGui.hpp +++ b/lib/Domain/Game/Basic5/Basic5Ui.hpp @@ -1,3 +1,4 @@ + /* * * Copyright (c) 2023 Aurélien Labrosse @@ -16,12 +17,13 @@ */ #pragma once -#include +#include +#include #include #include -#if not defined(AVR) +#if defined(NATIVE) #include #endif @@ -32,7 +34,7 @@ * Callbacks methods also allow to reset the UI to initial state. * */ -class BTEGui { +class Basic5Ui : public IGameUi { /* Bluetooth application uses a letter to identify which widget * shall use the received data. Theses are for targets representation @@ -56,19 +58,21 @@ class BTEGui { void _output(const char *message); + /** + * Send UI description for Bluetooth Electronics + * over the serial connection + */ + void sendUIDescriptionToBluetoothElectronics(); + public: -#if !defined(AVR) +#if defined(NATIVE) std::ostream &out; - explicit BTEGui(std::ostream &out) : out(out) {} + explicit Basic5Ui(std::ostream &out) : out(out) {} #endif - void setCurrentPlayer(const Player &player); - - void updateTarget(ITarget *target); - - void displayPlayerInfo(const Player &player); - - void restart(); - void log(const char *); - void log(uint8_t value); + void displayTarget(const ITarget &target) override; + void displayPlayer(const Player &player) override; + void restart() override; + void log(const char *) override; + void log(uint8_t value) override; }; \ No newline at end of file diff --git a/lib/Domain/Game/IGame.hpp b/lib/Domain/Game/IGame.hpp new file mode 100644 index 0000000..0fc487c --- /dev/null +++ b/lib/Domain/Game/IGame.hpp @@ -0,0 +1,30 @@ +/* + * + * Copyright (c) 2023 Aurélien Labrosse + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once + +#include +/** + * Generic interface for all games + */ +class IGame { +public: + virtual ~IGame(){}; + + virtual Player *getCurrentPlayer() = 0; + virtual void restart() = 0; + virtual void changeCurrentPlayerTo(uint8_t playerId) = 0; +}; \ No newline at end of file diff --git a/lib/Domain/Game/IGameUi.hpp b/lib/Domain/Game/IGameUi.hpp new file mode 100644 index 0000000..16e7642 --- /dev/null +++ b/lib/Domain/Game/IGameUi.hpp @@ -0,0 +1,31 @@ +/* + * + * Copyright (c) 2023 Aurélien Labrosse + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once + +#include +#include +#include + +class IGameUi { +public: + virtual ~IGameUi(){}; + virtual void displayTarget(const ITarget &target) = 0; + virtual void displayPlayer(const Player &player) = 0; + virtual void restart() = 0; + virtual void log(const char *) = 0; + virtual void log(uint8_t value) = 0; +}; \ No newline at end of file diff --git a/lib/Domain/Player.cpp b/lib/Domain/Game/Player.cpp similarity index 95% rename from lib/Domain/Player.cpp rename to lib/Domain/Game/Player.cpp index 1e5977d..3fb5670 100644 --- a/lib/Domain/Player.cpp +++ b/lib/Domain/Game/Player.cpp @@ -14,10 +14,10 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -#include +#include Player::Player(uint8_t id) : id(id), currentRound(0), totalShoots(0) { - reset(); + restart(); } uint8_t Player::getTotalHitCount() const { @@ -28,7 +28,7 @@ uint8_t Player::getTotalHitCount() const { return accumulator; } -void Player::reset() { +void Player::restart() { // Note: this loop uses less asm code than memset() (release -Os) for (uint8_t index = 0; index < ROUND_COUNT; index++) { hit[index] = 0; diff --git a/lib/Domain/Player.hpp b/lib/Domain/Game/Player.hpp similarity index 98% rename from lib/Domain/Player.hpp rename to lib/Domain/Game/Player.hpp index 9027a57..6e724e5 100644 --- a/lib/Domain/Player.hpp +++ b/lib/Domain/Game/Player.hpp @@ -36,7 +36,7 @@ class Player { uint8_t getTotalHitCount() const; - void reset(); + void restart(); void startRound(); void endRound(); void recordSucceededShoot(); diff --git a/lib/Domain/Target/LDRTarget.cpp b/lib/Domain/Target/LDRTarget.cpp index d3a163c..f0ed040 100644 --- a/lib/Domain/Target/LDRTarget.cpp +++ b/lib/Domain/Target/LDRTarget.cpp @@ -18,8 +18,8 @@ #include LDRTarget::LDRTarget(uint8_t luminosityPin) - : ambiantLuminosity(0), luminosityPin(luminosityPin), state(Ready), - threshold(0) { + : ambiantLuminosity(0), luminosityPin(luminosityPin), + state(ITarget::State::Ready), threshold(0) { state = ITarget::State::Activated; } @@ -28,7 +28,7 @@ void LDRTarget::post(ITarget::Event event) { nextEvent = event; } void LDRTarget::update() { - if (nextEvent != NoEvent) { + if (nextEvent != ITarget::Event::NoEvent) { switch (state) { @@ -55,14 +55,14 @@ void LDRTarget::update() { onReady(); } } - } - - if (nextEvent == ITarget::Event::Error) { + + case ITarget::State::Error: { onError(); } + } // consumed or ignored - nextEvent = NoEvent; + nextEvent = ITarget::Event::NoEvent; } else { if (getLuminosity() > (ambiantLuminosity + threshold)) { post(ITarget::Event::Shoot); @@ -89,4 +89,4 @@ void LDRTarget::onCalibrate() { post(ITarget::Event::Calibrated); } -void LDRTarget::setThreshold(uint8_t threshold) { this->threshold = threshold; } +void LDRTarget::setThreshold(uint8_t threshold) { this->threshold = threshold; } \ No newline at end of file diff --git a/lib/Domain/TargetHost/ITargetHost.hpp b/lib/Domain/TargetHost/ITargetHost.hpp index e20f90b..1357b8b 100644 --- a/lib/Domain/TargetHost/ITargetHost.hpp +++ b/lib/Domain/TargetHost/ITargetHost.hpp @@ -20,7 +20,7 @@ #include /** - * @brief Target host is a device managing 5 targets + * @brief Target host manages 5 targets and a IGame implementation * * A target is considered hit if its luminosity value exceed (ambientValue + * threshold) A target has a multicolor led: Off is ready, green is shot, blue @@ -32,7 +32,7 @@ class ITargetHost { public: virtual ~ITargetHost() {} - virtual void reset() = 0; + virtual void restart() = 0; /** * Callback for system clock diff --git a/lib/Domain/TargetHost/TargetHost.cpp b/lib/Domain/TargetHost/TargetHost.cpp index af9311a..5656c2d 100644 --- a/lib/Domain/TargetHost/TargetHost.cpp +++ b/lib/Domain/TargetHost/TargetHost.cpp @@ -37,6 +37,12 @@ static const uint8_t TARGET_COUNT = 5; LDRTarget _targets[TARGET_COUNT] = {LDRTarget(A0), LDRTarget(A1), LDRTarget(A2), LDRTarget(A3), LDRTarget(A4)}; +TargetHost::TargetHost(IGame *game) { + + this->game = game; + thresholdCache = 0; +} + /** * Fallback method called when garbage command * is received on the serial port @@ -88,7 +94,7 @@ void TargetHost::setup() { _serial_commands.AddCommand(&serial_cmd_changePlayer); _serial_commands.AddCommand(&serial_cmd_reset); - ui->restart(); + this->game->restart(); } void TargetHost::update() { @@ -108,8 +114,7 @@ void serial_cmd_unrecognized_callback(SerialCommands *sender, const char *cmd) { void serial_cmd_reset_callback(SerialCommands *sender) { TargetHost *targetHost = static_cast(sender->context); - targetHost->game->reset(); - targetHost->ui->restart(); + targetHost->game->restart(); } void serial_cmd_setThreshold_callback(SerialCommands *sender) { @@ -119,9 +124,8 @@ void serial_cmd_setThreshold_callback(SerialCommands *sender) { if (value > 0 && value != targetHost->getThreshold()) { targetHost->storeThreshold(value); - targetHost->ui->log((const char *)F("Thrsh: ")); - targetHost->ui->log(value); - targetHost->ui->log("\n"); + sender->GetSerial()->print("Thrsh: "); + sender->GetSerial()->println(value); } } @@ -131,12 +135,4 @@ void serial_cmd_changePlayer_callback(SerialCommands *sender) { targetHost->game->changeCurrentPlayerTo(playerId); } -void TargetHost::reset() { ui->resetTargets(); } - -TargetHost::TargetHost(Game *game, BTEGui *ui) { - - this->game = game; - this->ui = ui; - - thresholdCache = 0; -} \ No newline at end of file +void TargetHost::restart() { game->restart(); } \ No newline at end of file diff --git a/lib/Domain/TargetHost/TargetHost.hpp b/lib/Domain/TargetHost/TargetHost.hpp index bc5cc35..2823b58 100644 --- a/lib/Domain/TargetHost/TargetHost.hpp +++ b/lib/Domain/TargetHost/TargetHost.hpp @@ -16,10 +16,10 @@ */ #pragma once -#include +#include #include -#include #include + #include /** @@ -37,6 +37,14 @@ class TargetHost : public ITargetHost { uint8_t thresholdCache; public: + IGame *game; + + TargetHost(IGame *game); + void ledOn() override; + void ledOff() override; + void restart() override; + void update() override; + void storeThreshold(uint8_t threshold); /** @@ -46,17 +54,5 @@ class TargetHost : public ITargetHost { uint8_t getThreshold(); - Game *game; - BTEGui *ui; - - TargetHost(Game *game, BTEGui *ui); - - void ledOn() override; - void ledOff() override; - void reset() override; - void update() override; - - /* Specific methods */ - void setup(); }; \ No newline at end of file diff --git a/src/ConsoleTargets.cpp b/src/ConsoleTargets.cpp index 680f856..e5fba55 100644 --- a/src/ConsoleTargets.cpp +++ b/src/ConsoleTargets.cpp @@ -18,9 +18,10 @@ #include #include -#include -#include +#include +#include #include +#include void printLine() { std::cout << std::endl @@ -30,15 +31,16 @@ void printLine() { int main() { std::cout << "Console Application" << std::endl; - BTEGui gui(std::cout); - Game game; - TargetHost host(&game, &gui); + Basic5Ui ui(std::cout); + Basic5 game(&ui); + TargetHost host(&game); + + game.restart(); printLine(); - gui.displayPlayerInfo(*game.currentPlayer); std::cout << "Type 'T' to fire a system tick" << std::endl; - std::cout << "Type 'N' to switch to next player" << std::endl; + // std::cout << "Type 'N' to switch to next player" << std::endl; std::cout << "Type 0,1,2,3 or 4 to hit a target" << std::endl; std::cout << "Type 'R' to reset the target host" << std::endl; std::cout << "Type 'Q' to quit game" << std::endl; @@ -52,20 +54,22 @@ int main() { host.update(); } else if (str == "N") { - game.nextRound(); - printLine(); - std::cout << "Player " << game.currentPlayer->id + 1 - << " turn! ( global round " - << (game.currentRound / Game::PLAYER_COUNT) << ")" << std::endl; + + } else if (str == "0") { + + } else if (str == "1") { + + } else if (str == "2") { + + } else if (str == "3") { + + } else if (str == "4") { } else if (str == "R") { - host.reset(); + host.restart(); } else if (str == "Q") { - printLine(); - for (auto player : game.players) { - gui.displayPlayerInfo(player); - } + exit(0); } } } \ No newline at end of file diff --git a/src/TargetApp.cpp b/src/TargetApp.cpp index a812fc5..e0464a7 100644 --- a/src/TargetApp.cpp +++ b/src/TargetApp.cpp @@ -16,17 +16,18 @@ */ #include -#include -#include -#include +#include +#include +#include -BTEGui gui; -Game game(&gui); -TargetHost host(&game, &gui); + +Basic5Ui ui; +Basic5 game(&ui); +TargetHost host(&game); void setup() { host.setup(); - game.reset(); + game.restart(); } void loop() { host.update(); } \ No newline at end of file