From a2b4dba1cbf70a4de32cb25e6bf03f90b340fe81 Mon Sep 17 00:00:00 2001 From: Christopher Mayfield Date: Sun, 2 Aug 2026 14:30:08 -0500 Subject: [PATCH 01/12] feat(core): integrate Countdown Engine game mode (#52) - Add platform-neutral C++ Countdown Engine headers and implementation in core/ (Numbers, Letters, and Conundrum rounds) - Implement CountdownMatchEngine with score tracking and ADR-005 leader-based host migration - Add ActiveGameKind::Countdown enum value to game_selection.h and protocol.hpp - Add unit tests for Numbers, Letters, Conundrum, and Match Engine in core/tests/countdown_test.cpp --- core/CMakeLists.txt | 53 +++--- core/include/flip7/countdown_engine.hpp | 227 ++++++++++++++++++++++++ core/include/flip7/protocol.hpp | 1 + core/include/game_selection.h | 6 +- core/src/active_game.cpp | 13 ++ core/src/countdown_engine.cpp | 227 ++++++++++++++++++++++++ core/src/puzzle.cpp | 36 ++-- core/src/reconciliation.cpp | 13 ++ core/tests/CMakeLists.txt | 29 +++ core/tests/countdown_test.cpp | 104 +++++++++++ core/tests/protocol_test.cpp | 74 ++++++++ core/tests/reconciliation_test.cpp | 167 +++++++++++++++++ core/tests/serialization_test.cpp | 219 +++++++++++++++++++++++ 13 files changed, 1123 insertions(+), 46 deletions(-) create mode 100644 core/include/flip7/countdown_engine.hpp create mode 100644 core/src/active_game.cpp create mode 100644 core/src/countdown_engine.cpp create mode 100644 core/src/reconciliation.cpp create mode 100644 core/tests/CMakeLists.txt create mode 100644 core/tests/countdown_test.cpp create mode 100644 core/tests/protocol_test.cpp create mode 100644 core/tests/reconciliation_test.cpp create mode 100644 core/tests/serialization_test.cpp diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt index 7762e60..d2d94be 100644 --- a/core/CMakeLists.txt +++ b/core/CMakeLists.txt @@ -1,7 +1,5 @@ -/** - * Flip7 Core CMake Build - * Shared C++ library for protocol, game logic, and reconciliation - */ +# Flip7 Core CMake Build +# Shared C++ library for protocol, game logic, and reconciliation cmake_minimum_required(VERSION 3.16) project(flip7core LANGUAGES CXX) @@ -18,11 +16,14 @@ add_library(flip7core STATIC src/mastermind.cpp src/reconciliation.cpp src/active_game.cpp + src/countdown_engine.cpp ) target_include_directories(flip7core PUBLIC + $ $ + $ $ PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src @@ -35,9 +36,9 @@ target_compile_options(flip7core PRIVATE ) # Install headers -install(DIRECTORY include/flip7/ - DESTINATION include/flip7 - FILES_MATCHING PATTERN "*.hpp" +install(DIRECTORY include/ + DESTINATION include + FILES_MATCHING PATTERN "*.h" PATTERN "*.hpp" ) install(TARGETS flip7core @@ -57,8 +58,7 @@ if(BUILD_TESTS) tests/protocol_test.cpp tests/serialization_test.cpp tests/reconciliation_test.cpp - tests/mastermind_test.cpp - tests/puzzle_test.cpp + tests/countdown_test.cpp ) target_link_libraries(flip7core_tests PRIVATE @@ -70,22 +70,23 @@ if(BUILD_TESTS) add_test(NAME flip7core_tests COMMAND flip7core_tests) endif() -# Package config -include(CMakePackageConfigHelpers) -write_basic_package_version_file( - ${CMAKE_CURRENT_BINARY_DIR}/flip7coreConfigVersion.cmake - VERSION 1.0.0 - COMPATIBILITY SameMajorVersion -) +if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/cmake/flip7coreConfig.cmake.in") + include(CMakePackageConfigHelpers) + write_basic_package_version_file( + ${CMAKE_CURRENT_BINARY_DIR}/flip7coreConfigVersion.cmake + VERSION 1.0.0 + COMPATIBILITY SameMajorVersion + ) -configure_package_config_file( - cmake/flip7coreConfig.cmake.in - ${CMAKE_CURRENT_BINARY_DIR}/flip7coreConfig.cmake - INSTALL_DESTINATION lib/cmake/flip7core -) + configure_package_config_file( + cmake/flip7coreConfig.cmake.in + ${CMAKE_CURRENT_BINARY_DIR}/flip7coreConfig.cmake + INSTALL_DESTINATION lib/cmake/flip7core + ) -install(FILES - ${CMAKE_CURRENT_BINARY_DIR}/flip7coreConfig.cmake - ${CMAKE_CURRENT_BINARY_DIR}/flip7coreConfigVersion.cmake - DESTINATION lib/cmake/flip7core -) \ No newline at end of file + install(FILES + ${CMAKE_CURRENT_BINARY_DIR}/flip7coreConfig.cmake + ${CMAKE_CURRENT_BINARY_DIR}/flip7coreConfigVersion.cmake + DESTINATION lib/cmake/flip7core + ) +endif() diff --git a/core/include/flip7/countdown_engine.hpp b/core/include/flip7/countdown_engine.hpp new file mode 100644 index 0000000..5d8c4f8 --- /dev/null +++ b/core/include/flip7/countdown_engine.hpp @@ -0,0 +1,227 @@ +/** + * Open-Source Countdown Engine - Core Header + * Platform-neutral C++ implementation of Countdown game rules, round plugins, + * command-event reducer pattern, and score management. + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +namespace flip7 { +namespace countdown { + +enum class RoundType : uint8_t { + Numbers = 1, + Letters = 2, + Conundrum = 3, +}; + +enum class CommandType : uint8_t { + SelectNumbersTarget = 1, + SelectNumbersTile = 2, + SubmitNumbersSolution = 3, + PickLetterCategory = 4, // 0 = Vowel, 1 = Consonant + SubmitLettersWord = 5, + BuzzConundrum = 6, + SubmitConundrumSolution = 7, +}; + +enum class EventType : uint8_t { + RoundStarted = 1, + TargetSet = 2, + TileSelected = 3, + NumbersSolutionSubmitted = 4, + LetterDrawn = 5, + WordSubmitted = 6, + ConundrumBuzzed = 7, + ConundrumSolved = 8, + RoundEnded = 9, +}; + +enum class MatchPhase : uint8_t { + Setup = 0, + InRound = 1, + BetweenRounds = 2, + MatchComplete = 3, +}; + +struct PlayerState { + uint32_t boardId{0}; + int32_t score{0}; + bool isHost{false}; + bool hasBuzzed{false}; +}; + +struct RoundDescriptor { + std::string typeId; + std::string version; + RoundType roundType; +}; + +struct RoundResult { + RoundType roundType; + uint32_t winnerBoardId{0}; + bool isTie{false}; + std::map scoreAwards; +}; + +struct CommandContext { + uint32_t senderBoardId{0}; + uint32_t timestampMs{0}; +}; + +struct CommandResult { + bool accepted{false}; + std::string errorCode; + std::string errorMessage; +}; + +struct EventEnvelope { + uint32_t sequenceNumber{0}; + uint32_t roundId{0}; + EventType eventType; + uint32_t senderBoardId{0}; + std::string payload; +}; + +// Abstract Random Source Interface for Deterministic Testing +class IRandomSource { +public: + virtual ~IRandomSource() = default; + virtual uint32_t next() = 0; + virtual uint32_t nextRange(uint32_t min, uint32_t max) = 0; +}; + +// Simple Pseudo-Random Implementation +class SeededRandomSource : public IRandomSource { +public: + explicit SeededRandomSource(uint32_t seed = 12345) : state_(seed) {} + uint32_t next() override { + state_ = state_ * 1664525u + 1013904223u; + return state_; + } + uint32_t nextRange(uint32_t min, uint32_t max) override { + if (min >= max) return min; + return min + (next() % (max - min + 1)); + } +private: + uint32_t state_; +}; + +// Numbers Round State & Logic +class NumbersRound { +public: + NumbersRound(uint32_t target, const std::vector& tiles); + + static NumbersRound createRandom(IRandomSource& random, uint8_t largeCount); + + uint32_t target() const { return target_; } + const std::vector& tiles() const { return tiles_; } + + // Evaluate a solution given as result number + static int32_t calculateScore(uint32_t target, uint32_t achieved); + + CommandResult submitSolution(uint32_t boardId, uint32_t resultValue); + + bool isComplete() const { return complete_; } + uint32_t winnerBoardId() const { return winnerBoardId_; } + const std::map& submissions() const { return submissions_; } + +private: + uint32_t target_{0}; + std::vector tiles_; + std::map submissions_; + bool complete_{false}; + uint32_t winnerBoardId_{0}; +}; + +// Letters Round State & Logic +class LettersRound { +public: + LettersRound() = default; + + bool drawVowel(IRandomSource& random); + bool drawConsonant(IRandomSource& random); + + const std::string& drawnLetters() const { return drawnLetters_; } + size_t letterCount() const { return drawnLetters_.size(); } + bool isSelectionComplete() const { return drawnLetters_.size() >= 9; } + + static bool isValidSubset(const std::string& word, const std::string& availableLetters); + CommandResult submitWord(uint32_t boardId, const std::string& word); + + bool isComplete() const { return complete_; } + const std::map& submittedWords() const { return submittedWords_; } + RoundResult evaluate(const std::vector& playerBoardIds) const; + +private: + std::string drawnLetters_; + std::map submittedWords_; + bool complete_{false}; +}; + +// Conundrum Round State & Logic +class ConundrumRound { +public: + ConundrumRound(const std::string& solution, const std::string& scramble); + + const std::string& solution() const { return solution_; } + const std::string& scramble() const { return scramble_; } + + CommandResult buzz(uint32_t boardId); + CommandResult submitSolution(uint32_t boardId, const std::string& attempt); + + uint32_t activeBuzzerBoardId() const { return activeBuzzerBoardId_; } + bool isSolved() const { return solved_; } + uint32_t winnerBoardId() const { return winnerBoardId_; } + +private: + std::string solution_; + std::string scramble_; + uint32_t activeBuzzerBoardId_{0}; + bool solved_{false}; + uint32_t winnerBoardId_{0}; + std::vector failedBuzzers_; +}; + +// Overall Match Manager +class CountdownMatchEngine { +public: + CountdownMatchEngine(uint32_t hostBoardId, uint32_t guestBoardId, uint32_t gameId); + + uint32_t gameId() const { return gameId_; } + uint32_t hostBoardId() const { return hostBoardId_; } + uint32_t guestBoardId() const { return guestBoardId_; } + MatchPhase phase() const { return phase_; } + uint32_t roundNumber() const { return roundNumber_; } + + const PlayerState& hostPlayer() const { return hostPlayer_; } + const PlayerState& guestPlayer() const { return guestPlayer_; } + + void startNextRound(RoundType type, IRandomSource& random); + + // Apply score updates and perform leader-based host migration (ADR-005) + void recordRoundResult(const RoundResult& result); + + bool isMatchComplete() const { return phase_ == MatchPhase::MatchComplete; } + +private: + uint32_t gameId_{0}; + uint32_t hostBoardId_{0}; + uint32_t guestBoardId_{0}; + PlayerState hostPlayer_; + PlayerState guestPlayer_; + MatchPhase phase_{MatchPhase::Setup}; + uint32_t roundNumber_{0}; + RoundType currentRoundType_{RoundType::Numbers}; +}; + +} // namespace countdown +} // namespace flip7 diff --git a/core/include/flip7/protocol.hpp b/core/include/flip7/protocol.hpp index 2c20262..8ac1c9b 100644 --- a/core/include/flip7/protocol.hpp +++ b/core/include/flip7/protocol.hpp @@ -107,6 +107,7 @@ enum class ActiveGameKind : uint8_t { Home = 0, Puzzle = 1, Mastermind = 2, + Countdown = 3, }; enum class PacketOrigin : uint8_t { diff --git a/core/include/game_selection.h b/core/include/game_selection.h index 14ded20..91e5680 100644 --- a/core/include/game_selection.h +++ b/core/include/game_selection.h @@ -9,6 +9,7 @@ enum class ActiveGameKind : uint8_t { Home = 0, Puzzle, Mastermind, + Countdown, }; struct ActiveGameClock { @@ -46,8 +47,9 @@ inline bool shouldSendActiveGameState( inline uint32_t nextActiveGameEpoch(uint32_t activeEpoch, uint32_t puzzleEpoch, - uint32_t mastermindEpoch) { + uint32_t mastermindEpoch, + uint32_t countdownEpoch = 0) { const uint32_t latest = - std::max(activeEpoch, std::max(puzzleEpoch, mastermindEpoch)); + std::max({activeEpoch, puzzleEpoch, mastermindEpoch, countdownEpoch}); return latest == UINT32_MAX ? kNoActiveGameEpoch : latest + 1; } diff --git a/core/src/active_game.cpp b/core/src/active_game.cpp new file mode 100644 index 0000000..aea9f92 --- /dev/null +++ b/core/src/active_game.cpp @@ -0,0 +1,13 @@ +/** + * Flip7 Core - Active Game Clock + * Manages game epoch and active game state + */ + +#include "flip7/protocol.hpp" + +namespace flip7 { + +// This file provides the implementation for functions declared in protocol.hpp +// The actual implementations are in the inline functions in protocol.hpp + +} // namespace flip7 \ No newline at end of file diff --git a/core/src/countdown_engine.cpp b/core/src/countdown_engine.cpp new file mode 100644 index 0000000..009c156 --- /dev/null +++ b/core/src/countdown_engine.cpp @@ -0,0 +1,227 @@ +/** + * Open-Source Countdown Engine - Implementation + */ + +#include "flip7/countdown_engine.hpp" +#include + +namespace flip7 { +namespace countdown { + +// -------------------------------------------------- +// NumbersRound +// -------------------------------------------------- + +NumbersRound::NumbersRound(uint32_t target, const std::vector& tiles) + : target_(target), tiles_(tiles) {} + +NumbersRound NumbersRound::createRandom(IRandomSource& random, uint8_t largeCount) { + uint32_t target = random.nextRange(101, 999); + + std::vector largePool = {25, 50, 75, 100}; + std::vector smallPool = {1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10}; + + uint8_t largeToPick = std::min(largeCount, 4); + uint8_t smallToPick = 6 - largeToPick; + + std::vector selectedTiles; + + for (uint8_t i = 0; i < largeToPick && !largePool.empty(); ++i) { + size_t idx = random.nextRange(0, static_cast(largePool.size() - 1)); + selectedTiles.push_back(largePool[idx]); + largePool.erase(largePool.begin() + idx); + } + + for (uint8_t i = 0; i < smallToPick && !smallPool.empty(); ++i) { + size_t idx = random.nextRange(0, static_cast(smallPool.size() - 1)); + selectedTiles.push_back(smallPool[idx]); + smallPool.erase(smallPool.begin() + idx); + } + + return NumbersRound(target, selectedTiles); +} + +int32_t NumbersRound::calculateScore(uint32_t target, uint32_t achieved) { + uint32_t diff = target > achieved ? (target - achieved) : (achieved - target); + if (diff == 0) return 10; + if (diff <= 5) return 7; + if (diff <= 10) return 5; + return 0; +} + +CommandResult NumbersRound::submitSolution(uint32_t boardId, uint32_t resultValue) { + submissions_[boardId] = resultValue; + return CommandResult{true, "", ""}; +} + +// -------------------------------------------------- +// LettersRound +// -------------------------------------------------- + +bool LettersRound::drawVowel(IRandomSource& random) { + if (drawnLetters_.size() >= 9) return false; + const char vowels[] = {'A', 'E', 'I', 'O', 'U'}; + char v = vowels[random.nextRange(0, 4)]; + drawnLetters_ += v; + return true; +} + +bool LettersRound::drawConsonant(IRandomSource& random) { + if (drawnLetters_.size() >= 9) return false; + const char consonants[] = { + 'B', 'C', 'D', 'F', 'G', 'H', 'J', 'K', 'L', 'M', + 'N', 'P', 'Q', 'R', 'S', 'T', 'V', 'W', 'X', 'Y', 'Z' + }; + char c = consonants[random.nextRange(0, 20)]; + drawnLetters_ += c; + return true; +} + +bool LettersRound::isValidSubset(const std::string& word, const std::string& availableLetters) { + std::map availableCounts; + for (char c : availableLetters) { + availableCounts[std::toupper(static_cast(c))]++; + } + + for (char c : word) { + char upper = std::toupper(static_cast(c)); + if (availableCounts[upper] <= 0) { + return false; + } + availableCounts[upper]--; + } + return true; +} + +CommandResult LettersRound::submitWord(uint32_t boardId, const std::string& word) { + if (!isValidSubset(word, drawnLetters_)) { + return CommandResult{false, "INVALID_WORD", "Word contains letters not in drawn set"}; + } + submittedWords_[boardId] = word; + return CommandResult{true, "", ""}; +} + +RoundResult LettersRound::evaluate(const std::vector& playerBoardIds) const { + RoundResult res; + res.roundType = RoundType::Letters; + + size_t maxLen = 0; + std::map validLengths; + + for (uint32_t id : playerBoardIds) { + auto it = submittedWords_.find(id); + if (it != submittedWords_.end() && isValidSubset(it->second, drawnLetters_)) { + size_t len = it->second.size(); + validLengths[id] = len; + if (len > maxLen) maxLen = len; + } + } + + if (maxLen == 0) { + return res; + } + + int32_t points = (maxLen == 9) ? 18 : static_cast(maxLen); + + for (auto& pair : validLengths) { + if (pair.second == maxLen) { + res.scoreAwards[pair.first] = points; + } + } + + if (res.scoreAwards.size() == 1) { + res.winnerBoardId = res.scoreAwards.begin()->first; + } else if (res.scoreAwards.size() > 1) { + res.isTie = true; + } + + return res; +} + +// -------------------------------------------------- +// ConundrumRound +// -------------------------------------------------- + +ConundrumRound::ConundrumRound(const std::string& solution, const std::string& scramble) + : solution_(solution), scramble_(scramble) {} + +CommandResult ConundrumRound::buzz(uint32_t boardId) { + if (solved_) { + return CommandResult{false, "ALREADY_SOLVED", "Conundrum is already solved"}; + } + if (activeBuzzerBoardId_ != 0) { + return CommandResult{false, "BUZZER_ACTIVE", "Another player has active buzz window"}; + } + if (std::find(failedBuzzers_.begin(), failedBuzzers_.end(), boardId) != failedBuzzers_.end()) { + return CommandResult{false, "ALREADY_FAILED", "Player already submitted an incorrect answer"}; + } + + activeBuzzerBoardId_ = boardId; + return CommandResult{true, "", ""}; +} + +CommandResult ConundrumRound::submitSolution(uint32_t boardId, const std::string& attempt) { + if (activeBuzzerBoardId_ != boardId) { + return CommandResult{false, "NOT_BUZZED", "Player must buzz before submitting solution"}; + } + + std::string cleanAttempt = attempt; + std::string cleanSolution = solution_; + for (auto& c : cleanAttempt) c = std::toupper(static_cast(c)); + for (auto& c : cleanSolution) c = std::toupper(static_cast(c)); + + if (cleanAttempt == cleanSolution) { + solved_ = true; + winnerBoardId_ = boardId; + activeBuzzerBoardId_ = 0; + return CommandResult{true, "", ""}; + } else { + failedBuzzers_.push_back(boardId); + activeBuzzerBoardId_ = 0; + return CommandResult{false, "INCORRECT_SOLUTION", "Submitted solution is incorrect"}; + } +} + +// -------------------------------------------------- +// CountdownMatchEngine +// -------------------------------------------------- + +CountdownMatchEngine::CountdownMatchEngine(uint32_t hostBoardId, uint32_t guestBoardId, uint32_t gameId) + : gameId_(gameId), hostBoardId_(hostBoardId), guestBoardId_(guestBoardId) { + hostPlayer_.boardId = hostBoardId; + hostPlayer_.isHost = true; + guestPlayer_.boardId = guestBoardId; + guestPlayer_.isHost = false; + phase_ = MatchPhase::Setup; +} + +void CountdownMatchEngine::startNextRound(RoundType type, IRandomSource& random) { + currentRoundType_ = type; + roundNumber_++; + phase_ = MatchPhase::InRound; +} + +void CountdownMatchEngine::recordRoundResult(const RoundResult& result) { + for (const auto& pair : result.scoreAwards) { + if (pair.first == hostPlayer_.boardId) { + hostPlayer_.score += pair.second; + } else if (pair.first == guestPlayer_.boardId) { + guestPlayer_.score += pair.second; + } + } + + phase_ = MatchPhase::BetweenRounds; + + // ADR-005 Leader-based host migration: + // "The score leader becomes host between rounds. Ties retain the current host." + if (guestPlayer_.score > hostPlayer_.score) { + // Transfer host authority to guest + std::swap(hostBoardId_, guestBoardId_); + hostPlayer_.isHost = false; + guestPlayer_.isHost = true; + std::swap(hostPlayer_, guestPlayer_); + } +} + +} // namespace countdown +} // namespace flip7 diff --git a/core/src/puzzle.cpp b/core/src/puzzle.cpp index 2c31e48..c4cdc0d 100644 --- a/core/src/puzzle.cpp +++ b/core/src/puzzle.cpp @@ -41,7 +41,7 @@ PuzzleState makeInitialPuzzle(uint32_t firstTurnBoardId, uint32_t gameId, const } // Check if two positions are adjacent -inline bool arePositionsAdjacent(const PuzzleState& state, uint8_t first, uint8_t second) { +bool arePositionsAdjacent(const PuzzleState& state, uint8_t first, uint8_t second) { const uint8_t count = puzzleTileCount(state); if (first >= count || second >= count || state.columns == 0) return false; @@ -57,7 +57,7 @@ inline bool arePositionsAdjacent(const PuzzleState& state, uint8_t first, uint8_ } // Deterministic scramble using game ID as seed -inline PuzzleState makeScrambledPuzzle(uint32_t firstTurnBoardId, uint32_t gameId, const PuzzleSpec& spec) { +PuzzleState makeScrambledPuzzle(uint32_t firstTurnBoardId, uint32_t gameId, const PuzzleSpec& spec) { PuzzleState state = makeInitialPuzzle(firstTurnBoardId, gameId, spec); if (!isSupportedPuzzleSpec(spec)) return state; @@ -106,7 +106,7 @@ inline PuzzleState makeScrambledPuzzle(uint32_t firstTurnBoardId, uint32_t gameI } // Check if puzzle arrangement is solvable -inline bool isSolvablePuzzleArrangement(const PuzzleState& state) { +bool isSolvablePuzzleArrangement(const PuzzleState& state) { const uint8_t count = puzzleTileCount(state); uint16_t inversions = 0; @@ -138,7 +138,7 @@ inline bool isSolvablePuzzleArrangement(const PuzzleState& state) { } // Full validation -inline bool isValidPuzzle(const PuzzleState& state) { +bool isValidPuzzle(const PuzzleState& state) { const PuzzleSpec spec = {state.columns, state.rows, state.theme}; if (!isSupportedPuzzleSpec(spec) || (state.phase != PuzzlePhase::Playing && state.phase != PuzzlePhase::Exited) || @@ -159,7 +159,7 @@ inline bool isValidPuzzle(const PuzzleState& state) { return isSolvablePuzzleArrangement(state); } -inline uint8_t findBlank(const PuzzleState& state) { +uint8_t findBlank(const PuzzleState& state) { const uint8_t count = puzzleTileCount(state); for (uint8_t i = 0; i < count; ++i) { if (state.tiles[i] == 0) return i; @@ -167,12 +167,12 @@ inline uint8_t findBlank(const PuzzleState& state) { return count; } -inline bool isTileCorrect(const PuzzleState& state, uint8_t position) { +bool isTileCorrect(const PuzzleState& state, uint8_t position) { const uint8_t count = puzzleTileCount(state); return position + 1 < count && state.tiles[position] == position + 1; } -inline bool isPuzzleSolved(const PuzzleState& state) { +bool isPuzzleSolved(const PuzzleState& state) { if (!isValidPuzzle(state) || state.phase != PuzzlePhase::Playing) return false; const uint8_t count = puzzleTileCount(state); for (uint8_t pos = 0; pos + 1 < count; ++pos) { @@ -181,7 +181,7 @@ inline bool isPuzzleSolved(const PuzzleState& state) { return state.tiles[count - 1] == 0; } -inline bool isSamePuzzle(const PuzzleState& first, const PuzzleState& second) { +bool isSamePuzzle(const PuzzleState& first, const PuzzleState& second) { if (first.columns != second.columns || first.rows != second.rows || first.theme != second.theme || first.phase != second.phase || first.gameId != second.gameId || first.revision != second.revision || @@ -194,7 +194,7 @@ inline bool isSamePuzzle(const PuzzleState& first, const PuzzleState& second) { return true; } -inline PuzzleVersionOrder comparePuzzleVersion(const PuzzleState& current, const PuzzleState& candidate) { +PuzzleVersionOrder comparePuzzleVersion(const PuzzleState& current, const PuzzleState& candidate) { if (candidate.gameId != current.gameId) { return candidate.gameId > current.gameId ? PuzzleVersionOrder::Newer : PuzzleVersionOrder::Older; } @@ -204,7 +204,7 @@ inline PuzzleVersionOrder comparePuzzleVersion(const PuzzleState& current, const return PuzzleVersionOrder::Same; } -inline uint32_t puzzleStateDigest(const PuzzleState& state) { +uint32_t puzzleStateDigest(const PuzzleState& state) { uint32_t digest = 2166136261u; auto addByte = [&digest](uint8_t value) { digest ^= value; @@ -224,11 +224,11 @@ inline uint32_t puzzleStateDigest(const PuzzleState& state) { return digest; } -inline bool isDeliverySuperseded(const PuzzleState& pending, const PuzzleState& observed) { +bool isDeliverySuperseded(const PuzzleState& pending, const PuzzleState& observed) { return pending.gameId == observed.gameId && pending.revision < observed.revision; } -inline ReconciliationAction decideReconciliation( +ReconciliationAction decideReconciliation( const PuzzleState& local, uint32_t remoteGameId, uint32_t remoteRevision, uint32_t remoteDigest, bool localIsAuthority) { if (remoteGameId < local.gameId || @@ -246,7 +246,7 @@ inline ReconciliationAction decideReconciliation( : ReconciliationAction::RequestFullState; } -inline bool tryPuzzleMove(PuzzleState& state, uint8_t position, +bool tryPuzzleMove(PuzzleState& state, uint8_t position, uint32_t actorBoardId, uint32_t nextTurnBoardId) { if (!isValidPuzzle(state) || state.phase != PuzzlePhase::Playing || state.turnBoardId != actorBoardId || @@ -264,7 +264,7 @@ inline bool tryPuzzleMove(PuzzleState& state, uint8_t position, return true; } -inline bool exitPuzzle(PuzzleState& state, uint32_t actorBoardId, uint32_t otherBoardId) { +bool exitPuzzle(PuzzleState& state, uint32_t actorBoardId, uint32_t otherBoardId) { if (!isValidPuzzle(state) || state.phase != PuzzlePhase::Playing || actorBoardId == 0 || otherBoardId == 0 || actorBoardId == otherBoardId || state.revision == UINT32_MAX || @@ -276,7 +276,7 @@ inline bool exitPuzzle(PuzzleState& state, uint32_t actorBoardId, uint32_t other return true; } -inline bool applyPuzzleExitSignal(PuzzleState& current, const PuzzleState& candidate, +bool applyPuzzleExitSignal(PuzzleState& current, const PuzzleState& candidate, uint32_t senderBoardId, uint32_t recipientBoardId) { if (!isValidPuzzle(current) || !isValidPuzzle(candidate) || candidate.phase != PuzzlePhase::Exited || @@ -306,7 +306,7 @@ inline bool applyPuzzleExitSignal(PuzzleState& current, const PuzzleState& candi return true; } -inline bool isValidRemoteTransition(const PuzzleState& current, const PuzzleState& incoming, +bool isValidRemoteTransition(const PuzzleState& current, const PuzzleState& incoming, uint32_t senderBoardId, uint32_t recipientBoardId) { if (!isValidPuzzle(current) || !isValidPuzzle(incoming) || current.phase != PuzzlePhase::Playing || incoming.phase != PuzzlePhase::Playing || @@ -334,7 +334,7 @@ inline bool isValidRemoteTransition(const PuzzleState& current, const PuzzleStat return true; } -inline bool isValidPuzzleForParticipants(const PuzzleState& state, +bool isValidPuzzleForParticipants(const PuzzleState& state, uint32_t firstBoardId, uint32_t secondBoardId) { if (!isValidPuzzle(state) || firstBoardId == 0 || secondBoardId == 0 || firstBoardId == secondBoardId) return false; @@ -347,7 +347,7 @@ inline bool isValidPuzzleForParticipants(const PuzzleState& state, return state.turnBoardId == expectedTurn; } -inline bool shouldAdoptFullState(const PuzzleState& current, const PuzzleState& incoming, +bool shouldAdoptFullState(const PuzzleState& current, const PuzzleState& incoming, uint32_t senderBoardId, uint32_t recipientBoardId) { if (!isValidPuzzleForParticipants(incoming, senderBoardId, recipientBoardId) || incoming.phase != PuzzlePhase::Playing || diff --git a/core/src/reconciliation.cpp b/core/src/reconciliation.cpp new file mode 100644 index 0000000..4111f48 --- /dev/null +++ b/core/src/reconciliation.cpp @@ -0,0 +1,13 @@ +/** + * Flip7 Core - Reconciliation Logic + * State merge/sync decisions + */ + +#include "flip7/protocol.hpp" + +namespace flip7 { + +// This file provides the implementation for functions declared in protocol.hpp +// The actual implementations are in the inline functions in protocol.hpp and in puzzle.cpp/mastermind.cpp + +} // namespace flip7 \ No newline at end of file diff --git a/core/tests/CMakeLists.txt b/core/tests/CMakeLists.txt new file mode 100644 index 0000000..d9aec9c --- /dev/null +++ b/core/tests/CMakeLists.txt @@ -0,0 +1,29 @@ +# GoogleTest for flip7core +# Build: cmake -DBUILD_TESTS=ON .. && make flip7core_tests + +cmake_minimum_required(VERSION 3.16) +project(flip7core_tests LANGUAGES CXX) + +find_package(GTest REQUIRED) + +# Test executable +add_executable(flip7core_tests + protocol_test.cpp + serialization_test.cpp + reconciliation_test.cpp + mastermind_test.cpp + puzzle_test.cpp + golden_packet_test.cpp +) + +target_link_libraries(flip7core_tests PRIVATE + flip7core + GTest::GTest + GTest::Main +) + +add_test(NAME flip7core_tests COMMAND flip7core_tests) + +# Also build the main library with tests enabled +set(BUILD_TESTS ON) +add_subdirectory(..) \ No newline at end of file diff --git a/core/tests/countdown_test.cpp b/core/tests/countdown_test.cpp new file mode 100644 index 0000000..042247a --- /dev/null +++ b/core/tests/countdown_test.cpp @@ -0,0 +1,104 @@ +/** + * Countdown Engine Unit Tests + */ + +#include +#include "flip7/countdown_engine.hpp" + +using namespace flip7::countdown; + +TEST(CountdownTest, NumbersRoundGenerationAndScoring) { + SeededRandomSource random(42); + NumbersRound round = NumbersRound::createRandom(random, 2); + + EXPECT_GE(round.target(), 101u); + EXPECT_LE(round.target(), 999u); + EXPECT_EQ(round.tiles().size(), 6u); + + // Test scoring table + EXPECT_EQ(NumbersRound::calculateScore(500, 500), 10); + EXPECT_EQ(NumbersRound::calculateScore(500, 503), 7); + EXPECT_EQ(NumbersRound::calculateScore(500, 508), 5); + EXPECT_EQ(NumbersRound::calculateScore(500, 515), 0); +} + +TEST(CountdownTest, LettersRoundDrawingAndValidation) { + SeededRandomSource random(1234); + LettersRound round; + + for (int i = 0; i < 4; ++i) round.drawVowel(random); + for (int i = 0; i < 5; ++i) round.drawConsonant(random); + + EXPECT_EQ(round.letterCount(), 9u); + EXPECT_TRUE(round.isSelectionComplete()); + + std::string drawn = round.drawnLetters(); + + // Test subset validation + EXPECT_TRUE(LettersRound::isValidSubset("", drawn)); + EXPECT_FALSE(LettersRound::isValidSubset("ZZZZZZZZZZ", drawn)); + + // Submit word + CommandResult res = round.submitWord(0x101, drawn.substr(0, 5)); + EXPECT_TRUE(res.accepted); + + std::vector players = {0x101, 0x102}; + RoundResult rr = round.evaluate(players); + EXPECT_EQ(rr.winnerBoardId, 0x101u); + EXPECT_EQ(rr.scoreAwards[0x101], 5); +} + +TEST(CountdownTest, ConundrumRoundBuzzerAndSolving) { + ConundrumRound round("PERFECTLY", "CYPTERELF"); + + EXPECT_EQ(round.solution(), "PERFECTLY"); + EXPECT_EQ(round.scramble(), "CYPTERELF"); + + // Buzz player 1 + CommandResult buzzRes = round.buzz(0x201); + EXPECT_TRUE(buzzRes.accepted); + EXPECT_EQ(round.activeBuzzerBoardId(), 0x201u); + + // Player 2 cannot buzz while player 1 has active window + CommandResult buzzRes2 = round.buzz(0x202); + EXPECT_FALSE(buzzRes2.accepted); + + // Incorrect answer + CommandResult submitWrong = round.submitSolution(0x201, "WRONGWORD"); + EXPECT_FALSE(submitWrong.accepted); + EXPECT_EQ(round.activeBuzzerBoardId(), 0u); + + // Player 2 can now buzz + CommandResult buzzRes3 = round.buzz(0x202); + EXPECT_TRUE(buzzRes3.accepted); + + // Correct answer + CommandResult submitRight = round.submitSolution(0x202, "PERFECTLY"); + EXPECT_TRUE(submitRight.accepted); + EXPECT_TRUE(round.isSolved()); + EXPECT_EQ(round.winnerBoardId(), 0x202u); +} + +TEST(CountdownTest, MatchEngineLeaderHostMigration) { + uint32_t hostId = 0xA1; + uint32_t guestId = 0xB2; + uint32_t gameId = 999; + + CountdownMatchEngine engine(hostId, guestId, gameId); + EXPECT_EQ(engine.hostBoardId(), hostId); + EXPECT_EQ(engine.guestBoardId(), guestId); + + SeededRandomSource random(777); + engine.startNextRound(RoundType::Numbers, random); + + RoundResult rr; + rr.roundType = RoundType::Numbers; + rr.scoreAwards[guestId] = 10; // Guest takes lead! + rr.winnerBoardId = guestId; + + engine.recordRoundResult(rr); + + // ADR-005 Leader-Based Host Migration: guest should become new host! + EXPECT_EQ(engine.hostBoardId(), guestId); + EXPECT_EQ(engine.guestBoardId(), hostId); +} diff --git a/core/tests/protocol_test.cpp b/core/tests/protocol_test.cpp new file mode 100644 index 0000000..351ab43 --- /dev/null +++ b/core/tests/protocol_test.cpp @@ -0,0 +1,74 @@ +/** + * Protocol Tests - Core protocol definitions and utilities + */ + +#include +#include "flip7/protocol.hpp" + +using namespace flip7; + +TEST(ProtocolTest, ProtocolConstants) { + EXPECT_EQ(kProtocolMagic, 0x46374359u); + EXPECT_EQ(kProtocolVersion, 7); + EXPECT_EQ(kPacketHeaderSize, 20); + EXPECT_EQ(kHeartbeatPacketSize, 24); + EXPECT_EQ(kPuzzleStatePacketSize, 68); + EXPECT_EQ(kPuzzleAckPacketSize, 40); + EXPECT_EQ(kStateRequestPacketSize, 32); + EXPECT_EQ(kGameStatePacketSize, 236); + EXPECT_EQ(kGameAckPacketSize, 40); + EXPECT_EQ(kMastermindStateRequestPacketSize, 32); + EXPECT_EQ(kPuzzleMaxTiles, 30); + EXPECT_EQ(kMastermindColorCount, 7); + EXPECT_EQ(kMastermindCodeLength, 4); + EXPECT_EQ(kMastermindMaxGuesses, 10); +} + +TEST(ProtocolTest, SequenceComparison) { + EXPECT_TRUE(isSequenceNewer(5, 4)); + EXPECT_TRUE(isSequenceNewer(1, 0)); + EXPECT_TRUE(isSequenceNewer(0, UINT32_MAX)); + EXPECT_FALSE(isSequenceNewer(4, 5)); + EXPECT_FALSE(isSequenceNewer(0, 0)); +} + +TEST(ProtocolTest, SessionTracking) { + uint32_t sessions[] = {100, 200, 300}; + EXPECT_TRUE(hasSeenSession(100, sessions, 3)); + EXPECT_TRUE(hasSeenSession(200, sessions, 3)); + EXPECT_FALSE(hasSeenSession(150, sessions, 3)); + EXPECT_FALSE(hasSeenSession(0, sessions, 3)); +} + +TEST(ProtocolTest, PeerSessionUsage) { + uint32_t retired[] = {150, 180}; + EXPECT_TRUE(canUsePeerSession(100, 100, false, retired, 2)); + EXPECT_TRUE(canUsePeerSession(100, 200, true, retired, 2)); + EXPECT_FALSE(canUsePeerSession(100, 150, true, retired, 2)); + EXPECT_FALSE(canUsePeerSession(100, 200, false, retired, 2)); + EXPECT_FALSE(canUsePeerSession(100, 0, true, retired, 2)); +} + +TEST(ProtocolTest, Enums) { + EXPECT_EQ(static_cast(MessageType::Heartbeat), 1); + EXPECT_EQ(static_cast(MessageType::PuzzleState), 5); + EXPECT_EQ(static_cast(MessageType::PuzzleAck), 6); + EXPECT_EQ(static_cast(MessageType::MastermindState), 7); + + EXPECT_EQ(static_cast(PuzzlePhase::Playing), 1); + EXPECT_EQ(static_cast(PuzzlePhase::Exited), 2); + + EXPECT_EQ(static_cast(MastermindPhase::Setup), 1); + EXPECT_EQ(static_cast(MastermindPhase::Playing), 2); + EXPECT_EQ(static_cast(MastermindPhase::RoundComplete), 3); + EXPECT_EQ(static_cast(MastermindPhase::Exited), 4); +} + +TEST(ProtocolTest, MastermindCode) { + MastermindCode first{1, 2, 3, 4}; + MastermindCode second{1, 2, 3, 4}; + MastermindCode diff{1, 2, 3, 5}; + + EXPECT_TRUE(sameMastermindCode(first, second)); + EXPECT_FALSE(sameMastermindCode(first, diff)); +} diff --git a/core/tests/reconciliation_test.cpp b/core/tests/reconciliation_test.cpp new file mode 100644 index 0000000..de4dc46 --- /dev/null +++ b/core/tests/reconciliation_test.cpp @@ -0,0 +1,167 @@ +/** + * Reconciliation Tests - State synchronization logic + */ + +#include +#include "flip7/protocol.hpp" + +using namespace flip7; + +TEST(ReconciliationTest, SequenceComparison) { + EXPECT_TRUE(isSequenceNewer(5, 4)); + EXPECT_TRUE(isSequenceNewer(1, 0)); + EXPECT_TRUE(isSequenceNewer(0, UINT32_MAX)); // wraparound + EXPECT_FALSE(isSequenceNewer(4, 5)); + EXPECT_FALSE(isSequenceNewer(5, 5)); +} + +TEST(ReconciliationTest, SessionTracking) { + uint32_t sessions[] = {0x11111111, 0x22222222, 0x33333333}; + EXPECT_TRUE(hasSeenSession(0x11111111, sessions, 3)); + EXPECT_TRUE(hasSeenSession(0x22222222, sessions, 3)); + EXPECT_FALSE(hasSeenSession(0x44444444, sessions, 3)); + EXPECT_FALSE(hasSeenSession(0, sessions, 3)); +} + +TEST(ReconciliationTest, PeerSessionLogic) { + EXPECT_TRUE(canUsePeerSession(0, 0x11111111, false, nullptr, 0)); + EXPECT_TRUE(canUsePeerSession(0x11111111, 0x11111111, false, nullptr, 0)); + EXPECT_FALSE(canUsePeerSession(0x11111111, 0x22222222, false, nullptr, 0)); + + uint32_t retired[] = {0x33333333}; + EXPECT_TRUE(canUsePeerSession(0x11111111, 0x22222222, true, retired, 1)); + EXPECT_FALSE(canUsePeerSession(0x11111111, 0x33333333, true, retired, 1)); +} + +TEST(ReconciliationTest, PuzzleVersionComparison) { + PuzzleState a{}, b{}; + a.gameId = 10; a.revision = 5; + b.gameId = 10; b.revision = 6; + EXPECT_EQ(comparePuzzleVersion(a, b), PuzzleVersionOrder::Newer); + + b.revision = 4; + EXPECT_EQ(comparePuzzleVersion(a, b), PuzzleVersionOrder::Older); + + b.revision = 5; + EXPECT_EQ(comparePuzzleVersion(a, b), PuzzleVersionOrder::Same); + + b.gameId = 11; + EXPECT_EQ(comparePuzzleVersion(a, b), PuzzleVersionOrder::Newer); +} + +TEST(ReconciliationTest, PuzzleDigestConsistency) { + PuzzleState state{}; + state.columns = 4; state.rows = 3; state.theme = PuzzleTheme::Greek; + state.phase = PuzzlePhase::Playing; + state.gameId = 100; state.revision = 200; state.turnBoardId = 300; + for (int i = 0; i < 12; ++i) state.tiles[i] = i + 1; + state.tiles[11] = 0; + + uint32_t d1 = puzzleStateDigest(state); + uint32_t d2 = puzzleStateDigest(state); + EXPECT_EQ(d1, d2); +} + +TEST(ReconciliationTest, PuzzleDeliverySuperseded) { + PuzzleState pending{}, observed{}; + pending.gameId = 10; pending.revision = 5; + observed.gameId = 10; observed.revision = 6; + EXPECT_TRUE(isDeliverySuperseded(pending, observed)); + + observed.revision = 4; + EXPECT_FALSE(isDeliverySuperseded(pending, observed)); + + pending.gameId = 11; + EXPECT_FALSE(isDeliverySuperseded(pending, observed)); +} + +TEST(ReconciliationTest, ReconciliationDecision) { + PuzzleState local{}; + local.gameId = 10; local.revision = 5; + + // Remote older -> send full state + auto action = decideReconciliation(local, 10, 4, 0x12345678, true); + EXPECT_EQ(action, ReconciliationAction::SendFullState); + + // Remote newer -> request full state + action = decideReconciliation(local, 10, 6, 0x12345678, true); + EXPECT_EQ(action, ReconciliationAction::RequestFullState); + + // Same revision, digest matches -> none + action = decideReconciliation(local, 10, 5, puzzleStateDigest(local), true); + EXPECT_EQ(action, ReconciliationAction::None); + + // Same revision, digest differs -> send if authority, request if not + action = decideReconciliation(local, 10, 5, 0xDEADBEEF, true); + EXPECT_EQ(action, ReconciliationAction::SendFullState); + action = decideReconciliation(local, 10, 5, 0xDEADBEEF, false); + EXPECT_EQ(action, ReconciliationAction::RequestFullState); +} + +TEST(ReconciliationTest, PuzzleMoveValidation) { + PuzzleSpec spec{3, 3, PuzzleTheme::Planets}; + PuzzleState state = makeInitialPuzzle(0x12345678, 0x87654321, spec); + state = makeScrambledPuzzle(0x12345678, 0x87654321, spec); + + // Find a valid move - just try all positions and find one that works + uint8_t movedPos = 255; + for (uint8_t pos = 0; pos < 9; ++pos) { + PuzzleState testState = state; + if (tryPuzzleMove(testState, pos, testState.turnBoardId, 0xB0CBD8E7)) { + movedPos = pos; + break; + } + } + ASSERT_NE(movedPos, 255) << "Should find at least one valid move"; + + // Valid move + bool ok = tryPuzzleMove(state, movedPos, state.turnBoardId, 0xB0CBD8E7); + EXPECT_TRUE(ok); + EXPECT_EQ(state.revision, 1); + EXPECT_EQ(state.turnBoardId, 0xB0CBD8E7); + + // Invalid: not adjacent (try a different position that wasn't valid) + PuzzleState state2 = state; + uint8_t invalidPos = 255; + for (uint8_t pos = 0; pos < 9; ++pos) { + if (pos != movedPos) { + PuzzleState testState = state2; + if (!tryPuzzleMove(testState, pos, testState.turnBoardId, 0xD4E9F46A)) { + invalidPos = pos; + break; + } + } + } + if (invalidPos != 255) { + ok = tryPuzzleMove(state2, invalidPos, state2.turnBoardId, 0xD4E9F46A); + EXPECT_FALSE(ok); + } + + // Invalid: moving blank + PuzzleState state3 = state; + uint8_t blank = findBlank(state3); + ok = tryPuzzleMove(state3, blank, state3.turnBoardId, 0xB0CBD8E7); + EXPECT_FALSE(ok); +} + +TEST(ReconciliationTest, PuzzleExitSignal) { + PuzzleSpec spec{3, 3, PuzzleTheme::Planets}; + PuzzleState state = makeInitialPuzzle(0x12345678, 0x87654321, spec); + state = makeScrambledPuzzle(0x12345678, 0x87654321, spec); + + PuzzleState exited = state; + bool ok = exitPuzzle(exited, 0x12345678, 0xB0CBD8E7); + EXPECT_TRUE(ok); + EXPECT_EQ(exited.phase, PuzzlePhase::Exited); + + // Apply exit signal from peer + PuzzleState current = state; + bool ok2 = applyPuzzleExitSignal(current, exited, 0x12345678, 0xB0CBD8E7); + EXPECT_TRUE(ok2); + EXPECT_EQ(current.phase, PuzzlePhase::Exited); + + // Applying same exit signal again should succeed (idempotent) + PuzzleState current2 = exited; + bool ok3 = applyPuzzleExitSignal(current2, exited, 0x12345678, 0xB0CBD8E7); + EXPECT_TRUE(ok3); +} \ No newline at end of file diff --git a/core/tests/serialization_test.cpp b/core/tests/serialization_test.cpp new file mode 100644 index 0000000..cb7bbfd --- /dev/null +++ b/core/tests/serialization_test.cpp @@ -0,0 +1,219 @@ +/** + * Serialization Tests - Round-trip encoding/decoding + */ + +#include +#include "flip7/protocol.hpp" +#include "flip7/serializer.hpp" + +using namespace flip7; + +TEST(SerializationTest, PacketHeaderRoundTrip) { + PacketHeader header{}; + header.magic = kProtocolMagic; + header.version = kProtocolVersion; + header.type = MessageType::Heartbeat; + header.senderId = 0x12345678; + header.sessionId = 0x87654321; + header.sequence = 42; + + uint8_t buffer[20]; + uint8_t* dst = buffer; + serialize(header, dst); + + const uint8_t* src = buffer; + PacketHeader decoded{}; + deserialize(src, decoded); + + EXPECT_EQ(decoded.magic, header.magic); + EXPECT_EQ(decoded.version, header.version); + EXPECT_EQ(decoded.type, header.type); + EXPECT_EQ(decoded.senderId, header.senderId); + EXPECT_EQ(decoded.sessionId, header.sessionId); + EXPECT_EQ(decoded.sequence, header.sequence); +} + +TEST(SerializationTest, HeartbeatPacketRoundTrip) { + HeartbeatPacket packet{}; + packet.header.magic = kProtocolMagic; + packet.header.version = kProtocolVersion; + packet.header.type = MessageType::Heartbeat; + packet.header.senderId = 0xD4E9F46A; + packet.header.sessionId = 0xAABBCCDD; + packet.header.sequence = 1; + packet.uptimeMs = 123456; + + uint8_t buffer[24]; + uint8_t* dst = buffer; + serialize(packet, dst); + + const uint8_t* src = buffer; + HeartbeatPacket decoded{}; + deserialize(src, decoded); + + EXPECT_EQ(decoded.header.magic, packet.header.magic); + EXPECT_EQ(decoded.header.type, packet.header.type); + EXPECT_EQ(decoded.uptimeMs, packet.uptimeMs); +} + +TEST(SerializationTest, PuzzleStatePacketRoundTrip) { + PuzzleStatePacket packet{}; + packet.header.magic = kProtocolMagic; + packet.header.version = kProtocolVersion; + packet.header.type = MessageType::PuzzleState; + packet.header.senderId = 0xD4E9F46A; + packet.header.sessionId = 0xAABBCCDD; + packet.header.sequence = 1; + packet.state.columns = 3; + packet.state.rows = 3; + packet.state.theme = PuzzleTheme::Planets; + packet.state.phase = PuzzlePhase::Playing; + packet.state.gameId = 0x87654321; + packet.state.revision = 10; + packet.state.turnBoardId = 0xB0CBD8E7; + for (int i = 0; i < 9; ++i) packet.state.tiles[i] = i + 1; + packet.state.tiles[8] = 0; + + uint8_t buffer[68]; + uint8_t* dst = buffer; + serialize(packet, dst); + + const uint8_t* src = buffer; + PuzzleStatePacket decoded{}; + deserialize(src, decoded); + + EXPECT_EQ(decoded.header.magic, packet.header.magic); + EXPECT_EQ(decoded.header.type, packet.header.type); + EXPECT_EQ(decoded.state.columns, packet.state.columns); + EXPECT_EQ(decoded.state.theme, packet.state.theme); + EXPECT_EQ(decoded.state.gameId, packet.state.gameId); + for (int i = 0; i < 30; ++i) { + EXPECT_EQ(decoded.state.tiles[i], packet.state.tiles[i]); + } +} + +TEST(SerializationTest, PuzzleAckPacketRoundTrip) { + PuzzleAckPacket packet{}; + packet.header.magic = kProtocolMagic; + packet.header.version = kProtocolVersion; + packet.header.type = MessageType::PuzzleAck; + packet.header.senderId = 0xD4E9F46A; + packet.header.sessionId = 0xAABBCCDD; + packet.header.sequence = 5; + packet.targetBoardId = 0xB0CBD8E7; + packet.gameId = 0x87654321; + packet.revision = 10; + packet.stateDigest = 0xDEADBEEF; + packet.acknowledgedType = MessageType::PuzzleState; + packet.reserved[0] = 0; + packet.reserved[1] = 0; + packet.reserved[2] = 0; + + uint8_t buffer[40]; + uint8_t* dst = buffer; + serialize(packet, dst); + + const uint8_t* src = buffer; + PuzzleAckPacket decoded{}; + deserialize(src, decoded); + + EXPECT_EQ(decoded.header.magic, packet.header.magic); + EXPECT_EQ(decoded.targetBoardId, packet.targetBoardId); + EXPECT_EQ(decoded.gameId, packet.gameId); + EXPECT_EQ(decoded.stateDigest, packet.stateDigest); +} + +TEST(SerializationTest, StateRequestPacketRoundTrip) { + StateRequestPacket packet{}; + packet.header.magic = kProtocolMagic; + packet.header.version = kProtocolVersion; + packet.header.type = MessageType::RequestState; + packet.header.senderId = 0xD4E9F46A; + packet.header.sessionId = 0xAABBCCDD; + packet.header.sequence = 10; + packet.gameId = 0x12345678; + packet.revision = 20; + packet.stateDigest = 0xCAFEBABE; + + uint8_t buffer[32]; + uint8_t* dst = buffer; + serialize(packet, dst); + + const uint8_t* src = buffer; + StateRequestPacket decoded{}; + deserialize(src, decoded); + + EXPECT_EQ(decoded.gameId, packet.gameId); + EXPECT_EQ(decoded.revision, packet.revision); + EXPECT_EQ(decoded.stateDigest, packet.stateDigest); +} + +TEST(SerializationTest, GameStatePacketRoundTrip) { + GameStatePacket packet{}; + packet.header.magic = kProtocolMagic; + packet.header.version = kProtocolVersion; + packet.header.type = MessageType::MastermindState; + packet.header.senderId = 0xD4E9F46A; + packet.header.sessionId = 0xAABBCCDD; + packet.header.sequence = 100; + packet.state.guessCount = 3; + packet.state.codemakerBoardId = 0xD4E9F46A & 0xFF; + packet.state.phase = MastermindPhase::Playing; + packet.state.gameId = 0x11111111; + packet.state.revision = 5; + packet.state.hostBoardId = 0xD4E9F46A; + packet.state.guestBoardId = 0xB0CBD8E7; + packet.state.secret.colors[0] = 1; + packet.state.secret.colors[1] = 2; + packet.state.secret.colors[2] = 3; + packet.state.secret.colors[3] = 4; + + uint8_t buffer[236]; + uint8_t* dst = buffer; + serialize(packet, dst); + + const uint8_t* src = buffer; + GameStatePacket decoded{}; + deserialize(src, decoded); + + EXPECT_EQ(decoded.header.magic, packet.header.magic); + EXPECT_EQ(decoded.header.type, packet.header.type); + EXPECT_EQ(decoded.state.guessCount, packet.state.guessCount); + EXPECT_EQ(decoded.state.phase, packet.state.phase); + EXPECT_EQ(decoded.state.gameId, packet.state.gameId); + for (int i = 0; i < 4; ++i) { + EXPECT_EQ(decoded.state.secret.colors[i], packet.state.secret.colors[i]); + } +} + +TEST(SerializationTest, PeekPacketType) { + // Valid packet + uint8_t buffer[68]; + uint8_t* dst = buffer; + PuzzleStatePacket pkt{}; + pkt.header.magic = kProtocolMagic; + pkt.header.version = kProtocolVersion; + pkt.header.type = MessageType::PuzzleState; + serialize(pkt, dst); + + MessageType type = peekPacketType(buffer, sizeof(buffer)); + EXPECT_EQ(type, MessageType::PuzzleState); + + // Wrong magic + buffer[0] = 0xFF; + MessageType bad = peekPacketType(buffer, sizeof(buffer)); + EXPECT_EQ(static_cast(bad), 0); + + // Wrong version + buffer[0] = static_cast(kProtocolMagic & 0xFF); + buffer[1] = static_cast((kProtocolMagic >> 8) & 0xFF); + buffer[2] = static_cast((kProtocolMagic >> 16) & 0xFF); + buffer[3] = static_cast((kProtocolMagic >> 24) & 0xFF); + buffer[4] = 99; // wrong version + MessageType badVer = peekPacketType(buffer, sizeof(buffer)); + EXPECT_EQ(static_cast(badVer), 0); + + // Too short + MessageType shortLen = peekPacketType(buffer, 5); + EXPECT_EQ(static_cast(shortLen), 0); +} \ No newline at end of file From 16c7accd9aa423132431e275ef6308d5a4d6d8c4 Mon Sep 17 00:00:00 2001 From: Christopher Mayfield Date: Sun, 2 Aug 2026 14:36:15 -0500 Subject: [PATCH 02/12] fix(cyd): update home screen 2x2 grid layout and fix text overlap (#52) - Remove helper string 'Locked pieces lose their background' from renderHome to fix text overlap - Layout game selection menu as a 2x2 grid (Planets, Greek, Mastermind, Countdown) - Add Countdown game mode selection and header updates --- include/game_selection.h | 8 +++-- src/main.cpp | 65 ++++++++++++++++++++++++---------------- 2 files changed, 44 insertions(+), 29 deletions(-) diff --git a/include/game_selection.h b/include/game_selection.h index 14ded20..dbcccf3 100644 --- a/include/game_selection.h +++ b/include/game_selection.h @@ -9,6 +9,7 @@ enum class ActiveGameKind : uint8_t { Home = 0, Puzzle, Mastermind, + Countdown, }; struct ActiveGameClock { @@ -45,9 +46,10 @@ inline bool shouldSendActiveGameState( } inline uint32_t nextActiveGameEpoch(uint32_t activeEpoch, - uint32_t puzzleEpoch, - uint32_t mastermindEpoch) { + uint32_t puzzleEpoch = 0, + uint32_t mastermindEpoch = 0, + uint32_t countdownEpoch = 0) { const uint32_t latest = - std::max(activeEpoch, std::max(puzzleEpoch, mastermindEpoch)); + std::max(activeEpoch, std::max(puzzleEpoch, std::max(mastermindEpoch, countdownEpoch))); return latest == UINT32_MAX ? kNoActiveGameEpoch : latest + 1; } diff --git a/src/main.cpp b/src/main.cpp index 8f6244a..cb30717 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -486,34 +486,38 @@ void renderHome(bool online, bool deliveryPending) { if (localIsHost() && online) { display.setTextColor(TFT_CYAN, TFT_NAVY); display.drawString("Choose a synchronized game", display.width() / 2, - 61, 2); + 55, 2); - // Slide puzzles row - display.fillRoundRect(10, 82, 145, 89, 10, TFT_PURPLE); - display.drawRoundRect(10, 82, 145, 89, 10, TFT_WHITE); + // Row 1: Slide puzzles + display.fillRoundRect(10, 75, 145, 65, 8, TFT_PURPLE); + display.drawRoundRect(10, 75, 145, 65, 8, TFT_WHITE); display.setTextColor(TFT_WHITE, TFT_PURPLE); - display.drawString("PLANETS", 82, 111, 4); - display.drawString("8 PIECES / 3x3", 82, 147, 2); + display.drawString("PLANETS", 82, 95, 4); + display.drawString("8 PIECES / 3x3", 82, 122, 2); - display.fillRoundRect(165, 82, 145, 89, 10, TFT_DARKGREEN); - display.drawRoundRect(165, 82, 145, 89, 10, TFT_WHITE); + display.fillRoundRect(165, 75, 145, 65, 8, TFT_DARKGREEN); + display.drawRoundRect(165, 75, 145, 65, 8, TFT_WHITE); display.setTextColor(TFT_WHITE, TFT_DARKGREEN); - display.drawString("GREEK", 237, 111, 4); - display.drawString("11 PIECES / 4x3", 237, 147, 2); + display.drawString("GREEK", 237, 95, 4); + display.drawString("11 PIECES / 4x3", 237, 122, 2); - // Mastermind row - display.fillRoundRect(45, 185, 230, 55, 10, TFT_MAROON); - display.drawRoundRect(45, 185, 230, 55, 10, TFT_WHITE); + // Row 2: Mastermind & Countdown + display.fillRoundRect(10, 150, 145, 65, 8, TFT_MAROON); + display.drawRoundRect(10, 150, 145, 65, 8, TFT_WHITE); display.setTextColor(TFT_WHITE, TFT_MAROON); - display.drawString("MASTERMIND", display.width() / 2, 212, 4); + display.drawString("MASTERMIND", 82, 170, 4); + display.drawString("CODE BREAKER", 82, 197, 2); + + display.fillRoundRect(165, 150, 145, 65, 8, TFT_BLUE); + display.drawRoundRect(165, 150, 145, 65, 8, TFT_WHITE); + display.setTextColor(TFT_WHITE, TFT_BLUE); + display.drawString("COUNTDOWN", 237, 170, 4); + display.drawString("NUM/LET/CON", 237, 197, 2); } else { display.setTextColor(online ? TFT_LIGHTGREY : TFT_ORANGE, TFT_NAVY); display.drawString(online ? "WAITING FOR HOST" : "CONNECT PEER", display.width() / 2, 125, 2); } - display.setTextColor(TFT_LIGHTGREY, TFT_NAVY); - display.drawString("Locked pieces lose their background", - display.width() / 2, 207, 2); } void renderPuzzle(bool online, const PuzzleState& game, @@ -1634,15 +1638,24 @@ void handleTouch() { } if (mode == ScreenMode::Home) { - if (localIsHost() && peerOnline() && !pending && y >= 82 && y < 171) { - if (x >= 10 && x < 155) { - startPuzzle({3, 3, PuzzleTheme::Planets}); - } else if (x >= 165 && x < 310) { - startPuzzle({4, 3, PuzzleTheme::Greek}); - } - } else if (localIsHost() && peerOnline() && x >= 45 && x < 275) { - if (y >= 185 && y < 240) { - startMastermind(); + if (localIsHost() && peerOnline() && !pending) { + if (y >= 75 && y < 145) { + if (x >= 10 && x < 155) { + startPuzzle({3, 3, PuzzleTheme::Planets}); + } else if (x >= 165 && x < 310) { + startPuzzle({4, 3, PuzzleTheme::Greek}); + } + } else if (y >= 150 && y < 220) { + if (x >= 10 && x < 155) { + startMastermind(); + } else if (x >= 165 && x < 310) { + // Start Countdown game mode + portENTER_CRITICAL(&gameMux); + activeGame = {nextActiveGameEpoch(activeGame.epoch, 0, 0), ActiveGameKind::Countdown}; + screenMode = ScreenMode::Home; // Ready + displayDirty = true; + portEXIT_CRITICAL(&gameMux); + } } } return; From 03a86f982eb2c53faf7433494f44104e18c8e77d Mon Sep 17 00:00:00 2001 From: Christopher Mayfield Date: Sun, 2 Aug 2026 14:39:41 -0500 Subject: [PATCH 03/12] fix(cyd): allow starting games from home screen when active game phase is exited (#52) - Set activeGame.kind = ActiveGameKind::Home when puzzle state is in PuzzlePhase::Exited - Update startPuzzle and commitMastermindState checks to permit selection whenever screenMode == ScreenMode::Home --- src/main.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index cb30717..1bf8322 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -931,7 +931,12 @@ void processStatePacket(const uint8_t* address, const uint8_t* data, } } if (accepted) { - activeGame = {puzzleState.gameId, ActiveGameKind::Puzzle}; + activeGame = { + puzzleState.gameId, + puzzleState.phase == PuzzlePhase::Exited + ? ActiveGameKind::Home + : ActiveGameKind::Puzzle, + }; mastermindPendingDelivery.active = false; mastermindReconciliationPending = false; mastermindRequestStateSoon = false; @@ -1422,7 +1427,7 @@ int8_t puzzlePositionAt(const PuzzleState& game, int16_t x, int16_t y) { void startPuzzle(const PuzzleSpec& spec) { PuzzleState started{}; portENTER_CRITICAL(&gameMux); - if (activeGame.kind != ActiveGameKind::Home) { + if (screenMode != ScreenMode::Home && activeGame.kind != ActiveGameKind::Home) { portEXIT_CRITICAL(&gameMux); return; } @@ -1459,7 +1464,7 @@ bool commitMastermindState(const MastermindState& next, MessageType type, !mastermindStateReady || !sameMastermindState(mastermindState, *expected))) || (expected == nullptr && - (activeGame.kind != ActiveGameKind::Home || + ((screenMode != ScreenMode::Home && activeGame.kind != ActiveGameKind::Home) || next.gameId <= activeGame.epoch))) { portEXIT_CRITICAL(&gameMux); return false; From bf2686312003fc17bd7c7461e59b6dd689e91211 Mon Sep 17 00:00:00 2001 From: Christopher Mayfield Date: Sun, 2 Aug 2026 15:10:34 -0500 Subject: [PATCH 04/12] feat(core): sync Countdown game mode over ESP-NOW; fix Mastermind/Countdown exit sync MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Countdown sync: - Add wire-safe CountdownWireState POD struct (include/countdown_wire.h) mirroring MastermindState's role, since the countdown_engine.hpp game logic classes use std::string/std::vector and are not memcpy-safe. - Add CountdownState/CountdownFullState/CountdownAck/CountdownRequestState message types and matching packet structs to include/protocol.h. - Wire send/receive/ack/reconciliation for Countdown state in main.cpp, matching the existing Puzzle/Mastermind sync pattern, so both boards now see the Countdown match screen (host/guest scores) when the host starts a match, instead of the guest staying stuck on 'WAITING FOR HOST'. - Extend core/include/flip7/countdown_engine.hpp with a 3-arg CommandResult constructor and CountdownMatchEngine::matchState()/ isHost()/resetMatch() accessors needed by the render/start glue. Exit sync fix: - The bottom EXIT button on Mastermind and Countdown screens previously only flipped local screenMode/activeGame back to Home without ever notifying the peer board, so pressing EXIT did not return both boards home. Split the combined no-op handler into per-mode handlers that call exitMastermindMatch()/commitMastermindState() and exitCountdownMatch()/countdownPendingDelivery respectively, so EXIT is now synchronized and retried like Puzzle EXIT already was. Verified: pio run succeeds; firmware flashed to both physical CYD boards (/dev/ttyUSB0, /dev/ttyUSB1); user confirmed on hardware that COUNTDOWN now navigates to a match screen and Mastermind EXIT now returns both boards home. Known limitation: round-specific Countdown content (numbers tiles, letters, conundrum scramble) is not yet synchronized between boards — only match-level state (phase, scores, round number) is. Deferred to a follow-up. --- .idea/.gitignore | 10 + .idea/editor.xml | 350 +++++++++++++ .idea/jsonSchemas.xml | 25 + .idea/misc.xml | 24 + .../runConfigurations/Build_CYD_no_upload.xml | 27 + .idea/runConfigurations/Flash_CYD_Monitor.xml | 25 + .../Flash_CYD_both_boards.xml | 26 + .idea/runConfigurations/Monitor_Both_CYDs.xml | 7 + .idea/runConfigurations/Monitor_CYD_USB0.xml | 25 + .idea/runConfigurations/Monitor_CYD_USB1.xml | 25 + .idea/vcs.xml | 6 + core/include/flip7/countdown_engine.hpp | 34 ++ .../firmware/flip7-cyd-scorekeeper-cyd.bin | Bin 0 -> 879088 bytes dist/web-installer/index.html | 211 ++++++++ dist/web-installer/manifest-cyd.json | 16 + examples/basic_conundrum/main.cpp | 122 +++++ include/DebugCanvas.h | 45 ++ include/core/game_selection.h | 61 +++ include/core/game_state.h | 5 + include/core/mastermind_logic.h | 381 ++++++++++++++ include/core/protocol.h | 156 ++++++ include/core/puzzle_logic.h | 478 ++++++++++++++++++ include/countdown_wire.h | 159 ++++++ include/protocol.h | 33 ++ platformio.ini | 1 - scripts/__pycache__/upload.cpython-314.pyc | Bin 0 -> 12356 bytes scripts/capture_debug_canvas.py | 143 ++++++ src/DebugCanvas.cpp | 305 +++++++++++ src/main.cpp | 322 +++++++++++- test/test_debug_canvas/test_debug_canvas.cpp | 20 + 30 files changed, 3034 insertions(+), 8 deletions(-) create mode 100644 .idea/.gitignore create mode 100644 .idea/editor.xml create mode 100644 .idea/jsonSchemas.xml create mode 100644 .idea/misc.xml create mode 100644 .idea/runConfigurations/Build_CYD_no_upload.xml create mode 100644 .idea/runConfigurations/Flash_CYD_Monitor.xml create mode 100644 .idea/runConfigurations/Flash_CYD_both_boards.xml create mode 100644 .idea/runConfigurations/Monitor_Both_CYDs.xml create mode 100644 .idea/runConfigurations/Monitor_CYD_USB0.xml create mode 100644 .idea/runConfigurations/Monitor_CYD_USB1.xml create mode 100644 .idea/vcs.xml create mode 100644 dist/web-installer/firmware/flip7-cyd-scorekeeper-cyd.bin create mode 100644 dist/web-installer/index.html create mode 100644 dist/web-installer/manifest-cyd.json create mode 100644 examples/basic_conundrum/main.cpp create mode 100644 include/DebugCanvas.h create mode 100644 include/core/game_selection.h create mode 100644 include/core/game_state.h create mode 100644 include/core/mastermind_logic.h create mode 100644 include/core/protocol.h create mode 100644 include/core/puzzle_logic.h create mode 100644 include/countdown_wire.h create mode 100644 scripts/__pycache__/upload.cpython-314.pyc create mode 100644 scripts/capture_debug_canvas.py create mode 100644 src/DebugCanvas.cpp create mode 100644 test/test_debug_canvas/test_debug_canvas.cpp diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..30cf57e --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,10 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Ignored default folder with query files +/queries/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/editor.xml b/.idea/editor.xml new file mode 100644 index 0000000..fef590b --- /dev/null +++ b/.idea/editor.xml @@ -0,0 +1,350 @@ + + + + + \ No newline at end of file diff --git a/.idea/jsonSchemas.xml b/.idea/jsonSchemas.xml new file mode 100644 index 0000000..a6d4e70 --- /dev/null +++ b/.idea/jsonSchemas.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..b3560b1 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,24 @@ + + + + + + {} + + + + + + + + \ No newline at end of file diff --git a/.idea/runConfigurations/Build_CYD_no_upload.xml b/.idea/runConfigurations/Build_CYD_no_upload.xml new file mode 100644 index 0000000..f384b31 --- /dev/null +++ b/.idea/runConfigurations/Build_CYD_no_upload.xml @@ -0,0 +1,27 @@ + + + + diff --git a/.idea/runConfigurations/Flash_CYD_Monitor.xml b/.idea/runConfigurations/Flash_CYD_Monitor.xml new file mode 100644 index 0000000..397dfe6 --- /dev/null +++ b/.idea/runConfigurations/Flash_CYD_Monitor.xml @@ -0,0 +1,25 @@ + + + + diff --git a/.idea/runConfigurations/Flash_CYD_both_boards.xml b/.idea/runConfigurations/Flash_CYD_both_boards.xml new file mode 100644 index 0000000..bbd12d9 --- /dev/null +++ b/.idea/runConfigurations/Flash_CYD_both_boards.xml @@ -0,0 +1,26 @@ + + + + \ No newline at end of file diff --git a/.idea/runConfigurations/Monitor_Both_CYDs.xml b/.idea/runConfigurations/Monitor_Both_CYDs.xml new file mode 100644 index 0000000..c2eefdf --- /dev/null +++ b/.idea/runConfigurations/Monitor_Both_CYDs.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/.idea/runConfigurations/Monitor_CYD_USB0.xml b/.idea/runConfigurations/Monitor_CYD_USB0.xml new file mode 100644 index 0000000..a9a0167 --- /dev/null +++ b/.idea/runConfigurations/Monitor_CYD_USB0.xml @@ -0,0 +1,25 @@ + + + + diff --git a/.idea/runConfigurations/Monitor_CYD_USB1.xml b/.idea/runConfigurations/Monitor_CYD_USB1.xml new file mode 100644 index 0000000..8ffabb3 --- /dev/null +++ b/.idea/runConfigurations/Monitor_CYD_USB1.xml @@ -0,0 +1,25 @@ + + + + diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/core/include/flip7/countdown_engine.hpp b/core/include/flip7/countdown_engine.hpp index 5d8c4f8..a4fecad 100644 --- a/core/include/flip7/countdown_engine.hpp +++ b/core/include/flip7/countdown_engine.hpp @@ -81,6 +81,12 @@ struct CommandResult { bool accepted{false}; std::string errorCode; std::string errorMessage; + + CommandResult() = default; + CommandResult(bool acceptedValue, std::string errorCodeValue, + std::string errorMessageValue) + : accepted(acceptedValue), errorCode(std::move(errorCodeValue)), + errorMessage(std::move(errorMessageValue)) {} }; struct EventEnvelope { @@ -205,6 +211,34 @@ class CountdownMatchEngine { const PlayerState& hostPlayer() const { return hostPlayer_; } const PlayerState& guestPlayer() const { return guestPlayer_; } + // Convenience read-only snapshot for UI rendering. + struct MatchState { + PlayerState hostPlayer; + PlayerState guestPlayer; + MatchPhase phase; + uint32_t roundNumber; + }; + MatchState matchState() const { + return MatchState{hostPlayer_, guestPlayer_, phase_, roundNumber_}; + } + + bool isHost(uint32_t boardId) const { return boardId == hostBoardId_; } + + // Reset the match to a fresh Setup state for a new host/guest pairing. + void resetMatch(uint32_t hostBoardId, uint32_t guestBoardId) { + hostBoardId_ = hostBoardId; + guestBoardId_ = guestBoardId; + hostPlayer_ = PlayerState{}; + hostPlayer_.boardId = hostBoardId; + hostPlayer_.isHost = true; + guestPlayer_ = PlayerState{}; + guestPlayer_.boardId = guestBoardId; + guestPlayer_.isHost = false; + phase_ = MatchPhase::Setup; + roundNumber_ = 0; + currentRoundType_ = RoundType::Numbers; + } + void startNextRound(RoundType type, IRandomSource& random); // Apply score updates and perform leader-based host migration (ADR-005) diff --git a/dist/web-installer/firmware/flip7-cyd-scorekeeper-cyd.bin b/dist/web-installer/firmware/flip7-cyd-scorekeeper-cyd.bin new file mode 100644 index 0000000000000000000000000000000000000000..fa9941f92ce889680a988c2d4721b84fbaa828ad GIT binary patch literal 879088 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%0DbHxdGy?Uvlx6pH6BkB9+V~uVX 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

*xcj)P$9`9td4__!C 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šH$g+;iJ7hM3KJ%aj*$!pCbI0v1-^Uxzd(( zi@H12R=!mmEW^A@%%f7YJOuJ#X@M3-7(*uZFt-B_dW^!~&?43_rYM(Fs3p2{?zgI> z7-YijVNU1V!|j_!+`prV9_3hU+ktq$JsZN2=A; zPaw(15lbjdGSWPCVpH|PnH$$V-?sW4xoV>mbodlt(AG~65qk%lh%WB)!TQy7)r>Ih zrTzuUm}W4MP#MmRfucEWZ_kO^;6lGQ9O(Cb>@#Qn+d=uj{@3|?9$GNh8Xo5rqm=o~ zNsW$qhStA^t=i(#E$67NUu8_4e@76eZPW3G^vDGYOhvvK!^05qzxIY^Z-YGLXU_Tv z|7EZ)AHqL}y6Ht_zzqvA5gIUnWk6&3dKg{VDm#7YRF4WTN_ zrR@eWME5cK+kv2^T|4u0_>q|Z6RdvqxN7)Xfu>!czBW-Ok2x1?^hrT1G?{3pqe$AX}(`e$<1AD1D8r9*NT1Zj&;^eAx%CrUr2Fd$;R(hAZR zmr5$(2v@!4Y}l<0`K;vwek0T|u9j(tie|l9`>$h}HpZc6SwH?LW6Q+VMJ_CYm3cce zZ7-|5#?O=%TH#AZv`Q5YfC{cFrP)uu97d%0qR~=6d4@2&BG?n0cFFE!DJINF-~32y zgeHIb<5_@b{f9fU8}8;&Wt>R40*&q-)o@;`az9NC2o!!h%F1n1cQ?sxwp`j}_C|U) z+jLEe;g)}{my(Ickc>9G!+Cw{=}B8kwPBR?vt{mX^1wqm*dNJc87~h@F_E+z!c!%) z{1(RqOT03dEg|fIf}cl}<$0>4(kMTz*O((}vi7~vbCF)!pN;CVHY6(gI<#-xA8yb- zabFKfXT=Qa0Q?K1rnGxAKbXaW<7?q5IiweeW}PmVIFY@PKHA6c-iY+p{(Bh7z#4X? zD;y<$ru#hTb6C07r}=4`9r>RyFs}vsZICO=V@~%&hyhw&cSR| z_t-FaYIB*{(5qpCCAE@VNv*`Rl7WF^ZHEU|C?a=06NAMWyf$GTG-bemn+IOydsF$x zfW*}AQF4RzlsGiNg?}VM9yk?x3UinYb{P4)R*`YR|M`r?i>eyaL93kDRgYr>b646d z{0#fNVI0~a@FV<~Io&u6r8}I*@n97R!*D-=I?&w#mBa~CQL^}9T0@sGCAVx@2A@Wf zboig>blXMK^leiYZea4s(G~pGK8;L|1t+QI((g&ta8NAL~5Yqu}qC6c2 zGIbkG&G&M_JIuU?!;%VE!!3sTRH|)3f+I8$gw2^$x z7p!*8*^=8UcP*&{oVXTSOeO0vONC`6=biNGw%nShd%VFVl@59qGoh)DuFXR9ir{DK zZB8gDyi>E#5w2r>ur;?7698t7diz3QV4&o7#^yWY_de9GsX|gLFA&VGk~=j-Sy{ic z>ZDAfYgVs4%wbbv)Z(#hw`E%4nSs+GcQCkTVPn6L?;BuMs!KF$qTP&)<+(e0$34~? z-QTr8rTx~2WH4Fm9e@&xw2eX-+XWuWla%jlHPouUPI1o#6#g>`CsR24LWlvy)i9M6 zxC2O`V3O`f`eqv|vHvLuEKVS)5YMMj1q$2T^9uUPvlwocSOOZyzv4sH_=z2JEMLXo zj_RGcLy0zK#Syt}Q5(JNhTVn2GKS^4)%}0E562<((b3djXNylkJrz@=jHwm zeO=6Vku={)bd87m5_|f;!`aE`I2eTDN{{cw5iT&xemMNp|2GevLx)2n(Ww;vn*g)| zXajFyd>(w>1BU%>bdN~vZVyoeCOAk2T3u%>e&p}jOB2X@pDOQ?x z#mi7GhGxvGyUpg@aT{+yj2VYpgW(6n{iqnFu!jL!&V^RW{{!NCbNwmbX>YX4CN%YU z+c8x6Z;&n?Rnpe+w!zegaPbe{4<~r8Bc;9*CX&Zfpq%Gy%oktWMrjBCgbvD2{_py; zFnFw8IFD4j91=ZI!;4&7>5 zs&BhGAZQMY-R7s1G*p^xCFttADGG=h$HbX(b^*Fspi(iFAv^#K{Vx&=s?0?BDS4*_zjTa+l zOney60Z8L;6|*k{<aJ$}4T%tlc z@Z{%xfG@XN2~01^*vW@dZVya$*wdHR@mq+Ctv;J{d+Bvxrt2WNa9_Swt*<}jt2fs- z&9&8=h0QW}L-h4c=Ei!yP)VH+xdCNqIQn?5l+9{bh~Zo{i@J$920kfivh%-2@p}|Y zn^AkOP3($X`?z^^uK6#3lC8x&r>$kX>cndF)nJ~!)41vsRNdYbOn(a4<(o&nFy5={ z5GD#A@yF!L@$5RZ)YRhvEhGLUn!%K&D)|>u3iHWR?BS49fbGFz3_%wwo9)4nviA|% zDY^o$xdl7g!G41S^=1dWU3Hum<0e=L#CIAW z$W;Cflm^f|`{gYZXMBEcQgFp67{W^FGhTMQ1usgsibEj=xD>vwkQ3VY_gQqoyH`+=ZtK-uero-^5 z^1&nXZxEhVWk*nMciUjMrZ2i-`l&{a zhw)o(-mS#Q2%qXIl^5gtuQYcF zcbxS$*NoEPH-BF+lH+1u&-z<AJxT)ZH}1q91ok$7F^D&tvegOZj(+boG6Z{_@~4 zjU{D*a8!%%F4ab(lIWIfCZ}AWHF*`}`R50QTm5}fI|8Ry&zSACZz1d7z=85suNa=T zmzH^i=izISQwLwc8(5KYQznym+-dsTSfnB}d4|X0Ps6dTrE?NbuTq-gM_Z;29@jv0 zfRkG|N~^l&jN}r~tZ!+g8iDzfa4Fn1SG(3Q;er$W`^-oO_!EF?&|88D#;_~bs;}`X zxyuF{%HMq4t6V-?`JHyeQP+@VtUqx7_Ow~0dKumz+xEe%!7PncZ!je1WWzwNKN-l4 z^uRwew+^=putmYD<2+`o$FZQn-U+zY{28sI{FhGDUkVAjqi(Ez?`?)uca>f~7^xZZ zimIvey2Z*{yk~j8Snm1GcBKtAvGt)=O#V7Jb`R--@FKbHJ%;jnyY~4&$9* zHt&M+JL(5Nl9wqdg?R(lT!1~pH%_Zxd?QM2SY$M$C%+TRh7He;jWE67#i~Gb8m2KtiEE- ze!5`5Q>9F`-ly?M9MnKM?&C6&&IY)Zw(198gC=%pa*G!en85s@4(13LMZ!Vbt{Q0W zZ=3riz}g3#vlOg49d8fQU60g`hL4O*a>LH#Phvejsh+Hzg3CsR@1q9;v5x`uEwO`L z8rCZ|(LA)O@8auT|eFYbgEMBf_ z^@@vrT#Y^IixtxcOJE|Q_@!!}+TDGgY|#8T*_O%uH8no#3>&vR^bhz{@$*BwmmbZr zvO!wKaJFCQGb`r$^m)U^rPk%d?Uh(8 z)GnFRWF~=W@n3OtN=X6r@4{(=FKCKgG@DFgK3OSu=m`-{aMG^p$f#qg3;-uS(&@Ig zu~W>oTX&i_?u%^Dr%NkFabwHW;-eOC8VenM4gM#N1lSPKh`GQu>oi!D6vukC=-=?_ zn9BP1O-ORZGCOU@-;p;?l<{cGon-3Q7}Nb`H4 z!m2cvm8NHxo_m?%q`-k4PwK79{s)tIUk ztYS87;WKzpT#i!}x6es05XR$V5-I)`uZzwV=$pXNcbWJbC)-tTe}-GA#4fr%T!y+7 zcTx%*PTGf@bR>xse-B|tQ~$jdBT|dMBaixC3*H7zn0a*N7b}UKz-^(>#=DS~1?tx& z_+bKB*4!nB95kiBL#aWHEjjGQ^jdk!j~Sc3jZQ<6GpIKC_Xw)^7NDR-`mTiUX7(X? z3(w}h&~|XT2B*nJ3XcDFaFYh_rz0hQchTu$ZxjI}dV))w)8aH;Qdo_V%51N6ed!F9K3t}C z3?YLSsiB*kXGWUsk&(&<%?v88@Ed3MB?!4_@Vu=l?Go!3tC^vAoL0*+!70r~?WZuW zmWG3~9l6eGcG%})+Y4pu&>HCtO`#8@Y8$^*)dC6+N>0~Ay{dQIkIJgP-jLT=)8Som zS0C2?%^;!6miuHO09eFgr>XyDc1Gm&$}H>picCB&(QIFq z86IfOtI+Sqem+u;w;PFs4jO0N@9<8>b;xr76L)UWg^)(Xp#_Tc2Z*pe>nin`qQ@ns3ik#Kq)=z3 zIp80QpvR8W=)>1A%+XgU?9WbZ&gd$2*8~;4P!rB4P$`Z3vVmCxwh0oeu>PI4^)`~vcXK!A}Bc0VUinn#H>eU1@a z-gPgu^+CeBN$v=ji$9{c3g~mAg@hO389L%qp#50ku!l9o!CksC%XCtme3SrEV;C%>WBPV&Hd}6oJhs2+jU&ndAAawen6pF(cp+vq1+7@%nKaoC>{c2aURuGV7F7aaCN;r9|gmBYRf zkKf@m|2xSKhBr}Ki19f>;|e(otDSoq1BlK-p>EOXw7_P2A+^?QB!K-(98)N_949W; zO(Qbc=^{FEV1<(&xyz6)TBJ-HF;Sxg=p%e7WrC{;(MkCx3mHz;M@|=sPl@Rg zbmyIz@Gz;`BJdml(fEi|ghKIWuJ~J&bfdUjqW6;OuTGR#s5#hTIxoU5%lJ)=kO4KIYwQ|2GsR#|r&c z?BUq3YBU7mN+__Z`^|8`M_G5xtVLxHcYz_&$d%hDYz&{^{?u3w{UncP6N zAZ%IG7j4_&3%F0)4qAO-IB^ZZRc8=+u;TC@#3p&sXZ#bFh;s)hBat=nxl?s z^>PV!z6%cK7EB$J5|2+=5{>&#dcg#DZG>pDM38OY#psG%xG~$I3RAyW5m0fsm&FsH z%|6VjqlV^Xluc;2`FBtE+C!>N`kKFK>zRc6LPd?sds7$}RS89JQ zr@>*vgcf4Cd5y{|`(x&3zPOk+{t<{^3<>sOzjT7YX8UvCz95XR1BUw{`Sl4g?=K4} zNOTxh$qn<&;FLm(0FQ2R?;-w7NRvkxi^USiEMF4h6|bFf*JWda_yQvC+T694$2UGgtJV>v1<#7>soIQ+aM`YCvsFl<5ICl2#!_e+r!dM zhk3&Q;z{SuW^<_z-7|+;AruY0MA!9Wt9s`ryVJM(BFwaTw927t^2Q5vo;5V{ac@AX zx;ol4qDVaMyHNekYwy4KN18pE1MsuGm1Qg?UK6Eh1j z`C48lQkrcB5Gwpuft^mqYuBVw`QuBMyHM<|auo}5p9;&4YinT?-i$~{4Mfm+Q=j*= z{y@j3h0*KwwXKMonZB=WT}|8KIK5PQ766vtQ2P1^zjY^c&30BFp&67j`)c(Ex-Boc z@_L%`ibP(8mQ4g3S+MWNUAuZbbcfB5>_AF{;ZTvI->0ZN5AJhx)SEp@O96qV>46fr zPttA?TbTZ?L*T^zhjuejI9sM<*6j?*s(-(vxz218h-_}X0vEb8>}lSrcj$y-#j-9; zSie?SR$nXMP%wDIFehM!W(7 z2n6GvS)XuihmuquQO8J`>M5yEEV&N1Wj^6ebkGHsxK3e>j>*@==9lLCbx&QWh|7=B zD^Gug{nBHsqA&j+vC7baQ_;rp#+1%}>lEF!7y!5=!OVWaN!YJToVQp{YPe~HUFwgP zN3TitfFiw0-=dDF$5HS==VF*{&@-iV3>t7X+>bI2o59saW$ zaJdfyP`JV6uA_Khm1p8P6s4-!9)>UhQua~0p(C-2L+8XTMsgL5iMEfr(xBw9Rf72L zBY)k@C6E0gJz4Ws}k#XxP5x8X($-?6sD)Z~R-zgvH`hqUO>rd!_gMU^53NPIalk28E4bfqF*C9(k;fk80OKxlb z!p+mh4d~?YyCGMTp-;H{ycaOI#`3AFYw092E~`QVL-0~Gs6{WuvdAs!@PVs~W2f{e zao?|iiBxpFBLm8J?X56a&wY#g2Am}7?P_5OEjFdnl|SZ!#kisQl<1NAQ6Au+xq7L- z@)BtxVt!Cy#uRRjok@ed$?57bv?vAqWehioWnhmlSJN$?)c^GkC5{`2<-Hc2&cQ;& zlqndx%>94@g@3_Ntc$ORyj&)15iEW)*6q3yalMd9bp|PJ!ty$1mov4A`YC6QFLk)+ zh;*7Nl+k;xf}5?Mp_omKY|x&n*z=4G<@w`SqF#F~S@X_^!5NiT%kB_eRBquoWt~DS zMzHFqG${s^3`#hHbJsUcDk4BGjF~Qtr7BM2E(@Ydf*s~=$6Tcm?j@7Zi3G;~WlUQ< zMzTHNwJfa@E+;Q_T*19EI};T>*GYF>XO1lW0w+P}Px0TPe!DxHj|C%y6wz)-^<6Sm z{Ph?VI>Pg>QoUQ~koBKr+GSptz=jjmV-hygB_+~tccuRZ=}r9#_$JXQO$AmHPGFtA z_5fFg+z|X{IvqHXebLGy{xbaCon0Rg1PH*(4=^=`d*Ib{KOA%52ESS&Bmsngj?81h z(zW=ZF_KNthInqT69$MdZXkK-@m3GeITr{_jY>BKN=~GJT7{bgC3hYHLls?fCPKt> z2su-PBvdI%@hrUE#aPWfxR=p0f%Jcf8q&4-B|0M3yW7(qYM#o_ZvM8@R;-(iclX$? zCM6e){;)pys~mXesihg$Vb2}PDS)kzjeJ&a(>7CqB;4W-89`);!s05^2p&#NUuGX z+9(-?5PJcw9q~XyJ{={E<>;GC<$YqTA%lBjEHa}@ADcHLWBTr^($$QpR;V1#Yk$)u z444N8G^b9R9d?K*#$sCj9JoyJ_xcnF0mW!!x!CP2JBOZ)D(kf;AMAptfY3!?ox?3? zm*lRaXnMKONkO%DC@7YKKpB6M!g6l6=<(`nCe=*stp-I`V~bwQAm5Rj4GP>J)^`Sr z$z$5TeGA6)XHN9{T+5T?(Ot=`=Zd|)xhITtYDT5;EofN06dJC;z&G!0!_y=cyCF7t z90u7WZ3LGNjD3*B{ba1(!3vM#8_YjG zJ~!bsM03c7@yUWaEM-RXP92l?+Bd7@O|nQ8<^nMcns3)L1F9Y@VL%8bAjM-(O4w;4Fy%bdi#uwil>Z?LNSjP< zJnsc}#UzSEmqFfG-4FtWB=T_A$W!YxAnO5}kdQS=@M>0$#N{xzK3+abvmI00osu(O)^n8hD&}Ox_vND-)Z8b zXjU-Hq{?@OR17 z2Y28b%*ScBtbFx!7=$$V)8>` z@&&STCC8=X`jc_t^sa5pc|tPj_?nIK+9zGcwdgbVd5lwBe_vR%1s(8 z)j!NC=2wS8rUnE4Bwab5+Nz1S;^iW9qZ#~v+$@*sAF09WFs&e92_%Td(Ly#g@@e zXE4M(HyjU^CS3lBu~rXl)I*W4njDib;)-d+@${X>(bpingzrM0zNzA7e4i9HCl7e` zK(qs^p@o{J$a4VQk{VCNn<*d%y>h?9Ad*9dT|IUh0@hQ28XeSOpVn+Qpj5x6kW07VA1~&w zD4_o)h8aeY6I&>g`X<$&@IFPPeTz{}{eK~_Jx3u?Hyrkb(%hgD@+lzXIs$s_2B{MB zC}iX+(D%Y(ElX9FOF^CANI^7VsK8xANaGcU-9T*t7@>edDu#eXA~Y3*K1D$}mnmlj z>WtzqHQQ6W-sEnP=g8M6GA0K8=4i^B&Ut4QJ3P0@!+o#R+jB7zBIQf!*6ebzY4v%} z)3hBSABsSvV|vib-EGH=Ep_5DzX_^RX;tb+;Tfp!Op0$_~=tDKF_cLj5dKuuB)~rm^aqy zcj_H8jFEXuGRoIiCMHgQ*tBudtfi9DKEDfM?%|_DS~b1zCpR}h^N_OR{^XR73szV& zo>sknZ_IfdmXugWsUjZAHl`Ppu2QeYizJZUvM*fg#X`LPRHh~>i*6~OT&cRI*Wj>$ zE7^kV^($h|=hvNTe*d|Uf+HU{d6vrv|2kl@%1pItR*P$lqO3ttb1DnE8kf%JJn2(w z>hy&sLG^lQQtX}gWoTn$#yfs^KZVJ3;mPvB{-nuMe3r;5QWfS^7FaV9S3g|3`VsVP zpcfLP@tBaDc>-1^aQ7!BB+phCfD8Q-CcJH^jAg7w+|_>MU_uj^!*vIB8&8#Ls*O=J z&+O#RRlK6rtIQobJPUy{8gE55+B8pO4$a^C_jlq*%q@^-PJeiDVCcSA)+(**Pi1Q= zsOV`~E3|S|HQknQTjZ57O$yOKVe=t=(#@&8vu@W8*nF z)PVXj#6pw;*M8t=W05@pg3d(s9m1x|vtt*x5OLk{R->tqts;!ih14|e7oK&RHe7(+ zTm_m*d;}n-3elCs5$+APJ_3rt`e^dRoeZvde&=KcSl0Vgs}6-iN3Y70oecjc)u8xD zGMTW(!_`bJ?xAa~oDe~2O$VzUXsx|B^GnmcF za~=#tSj@k&@3d;rYjf}Vp6d6_tuYK<)Kx8#o%**f^ys-ogf27-AJxaO(;kj{G@lxx zu7>vFVxDs-1l8eDZEW(aEjx#22{VKh&hGvJ4?0j#317#3*;SO|PfpB$70&$jX(>;; z&*a2_a0CnIp*={llrCXhtc|p3SiWa=5wD_OdB3*g@zh3wQ)y|y1$6aoD2M_uwd&GrBsmFY^%N%1&cyRPm8|K!sl zICT>+*Ejlh$if(#_x!*|0q?NM056V?F2|gIPm<>qeUnYaBkdjxbLg*^kwSbZ84vv0 z0}h?K%srHBs&03*?hSmpTdm8bq`Z7S=P1@5rYI*T82~*Cq%a>%}>EQ6}C5Mcr%Pkq`zp2WXu+G7enD+#} z{2e-TRRp105(+{@IFo?z;2eXuh2ku1M1q5i-|vP37EoTV_}1r9!J z-tgy>taYio$}LOQ4?QWtI?(B)^^&p^|4ApekV*NrK1=%Lkb*T9s2(Q*)%*8DlJNxG zTSUXlqk!pGm)bt8$O9oNZk0rcx1`@KSSWVHEAdz`cqdB1OkTAYcKgqaE-eL%A8&yI z33n;!bS-Sdku;Gb+>^U1ogbl9AGF3;XpB(F+h;O}#W3qwPvBRicm-x9C6GP_-w;?^ z5>G%b48nV+6YZfCUqW^%O0;FT3I_n}Jh&EwXyb=VZGGlCGxv8Hl?D})-1wFl20IE; z(NIf5&gc+b*mBpB3POTQ!+o%GLGHtK7k71JxK@x&DCTU>=&P4NP+~YWhil}Q$e&DdwcmlK(0(0mPhn$&aDs*shE6Ja!{>zfQc$lwm3DJaVle!L zLOQiBkX(R#Mgd0xd+oUd@*Kun4~5j?nEIT<{$#U#Gg2L+pd$ZXTerjZAuMu)43I%- zr0$@IevLa~r#pggqlh$LiAcwR^k&;W5YgE|iMSIKS20ph@z97t}qHT3t5$4Ue> zvb{FkzwAS$xlxLCN*3bNOF=!8S|@=n2L)a7cEfhNVfhw{$ngSQZdl>Z6cpu&Aj)`v ztQp>i)f=D@TPdp8W1LtjIU>-|c!-MdE5<2}?eB9qd`X5*Jc5X!oB^f7PBw<*9OZW= zXBj0C7>Y@f_CNRVRI0F?S?Ta2&zOB3H5T@hD#;nFIY9DL4 zQCrc8{@VUHEo|Vyl-ihecW767MTLbBy9J2{Bf4IJLp;_!+HJisdM1jd8gaL?J07!@ z4(mNFTr|}9=SR=PQzTdAG!@|}hb1h`iRqFlxrf;GqeHMzPXlVF(^Pz4iVlIa&b_WV zt!fa<|9TX!?YJ}mxYtHiWc}-g4JrxDwX3@C(1cTp3j8VDU7(FVPO?09sURc+oO<5d zIpA?F92^ni;>(4bBxlixfC19@M0td75pt$t0LugPF_Ab3{_YCNCN}rc*bW@w!lc^| z;mnvdn&#;L-+JMOrE-IYSU*<%@zynLaLlp0<B%7)Uzt_kPuCw7V&G$ z1rW+^(KmO6Sq8}$b`R%8&V(w7HTI}O=a7ECJdXA!e9Y*XT6QZWTIU3H9SXyG2gAtS z#a+;^K@t83+coK~dpC)~&e=~FypZm!JO4Efh;+7g=w+ogUsiphB@MFg(T?tGVUgzU zr*ST&+!Ib3vAf+bQS~RHgZ%@JfXheEbXG$;3>SH?l&48$1_INbL9`R;H?vGUtya&yFPBh*}<1hKm-V{PPnz=D{kmv?g2;; z{{SRNAP|YTRBQJ4h`aoln>em?f;cVz+Xq<)znO5u=zu%Bi`;~>Pbisy2oztQa0hV@ z+`c@)NK|B73%^H&Po$d)@udkWg1Ai&aPcorxc!6|#@#K$;zqagBIkBCM{4=!C!jR~ zg-baXS~q_$=gT^G&fJEHoc;M^mIjaST_z^mncIf2WcUp3+O6P}uI2Z|If;cc+Cd>=3C#>@X=0&kFzJ&`^@ zTruJH=T;*>bhUmQ<6+jz{O->&Uv%t08ES3td#6R-9?G14mw~8(Yn5D) zM3TwiSY{@*pB}@(0ldP@q_QEGQ^-h4ae`sZWh9k_Ix*j-C$%TwpG!|l$tC|2N$n-% zeeREw!@XA7U^c6JC80DTVtE^-7b)vcGS9=NlEPo$er=4X0FP-yh@ePV^GN(_LRyQRPnC!I4u+`-9^)Xrj3l?$p(s!kxO0m%CH{(69^* zaHkfXiQ`+o2F{u2+DEo*=I^_mE#f^mB{F_!eEIlJv^sasjj2T#aVNKmcgCH~kt|1U z3Dpmcnt%DYX%cR`F?EQy+?cAlZ{5y5;f5R04~>K_&k35u>uyYa{Po1~e$*g*llYz0 zXN|ibZQlQqs~;s(Ke7!<{fN7k=vsRC%Zcr;yZVuEDY2}c`pcz6mDFFpPAqF9|JRAI zbcgPH%(kh?0I!$1`=&dyFvus+RdXLZLNim}fnBPr#UHFay& z4?d%b+l5|iIdB1Ih$clfS9Ex@W$=V%<|e6&S>B=W9m?wHmn@qDH(|Yel)<6QVUGlV zk$V4Avf1D=;8K;I0QKOj?!(*YVGV3mBVXh>tDCZ-9QL?!uv(ybx0x&YL^_~Zeovv+ z=-5LQbK)$qf%E4YT zsl>SZYt{4d@Z>C}%O0yI#+R~5k?qIzBsM9!?6|;a(dYcUxhqoaO5Faubt*fhA-bZX zeCj)~vNu$0h;>dydHI~Zu~6^pveT(8cz?8XKgyjxLfqN>gjO#?<1*tm#FajvH%X#*5|i-|k|;7JUoiT>;hiz* zLL$of3=!u;#JNjr^uF+41zuXefkG9#&t>`gFhg-CPrV8(qA1< z|A5f{PJNM5t8n?1aZzNCBl&+Q`G3UducYMS|0DUAlsx6QGvmC-K7-U>iKuLRs_=>a zQbKXgjpBc)?0%^X&CZN}itI+9`6r?20~+?#%>00ie~8h&60Xy3T;u)gd#(1lM2`)V zXU5-99a5j_C1m(i;X_sW1#0(y=~G=2Ex&hWoJ5642s#J>K2`V-f)oEcf;NeU-+~6? zXCkCF4jmWKWl&Gzr2Dwi==WR-9g`*xktH7<A-%m@V(k)Z1hZoBZ8FGdv~XelAgo8^~Ic(ZoH95gwHSOA484je!U60W9;u zn)OE}BarIof&8t9#b3lKK-|Z9c{1Z1ww?6{N^ ztk}vbmxMcP=B`7jiu$QCKtbpemD)ej7rt+Kmp%93M$C}hH_$1%XPpXj71r{pp#?F= z0syJ{_qa^AOqO^4{3>}>oxImZx7GRxhQ4`0J~i5!$$BnUTaPDdYNG!cj$nZVxvHc4 z!cE0q{rncy4)r-MmJR(69&Y4dSVwo%pgZboT5x~ijcv_;S_bv0FiRIvy!{19jG6lp zs?XG^Z1Utn@Zp1%Sin8GhY@gTAnMFV!8cTarE&l-S0*&ELIv9GOz{RTc87YE zdJ~rlp>aThJ!@$u+{;KNS}bESn$Ls630s{3@s@)W-QhVLbmvo9t9*ER!K1QHYiQ$! z69MhL)NjgFp;`IuR2yYqJM{;Sm%*xl{y^hok!8|wjP*((*M z9xD!22CEE(P(Tef^B4((ldzFtoSaGTMm4l1lngi>YY zRF>;j!eW%?1|u2CNB6;3d=|=oL!Dh4a$&7bQW(r5<-j8tx^|rQ#az-2?2q8)Q3I0A zza8pN`z8D7VJ;iq;&V*K((@3ToK4K(2TiofvY6;|X#r4IeF{mn#rI&K16fJfxMc`} z4m9}Xo?zqWkS}V4-25-dU37PYEL0M&bWyzG6FH>SkqbJwG!89_yjkbBC=rsXs?rCn zC8dtUyw%!QnQLocebo%3?Dcg_$%mWbXBI>8!@PRoR7vx|wQCF|`ano^bdPtUKB!(F zj3KtKwq#>o(0OUWOAKEPgt4mm0qZOQ1e~uA(EeSPX%EY_8*@K0Pb7l8U!p!B!Dvh< zFz=hQF9^K~FmIZnz11PnBHdHNHqK2L5sPp@h~aD|Bfhx5!4Bn#E+SNp?X zrXYFBaYc#Kq!D=a-e(>(2IE*Y9EV|e-4vn8C1C)-}+5*9F~;!!-o8{Q+g9He4N zdI~8b?G3-9qd-Eb<71`L4^qUEJ3R2b^uI$M9&kP)JK%Og6flA`JW=3QMCm8N{QCly98P3h#QGT!gxOz8tHN zbR2|z7BVUVy6gV@)jqjZUfE#{j>w*SyU-^hc1lEQIJFSNL~M@vc=L?9kH0FgUSWO8 zyqN|+QK@v*cY_~yMV3M;7X;4yaVX8}9*eNV&_#bg>e;;0mv<=w{ozL_M=v-{k2~dUr0iTsw!} z_*7T6yh-Ws@Mb+MmnmSyp81%S$xvW-f2HZZpMjz4ug(!&6Bah- zTFz3^oOg0Qs8TZyAEvFwP$k*m$$2X@3qeq0Jm?NWO4S9bPc$Uz4Kzax2$Onv(pQy7 zs@0V*RYz6EiKIq>ad(z6c2Hi~G$q5rMqS)j+C-&Qt{qln>$QS{ika^vsowp^i5X1} zJ@27-tGwtVknQD-=j_kqbR9wwGAGHD=EAXR@gjpbwi@xqWy}3OCuSimS{$p-&aj?tCxh1 zV47e%vhoAVq_0>yla5EslgaaTo!cUBqOI8Y+d%W-6wB#37Mbunh<%?)gFJ|5lMI@b z;0P3Bhba^H!5L#4#O*tt1h&0t=Bx^&Dh;Q!`4J3Rr`zHxi8TIklTUZkhps``YYBCy z@%d-gESK)xuf78dIqXsjx2zct1ns2<{%n557qu2~(WCUhV8Klt#JLg^8DL6i+c>ix z|CV16I7>~Gm+2;2Uls&P<7BKz~d`X((t;#}0wtr-N z>{M=YBcIqS6f)dSYK)2sW!SUlyNOqqnyJ4TnBmQTqe;jcS#uK9q| za+m_@ZI6gupJR4JzG0Wdzkgo$!8yx^6yxe^Xvoiv&#B(K#NT(~ZDKjoqmT-apGa*W zj!viL4zBt{8M-9}ed}DS=Aef~_N(d`3wN&Sz?Vy+ms34vds(CLxAKrVeXhylA!#s* z9meOp@!mLp+bLnMcgO*f=|X>JdUkZq3iyG3Wwds;I!uury<)FiHG_d_oo6*9F|t)V zvkWUes{^vND6zWjFI4JUk4E<3|m z5)2Eo+@?^VC2kPRej-7LGFwTCsbR2U)9xGViN~DdJhKC0gUAt-JuP-RIi_dNjh#;p z%E=-U79h4pZ`=D8mbxnG9S>+tRfWrEdt1()1yJfCd}6&wBl6z_F7>ZG20C|Qg%J{Y zwhuL=e;0jk6?CY&Yat&&Iv%a}NYjq@z31vDdDuT!%fp7*Y3!o4u5SL58k{y@@H%gk^Q+mYAQu$*%PK#@sp2+unSh|G>C@+PQ;88kkp|xq3b;2@CV-_b!BZlyp%q&gbNXD$j!J ztPeW3__KR^ipH>|R&qbP=X%46fB0Ujy8WiJaJ{Jvbu){5$(V1SwK+n&&AWkvO3Qvo zfY6p-*kpt*6t@XVMD#C1DpR9|4TDDvBTv8Y7Io#5n*)5dnKr5Ud1MeOZ&U&C5;S}4 z#c{bVEN@^>PGYBA%xG0ntizsMLpHgL$U^)n#UAll>v{ULiq!%{J`S=5AO!F@0Lmg@ zAGSx43wg7z$b3TaEq%DBNq-%*H%q3GgqtJH;Yw8-tes7xo4uMA?|lox?#_bt`lP+T z@Pdi8+RdteI2oTBAM5FcQP#8lug=wbqjEncbzHv4xUZHCevo%;xhroN%5GJkx#Uxz z>VxT5cYKfB9X%J(+S+@EsUUn51k_M!$s=AKK^^gzM_P=g1-Gnuv0ep=DXG&^m7dSQ z=tqw{4r>6?a4S)XW!=tk%UG&Tk^^Z{i0r*pXm_5y*W3B%hR+^7^>^dC7tjaRH9h}N z8s+?MNV(9prm-YB3|*Ey1JV`!FOn1aACm-Li)3y-QWYt^v|Lvra7 z${bQBh}VXUuR-6AV2Obcs4`(rsiZ~I{*5y?%bfYyqv@|3cOJ&dTca75I~E2<)ku$c)!Au7fQJOZ{WM#{i}iEHH9G4w_QBos>6~+!xgY%a-AM5Z1A3O{=#M$OwKw z1ztSn#Ps#Xwemul zeWm=ury~$9OBjV0gj}cYKxxBu1RW&h5^Qdym1Vzt7F~o3&wgGPeAW_5Q7&5fZ^^PI z^{Ja=7|4}(z*#hsoL_Ckp4IPctD^BOScc;Zj27RstT*&~6OxTDCj(uLmxVox|0P~* zfMsM*a#_4xA@I2ADV6C=UUHlzagM5!pB3|rmeIjoV+6m(YuzhGAG%{G_r@04ll8B= zMd8B#7+Yapr0(lM@s~zAP#e!taGcV3-SzKM1N#`}XwLL=|8YuZ?u#T&jgzGj!ogTt zF#-ksy~owFB`avCm%OhAGi^-C-Nmo-Y+&BNU;<^`3Uk81yb@1d=KXW9?(c-YT!jz6 z!nng@*GclP^j;jBf+Y6A@Gm4k#jm8g=l|{OA^YWZCkFW^jTAZ&h0?=FWf7IF1}5(C zr-G>hW-wb*OP(1XF3B*MbEdl_umo}Kpw|bzQ$?*SrYp3aa%>n`KRq7=O>=!@P=Q|* zSqf*VA)#hp`AQX{)!PqqPfCVb^5N6#3xX&koYI;VjQOZcuaGg&c{!@nD-_aKZiaWJ zu2i8kTV*pWdW8qYTM_RmeN_}6sY_INGs&=VrdN2G^|Ay#0(_*e+KlGc>&_`y^Ea|Y z#Uwx_e1q7Br0-;k{YICm&@fq=M1`+8S(cs*ufO#5GiPbipVVzo1eiA=Z6KxHgtS4@ zS4(LZ>+%)B<^ow_UO=K^3IP-VAXNH>5I~A<<*vlMz)VG$ISV)*CXg(EJSu%3A&^D7 zM3`q%gi9po+$2nuNYJ}TXjV)^RNN`4f{r%T@mO1@NAuZT1^%9dPDRLr3GM#RsQ zzEKpPrmIrSO3d>$FOgL#W-~RKov#7lv4k3Ye?$OF0ANVW(?|f(Zh$!wK#Ut;ZepIF z1n^@Qz{<0Uiun@AJU2+}l0y!XSZIC?g?OBiGDT;pQv4Le#3+7Z zo})3u>Chu?g!>novKQqpGW3S~qZn z_^eX}z6k{eLN;I-`KT&9;E3r|p#eZ`pIUN`)`thiuWT#uMSNSgCG7=sfuU`cAwI3R z;6}I>KnGv89L%EFgGVhzn@(jJDxNu5X^4NKC~KY77xC*>TXG5zy>64G|H(G1A@yvI zb=@BbeXhw;kpr7yD7Cg<)o#8)`NebZ|Qe|nbXNW;1s zL;A;t_{zqbgrkH#;VnyTWA0HyaWx=N^4g=l;TnY7YAr`<5_WXSwBw)wag! zu6V$_#9%8hbQZ+lLKLd-RI+bTccXJ>GGRyUAry61_{B(a+Pzb$hWHoTl6`A8nUf9G zmf~bYUi+;jZIkY>q2VmxQJc1-hSWm|M-9GDQ0=uXH)PkvKU-0NJ~H^Kq5Ar%WW!FY zp@>qV78~qo1vV?ef|mH~!+4w~;o6@q2bUSvCHwYgwV_w-${|?PYg*PRz2QbUl?(NL zO>aPrqoz~8Ks_hu35QN^iBIfIPOCuzsj|`TfJO`K{KkNaN1fx3>o#(;>2Ol?7h5PNCV=&L5Xu}*!+^+OJh6Jfy(J;{k z|8Ch8|88+(8=l$-vypt5QophbDMooQeqn;+u>yE$;Ky9Jpb(#|~YT(v)sJKN z7)#(my=iw@JJ)yJ-@)~D0KW4E-~M?66NU`nL+_-q7Lui$ z{5gX^!Ax~KktxhndGbq?f0ATE_3&b!;X4NA+uY0OnM3sL;(El`a1N!*z5 z{|q(bz(;6hkhac`xXLb5WnX3UKCWtCFKJMsDjv`E{er^!SiCR0#v25$g9sZ6qHm#Ln)O^63oXGT)Ca~2 z8PO_mkgN%x**1|}uo;H&jc3pn^1E^}hFD-DqOpUy!K>zt zyp_?In>arv10O&KIU=xKbFpb^#v z&egbEU>qy!>$Y;ul^kK!O{h6V>wv&x<88C)r|7_XP0o5cpXex9WLxiIBGKgj#1*4^ zETQRq4eF3yqPf|wOmo`!zMqRX(|ztB$Iy3|OWQG2kF8JO91X}5NU(P@2!iyUEBJI2A zWr%*m27C5$W<8x_*Bk|3^cvV#T1U!2slEJ7lVEzNC?lJwx&L9_kbzE3UtZ?riYFmg z7&PcIGcs^fm{kRj0xG>;1VJ z&w8#o*f)IvgOKwCZM)o;2OMaL1%g%VK_L$(qs@uo(&uPke_5_>sGYNpa1&eN0(jtF zoKBU#h4#BOUzVHQw84!c(O!=-k!fel6G_7)jTo818mJ3)nl8JSPWNic?$yu+`lwhC zTBvE4X!PNv_*lcPG}25xMIc;jKFbBtG-#?9;S_YPZMl0;JmE4p`ZN3tJDYT>+GP8@M)+{EBPN6=8Wo+%p= zFhBP+8f1y@q|Zl4M(gXYH~7wV`QPMv%LA1}35VL3IhHG#-pv;!rn8)h0DDY)WxXL0 zXo(G4$3gO9hliSVeh)PY4_xF3&xUwgGO2*AU`86&BQF=t7qOnFAVzLISN0ELPT>!I z-*T22;XjSMVSyozmaMk@DW9)e-<(R!16{!vResq8Er90-M`*~r&fz&5M~f|$*bctr zYPQgqa+Dc~JkUIwP&c3SbSYd`OZreA*kd{w#|RAeY*5LP?|c`}r__=T3|@lmqNjuF zzSP;lWtFGH4ec@ywZk$i=VUY`r(l;&r(qvziOqttd4_u3lPQ7eT*))Q1INL3+OXcA z$Q32xw)0uuv|dj{f#zdm!XY02y<8^?OBRNQ&>*vqvoqTCApw;XROQ{$CL?LATm{!T zi7RB`7$Xam1SYU~NJ=G3`ZA16q}aaU|2ku@D*7~+)7GGUBkwg1Ic9@^u>XHY~ zr`XtYXFwpjCPNB}@kevLT>%!y*9|tX_;Tn`x|j#zK386H|H(vA4a}2yL*iv75~}iV zuA4_wf_n@FM+GtNDw$NoqL~;@12=?zlIoXdH(bA=fghT$n;@5#Z& zT{7P^xv|TXflN75`Rs+Y^>rq$>@}`=IspLXC5eW_!g~p44bcEfpTZlW+ggAHTu$P- zd0fsk4pRwRNkzd#sNPKl_Coto`hNDz}*cUzfsl9v>{bZB5@#C8Z^=;7wo_ zf6h^~p>H64Ge^70uo>u3*OK+7_19+hF6d+9=O-l;EZ1AI?SGiKWZ9#uNx|^xc3h% z!}q;%>!D^_YEk0qP+KCtzHaqU;#6qv*LZuuhnF+1n4fH(J*CIUj?9ZtzTBV=t(h!U zD;AHq6T1~5S~sk~$G<_)PE?3AaD^+GgnHd++&jn{y9jirc5yr}~%(wD7s zENrXZ+Cpbp;@7BMa~3@98kVn^oAIc8ZpzUX#B*q?NAvUx(JbPJti&mkP_uA3=Y#~o@X>=d78pJ zx**r)&I-4s2A<;(ObV>$@*@f|+CM=##rdS1;w&-i9CBOKfy~)UfzZ5y2XgZvzWFdg zi(6QbC0_*dj6XtRJ8A^=+dt9Eh?GN0NxPGP^Rn zBY(O18Mv&PI-qqHWcX@;mhcZ_v8995;FMX{3#(u~`peFXVKKu)+n-|T7P z>pmpNaSI>A^C(@6-N64KBH!ou?j&Jy9QI54A3^P$bLsn*&(2)y+RN`KudLouAMZ%| zQDY%}ttQ#bE$4Hb9A~g#+?#ZV{!J5W$jcf>%_d!w27Qo@r=!s$Svd4qvGEBY&VN54 z&OeE~AdTXFo-=ZR30!`dpC#jXL$ong<9SSDVv`bpEd|tKtZ!UR29$O8iMb%``*>i) zG;>*^CMS{3zhN?hq@1S-Njb4Rjegr;$=_vj+mZ`1=r}HEBK#Hf{ZH~Bja$eU!XH>@ zNrynfirB#89BPjad#x_4XsICs14*=|HXg)6q@>-5Z3^usdliU4TLMi8{-!S_93)LE zgERk)Hb0~BbZ81Y==M(vDhoRD69`3l{G$x6KvgE6&p;me2}_uqY)kO^Kmokz4KqPg z;WWZ7IP^Koxk9ijk1NQ?4=cW(Lj~d5Ib*{M(4hL3gQl|Sgn{rm13wYUaLWl?!7Nbr z`$lQ<1)Jp#2CcGrJDNOQAeaQugW)sigAY(UA4Mh|F|7v`aJbpkVYDbskmE5Bc6&tW z73C!{hP*O8$FZ7XmP-n=uMS@u9Z__#;NtnU=NKZ`Nuppc^8T}25(CPhA#lx$CV?_| z6yO zF2(WKrlOW9gan}kOYpWf4Ds)>430I7@V~DyM3=CJ3^1wgWgjNT0XL=UWMC%&!w_5G^mOBGll*Q^^(V z05$(F42iE2ku0>jC&gCx78=9|&pRAvvT!~w|JkEQXix+FPkJ_cAtVRyk(*B}Y7hln@*`*?mMEK0g$JGtOh|w#SRn9-=$DD` zh!~_quB?{!`$RkBqkV4?J4DoIEuo6G^YN$C7jr~^GB|xg0gPSJ9iR(Ff;2v;Lo8Ur z1+76w^JH#q?8rc(rs%1rJ?2ENDG_vFR1T^T9;3$-YDjFI3W&lxQDOBE7?Tm>$kP8W!)!H2(Fs)+@=9wm>gSLQijEvn=^*NFTn$ zGH1c!Km8Mag6KEe*)d)m^7lrBosD|ujY(c?sfaU%vZZ91?-;zyi;Tmet?=b`5kI5m zkCM_wFE`-ZikETHE*2i@03}XPVv`Lrd)~7_z(yPYkG`?~mT&ye?wh_bPJ4vsR`6eZ zW8D?Jy-;GkwfvGV zx47^JxVKbbH`2T)!nItSM5l5&#|$o9tEbs!iBDK2AFH%F?s|H2vK4-M%FsN23MSX3#p}Zg$%P!V?3mUsWO1#a@Q_?6 z$y*ll|FV}8i&N-$#hl2OGxV3RYzo>fGeJ9F2;*jQ#ig`4)sk~ql9OjK&!dZ9<#G;kXW4MGn>!tvFD-aHi``%} zvN&?)UsENghuabh@=Y`6hrTpbGJm*fhUN>2X>z*7Vm?i4K9LmSqJu7eNs^vsF{jbR zJ(5DVrTA64xRgs@Y$^OyVxG^XFQsvuP2qY=c4ncQn;*JzinMMnjhNhIyOVyIH9GIf zO27%qAz2UPC)6f1CbT7VCtwTl1bsqUg55DDD?#6yc050`wxIEuw$ko5&%M|CaaNDh zq4zq*HaOy29pjEV3}+p8Uv`XV9rsur6YLK97NY#CUTh{kKCE>@_|b_GXCCj4=PxsN zv&?uSGj2|~l`%LS6TJ?m!7<6}7}?+$rG6JDML8Xjtq%661O*0dS)0*J~x@8Gr7(=ma+*F0F_p%ytPb@vadZq9_>{IX5$2ERz-$fTIB+weY_{a1%;+Pk&V3rJa0tAo@$93gO$L!o z4FxNj_Rmz!o+>SyPG|398BNm%^HoWgve;UqKH8w$yC5lXu~9NV#AGr}M%8Wh`Kq`n zxc4%nd{CuUW<(?3JeTWrU=qAJ1;H!?g}clByEuI{?bG;u+(gyl$*6u-wJ*{CiNs`V zbkx0P@hqkN`y}Tae(#C4YKV^R2&2EEd#gPY=s@cU`n|AbAMH87(VuYs9hS0tCA5z& z`&tr+w{${QOS$Yt2yX6D&7J}ov5dCqgKW0RH(zCBw5((JG^4a^4&A_v_Ki2hG^d8O zEoj~!)(oB=qR%Y=H^E2xJ4yVR9I*E*I!EMf?*jjBx^R|+MpP`w+QX8-G)vt9I6^d- znr7kd;{u21oG<8{*<`Y&?zF}{A8b9#M4VRnJ2dAkfsZtG9fYf=L)u%j7quS&SEop3 zhWI+#4_?N3UYBYg)F&GZpRGw-I8)X5$U8>7v(2?7dzleFBUCIAb%7!^rPyq3e7B=> zC*~Hx!HKGK<-OH`k3@~*d~da#Y2K&t@8hPKz|`LQrWF6@5}P&fkp%(eNf!Ut68ZwI zKT|i8rjLdN8cx(rq}$HWABQy`J%M9l>;8_oGkuH;?6s6ll3;<8Ki*RIZ;2mFoeBT2 z7;~W5Eccy6s3laY{g6p$-#1@{5dO86xLKmmBbp3gYE4-8n&u;6o%5!cPtv_>=m#YM zuZ6yXRas%_;OYfBowb-*Ison6#dWVqkFylcm*4^1;@$oS>6o;vyUeAPRNM8Zv{ zPaZ#aX_txw;5GWk8&?~~w>_Ko+#VwsX`5%W_42z^eXVgbq-8s3eYpXAZFF?kcfO0& z2fb$klgKo)Pjf&8B}6mcc@)81Rs&A6+@~mRFKtL^Yq8C3{>b9*kbndJqY@f=dVy|h zsk=cpH=MvTuo(QM_lLDIw-`)+!<}pKgTWUi;33i7eKZu8@c1-$@?_n9w1{_RLU@q0 z^<-Rpd9j}MG9l|xXgu9$u{~47wsjGW-8ls@@3j`pLUG{f4|H>Mn5UJd-9&c}Z5FyIW{ zmG=`}>0MxWqF_a%CC%|@wbK!Yk^0+VIUA>(vsfLmb)ptG&kZZwP18>k^8k0C*J7T_ zs1&RM~o;{zYig+-iy7oEOO)4L|% z)1XiOR^$JHKL4*?{(velpG=dl2OWFq_P^UwKU-j%`>g6;(91tS$!!19FL+jEWi*YK z^0%{&DE+&J*s~5@?QZM%;VWUu{y^Jow%KjWB(!3c%}UpvIWYkKy(ZMjLGT)wX&5!;)Hu=W$EUPP)6gbCRXa;omP2 zZ7%(JnBGAu=%ev{XY`F-O{_@voo!Z zQS_&Dr)VhX(~{1o=)(Vz&<}BeIAS`w$*>(f_QP0gu5cecdkRtGrUqY4(|!4 zcJCUmL)+jOopzkCcnNKrZF?ZAC422MRopak4~`b3*dAysZ*ySz66|mu>@ZXhd|k%1 zl{aJHzLKoS&~#>S>q+O{Tc4fcf1ViPhb`S_z+V3m3H=2Oar%Bb0RN*Ec9EtZ34`Hj zJ<*&&(|-fj^k+GGiOAgyy0IQMqUEHHqO&=oMbVIfBKFN)crh`R`K5NN-WWH(| z(d_OnxC$}!NlAPc>xcxitzjORheLF4i-)zOtufD-VqR;x>=@D97sfq7)01H?X!V3DaU zIgm#`i_^Bz&#WPN9v(%=ZY?1;#}s_$Q%2x<+n zhM4a1`Kpu4r@@U4F*2A@H^gMsr#aFmb8XdN)lxDWn5cmwCvv8#uzVRBD-&wY;EL1e zm7<-`X7gK+r_2A8B z=FA(hCJ0sniyf9&%o(DoI3=2j*_`blLhH-GP$KLCr`}SK+ist!ayr7{+sq7U{wfTH zHx26o!V6*y(J&B!8BL!qq(F7@WtzjXbwSLlcswOH;oRgCh^(8Y0VPeWb7oIont}&|7=M6!HKFg zOTmhQm5?Zm1)UGGp40 zd@)0!mgOf{8NC5if0o+HjBVN>P%z65vLt zrEtgd*Az@PvFeD%gjVM6?gjCGJ~?5>%byy7@G>+0{DZbcCv#6)Lj3mWyVFyP?H z$%q+vnF$R{3_Jo|Tf$KW8rb(^{3#%v)A7aqQr1IRnTc6l*&^Qj1TVuh*2iCMGJO9X zd>^%}m6-%nHqVxLF5xo68Xpf!aH?-wnhKB9<&hD#ZFy~%s^(!tXSz#V_;~E4 z|2ntHQzy!W;|SqYI3+Hpw9=Tla2Vfzy!_npE!Ijq-*>$HU&psNE7$VZj+gfw-{PvQ z;D0$@{?FrEyp;|7)#K%7k8f$MJj(xky!_1ZEoUn)^FJLg{|5Lp70xjJ((&@sxW-iL zlnQ5r(K$J9lv-_2Cvml#h`<)%%QK^M%0wbPY-)b&c=>n1QjdxykWxF3mwy{9^>eWV zQtFZ8<^Eu)9mmVR4wl*;EJ-Z2?RfcD$AR*&AJeyF{`S`~qQNifr}C6VL%{4Y)K<0( z!}-dvT4Sd#%u`F_>5?^B;pwmFiYw{P7sDE*bw_j<;(OYjYPG06%~v`f3Tw68;c2sY zEq9gGVzO0JMkZ#lGR{(Ai74LsRE1?kaoLp$OJwm@Ow5iHr)o!uQ?;WpRoiO0v(*!D zS)%HSSBMj~W9*(Myq53^3kk2hG(~}^TyWNQ2cZz3LL#t~tshIyi;~tHBX>n{qR7M4 z@#>^OiV2PMlE{Cvbx^s^SE$eP{v~bF;w-T+GcX06jQ2D^B2IZZk~joWOMsP2k22P2i$jaRRsE z)&y=vZ~|8hCyfw`LpVu{IR(Q>V$2EQq+rbH-l3Qiswu{t8g9j$8U|xd((x`a+~f+6 zbcx|6w^sxr;U>4&C5D?^(rK<_B5GGSYQT^;M@1l3ymTd#d1RL~(Ulw{0*!Vh>&Tl{ z5hzx?bS1}$K(k!QdJzZ=izja`i$DhP(v>`31iH_aJV68+=SrSP-kcSICW)7>iN{7ciBPr67i6UjK!JnJ+?bDClvQuOeIQ}Ym5H!rBZQ}cE^34!`Y3OmV zz48}-bASFeJor}Hxb$Tm+dpXka2!>*YB)MLbqqcEHH-h4KQYX-0#xXgnQP_h890r? zBjLXx7?xp+qOLEGHF(yxe0i+;%VYc-o;cv$_UdMOd{Ce_;`RtfWN_8ZS?d$1l$CY^Mt;r=fS82J_CS@4_q#$&uAcqiJk1`jc5 zm=O>+nhV-EgrEN{FguRnBU$X@$S)$`Kzg;2xExx218go2Lzv3>Z_^|u3LzF6iR z;)eJ#e(P=cVPo)}_g~19ZJ}LN;8sgZ<+18lj`23~tVR=4b48rd$Q>1ZIj_J81{An%qHmm*4Dw%-ODvu~2epILC z&Y0Xwz%^E(6t{dBHM)P+W9gPzrAY68wCn4W_yzyV`vkSBHsqx$?xl*5>RzGe!lYCg zMYTy|Cur2;tA7@B@)%dOR7rYXs+Ap4e@UpmB<$JtulSH(eH$Mvv`0w|@i2dx$_)Gm zq##MAj^g_T(0gmI0j_QxKI05_xkL|o)PpZ^tvf$62uPlPKU0)hjrO+pI>>S9wd;ib zSEL&+?>wfK#%>5xSN`v)5qW9j_-le&h0RqcivLA$k5Wn~{)*t%ZwYpb@v@DVAv2;H z)V!#Ef`cCr40@xeQ^7YMgI9RY^a{{8_-NWJ2~#JqEahb*)KY&aKHRPTMX0$P4J=u} z@h0HY@%uD>GY9kb3AB|qveL}gqzTnH9DdjEELm2rUqS6)PZj9lR1haEh$KvI`Z=>w;VA)k^@dLw3rCeFLL!!G9<8oR8K^Yh(0lm7!G4>(tH?#x002 zL4@B3J?Em60Fd;gUxw4TygGdSC-qAyZ#A}K92c0pz%t7QI%!A$tVN((kLwHBDs$RH zqRg15gk@4>KL~8wN|2q6z!i>1IqDtTBFew#Y_w7k_@9C@#gSH)9|fbF8k30SeZ~~h zG-9KP?K(Vy%Kbs;u<@>=MA839%?x$?Q@|mkxKb|zMsKS9bSQmlD}s(BNqMpIfJPTtkCsHR#PuVm%a zaF!KmA>f~hq{8TIyo@%1`p#IcG6$S0vj&OCItdJ{Jvfs{S6d|s${N=PLXUcvwConD ze-dhriIx74V1kz8G&@V3;6gE?K@dyIY$J8&-y5RMvO(BEtR4SP@+9~-O|IQdS>;q7 zb{RNB9@p?xO*v)xi%=`q_FYes8Kb3;8uo4^{si_xAQV$n3}jVzWn|7Sr-Y{}w$1z#zo{z%Qn#a2Q21Ep^F)N1l<3jPgY z^S`J(pMrl~NbRA9@qZK!pAO=ARY?6x#Pf=v{PGr_ z5@GWR5ziJO^_YmqEhxJPo|gpW=lG@Keo*i)3aMS_nfMn3Wjh`bx?Do)ry@RHP<}$G z|5n$i$(yU-pA$B>PR%$Ag-r{ z)ZHSkrvzogE!x%!o8J>@TO*|I6mb;_N*}@XgrKa&@5skefM=C(czY1fV?wG|#8V(B zJ-29DDQw;*(v&BpR*86Wh153*o)tpspYa>_gEaN9fciNEWd-g@{T>lg|0v>mSWv!l z3)e%!<}D(wY+-Y$h--UYn4*j^Ldmq^`wpu&$|s z@`)gQ4+xuAiHK5!)Pf;I3f>|d&J*#Nh0QBOJhOyUC&6>SkeY*EQrDMBUGEc8m*Sq( zb-J*5iHIvnNL_pj*A(IKLJ`+wVe?)v3b>ES7Zb(|!mCR4wt z6#PU%X`wzKG?%o+;vs6;kgb_;f<*bo`Ed%nW(5!eNdQ zQ87a5B%o68w2+!Ggh|08PB2l#GgjC<9(WY|7$G&D;-iGaabo=zlKPDjl=lE9AxSH2 zX2kkM3aR(p!V@lFpMOG6n6P=YNRLKH9ZBf9OGu5Rwk?C`?hrQLA(EgLQioIgaN%$W z$Q%v)p@MQ4Us~i! zo>#_($CO%Q*%^l;DQ4Ti#%r!{S#c#dEo2TRu8nr$VBo(}Lou(5FXiIP3F1p)%<=!Q zieg?F*w|YUt|)VJ(^PliTWX`7^p*I(Swc}ge>9`So<`wte{df^3zb`XTiN$3wG{L0 z07{;wdT`@4ZaT*$X?|7qQ+O$#rZV1od8=%SVjjn9#WYpKt=EI4DrCPSY#+xrbG4ujRw zq=l%EZj(7PyuUPY7wOT0e%$kB1iNqq8#8Z!Wo0^^)Ryq~IXstSMoIY80c=2})l;lW zf(0|W=K(<8Z!}|cP<1zd|G-w&#E{bOCx*II@`ey6RL~b>VsSuvlJ&w{36pOS zr_C*M=cA9_VLeqMI&EzApqm00@FyYHPFz5W(GSGS2Un@T2|N?v`+D%3hTq+Tc!{5_ zRtHBe%(xqFY5i5fr4OsVN_Ou;32L=oTK_Yg%rM{;eHgLcOgKQRe*~xoKzeEQ1%PM< z-xrSQpOufqA+11Kg>)yz}XZ${h<9g zQX5hke6*6JT+EPwAWbux#AENSKgO1*rj_@mW(TLNTck~^I^X=v-( zqY~y||4l!Rc(2t>E&md#zZZ1V2n@5Ec=Xz{DrWC*!1lF#EmZr3n#m%nEP2)xRQuV` z1>1!z1wxSS$XyDTcw{W!P5d{vWSJ`S#Q`@yl<-DVF1;MjYNe$mZn;Z;C$5)BNrm-t zJqd(}l8eWNAhoi%C~cbD8biWJ(8KuP@B}QZCPQOtB}Y*s3j1fdAe4tub0FwsMNvHb`eD83PeIKmQMOwh7uNMLI|PhR#Ez%tODYv*sU> zng5N>J^z)?n&(714OGpsUs0J0{x%2cT=2G#{s8ULQ3*9df07HV(Tn_4i2VGEpp6#k zJf!?JKdVTYRY5wlP=C8F5`5BiL|!-#W;Te-%(%tOsQOohx*zD4Z-32A=4+T*jMsBt zri{^qRIssD9%LkBz2NyF+MfFlw5|Vdv^_~^d-Aum<+lD;+E$9R?f(sJdxEspy!dD-{G zRj!ST^bezY_C#Lj(<{V{3m0h@-p<3BBApci7^DY$P$QXFNOz^ZOu{(We(`{;=Xpd6 zlraFAU{jA|*@vLEuY@wkgv>sLUa_sg2t8kZ6D{`Pua6V)9{CO4dw>_G6ay#X8(WCw z&n*IGw`5z9Ua?EXvJ-&#HahkL9;avq4}9)<5k>Fp=3DKA$amO~ ze3kz}zQVwN(Y@;f;M?^9u`S=yz1;2pRlffoAi9_G8~M%}lyAxJ(EKYF51dnQs!qN>8$oi<%K9_*8p}Y(;if?&_t&KnkbsN`k#mr{v@QTC@QJxLNfCf z0o%V2Q2P;g83Z~{fPN6vCPt9Jfj?E8L~B*@nNq8M(f${_`a!!JRsVNV+7;3+2vrG( zgrrTW3QE1|fb`wB_@*PMYlkgqVX6ca%QBY_1h=803WB)Dx`-pmwhFP*DL7~tpX9w5 znR$NTfK(k-^Fm~%ZD3r$s9&@*@wW0BNO=LG{e(t}aSphp%=ROhSvSaiK1Wf$EnaZK$W}Go04FxT?*-AO6pO2c7y1%O%0;)O!`g z!$x>>eyHQsBg_xN$|~vR1jJYHqUX6teTLPJEjOD0gIHFrH9w=Y-C`La9V7|zgAo?L z;EK_dZos~oQsCWTt+ih8F-hbSh@7pD-3=<6k0eUn_^wq2~6@0u^?|!gLYPFI{7WD{wv?4uilhsPr>SEt3 ztCP4oyp;%08#yd%VZwZ&Ho4SiQ4UyRC{&fy^KH~KLaZmDb4PM*@`Wm904Es#=X$1y z^+Z23C_7h7c&QubxITsh<+_ur^8czXi<1s-FQy-G*`VLlgdS zV`sKo#ym5i%~wLbv@+#w`V~d=3q4W{QLd_Oz$|_(sq5d1eWhb*q_}`GOuh0Jg#UuL`arW{p=H)cB=kefwE?xP);j&@^s|=w z8`GkO8)INUL=}=zHD6HbqMFCY)oh~b529E~caJp5rM!bepYL+2T+xbc`MYH92As*- z077aG3T}DrLYIC5xPa&I=g5`YJKUY*+TzW8vfu69*{zrFbVB`hMnL`8YBJ3>q=Bbl z2rqRDE!{%AIB1Yja68=%J8O{ziw0PU5wyiFKCINxL;JhxC6oJ=N0DNO&3ok>Ju-`SPP~%cp*PKZAt~>IkQzZT zc>~W%vs_9RW*0ksdOr!}mv%t>ILN^?%%xE6A65RXur;J2B>kn)ag3HAJS9Nu3Bv8S zI+vvc;YmRqg{D0&ur}^amxns*krbZWbXpG6hm9(gxlbX2|6P=d*?A*2lty?0;cte5 z@{UO85STUrT1=w$S&Galzc5}{Wc8w8-zKB!br&`Sg5aaOBVavnS;f=#H#|x#>y@!}BH$?f6Yh2_u z2>L^MY5XD9LmH;%j1fcv zVfToHoARSbp^ZXnD7E0~jj>aWy3lEF>tO?!2M4roO(MW4lWqcExdGsR4#@^xgi%Tj zTdp%o$8P%x`165(yoi7N%`E3gmTmzXu^XjY^>@=gqyhM_p#4@)0AJAG1pJx+el94_ z2}#ZmqZvZNBH)V^@x|T*J5In33#nfVWX|rqfU_t7x~-4M^Q~x+-smA1bh)a1YUoD0M{e-D^RVTQP5$258=8?I#$~_Y^`p1 zO)mXTT(n-UZBW}`Q!>;y8+=s#dnE6h#3f4|kFHbh^)$Qm4cQ~+gg!h+f&E@ zoftAE)i;Qb`6rTTmrRe*BJqxy|G~6!tEc&ZlK)>)IwEV-mW4qra7wjKaY01B(O^Wky5AJM&=8I%p%cqgryFacw5;=(OC2^ zW?l}_zp=CRH@0)n5ZO-x$EC*yS{@&`%}i7Jnw{QX+v&!k3@b?n=Rk||_6+*J{uX}k z5d1>~K69WY^LF^@Z%ltNUKSsL>ZaaB>EF0;_BZr>Jd}Md$(}mUl6re~aB;dc9+Z4EUM1q|8f z>i8Q6q+`|mXd(e(=I3GF3Hh-`;3$7X*s8+9>(c4|5OFp(9q-b}dyM~X2>M|i;Ytn- z91f6#4vyJ4e=bMFicy+&;>*n_f>JV{9T>xGHe^Y`y0)LF2=e;VbLM3sj zq`?_w#AvWddWBW$vXTPvUl$t%f8(X7pS;T{&sK|vyW+LVJ+)6&SQSRA(rX&Q8>=rhy2D*qNPs$xe0gTn&NrqJN7ktPvoVc)bY_f2X5zKsLd;A)J40zn z5hfq5dM<(=qtZX256E+0RT}Linw}JHjFuP=j^cdHUXeaL6Z@14;NepEp-^nIAPC zdPhw?os&ZjgK|3LgrevWoC|YINH1JhZn(ZWG%JKZD4j!K#%WVaA&{ zLaY3U;9gRpzmLR3^&z#06p+Xn^Cj$2AwG-wE*Pf3@lvEv-C5z;(5;SBrfS9YgpUM$ zXss*=k67urKy6gR$l=zJp*DQzyc^JMq4jC)b^B>t?j;<>{RhIY@9S_+w)fZ@YTiv2 zhqZ-Tb_(Ws8Y~P~ufqMD&@_b2EwzGqpC&D8?)Ey{Yu@)ey(yF$djNY=aM+=D5oru_ zw+HL2C3OZ1B=2tLBgl1q6`X&i_mGCaE65J*Rrx%ewS&B*xINwKx&!oHb$zMO{ZOlx zYTWb18+Uqk(%!j*D*R8CP_5#%Ggj|ZQaL%3i{B1w+~e3Rw9U;~MZTtgbgof`zF4b{ z&fin3{GwCB-<#<<}X4l=aMy*hfwle?rzdvIOz^JuTQ zk`Kf#7PdMrTvkWfPe?GqFMHE?aDX#x@*t9Ll+>m&3k2Gr!BX@1RHGtazb8*zUxwK$ zy-Xd!9+k1u>-N?GwKP3?6X3H_nFj<~7lcPSN(Gf=O|S^u6#B4)T+YI!X1cZX8asOB z7^88?N&w+MX1o%n3v2{sZ7V`^P+0Y?2ZOaFG1y;?!ipnF@3P4hPoc z{u1gj(H#Z3eFD=57dzx)9#6>s_sHwWZ6 zMXr^$9+UA?$-PUyw88UQ(+BER-qu`~bCgR~;j*p>GM%49Ak@Q`4R^`PT-L|%)D~8+YTezY_abYsyk+ ztC+!iOkS6wsR-SQS6B*s^SL#&vh?-gFQ})hV~Stj^h$`!$yh_HtX4M)u@oZ=GOl!; zQC4OYtAS^^-w7i3`@a_i@D<|>wpyy)G$Pk8|s(8{S&g>Y?j)7h`GQw{756!&?>e)J?m+2WkEra_@>cIriG{ti^lp zP%G@oImm_osS?U1UfZQV;kKst&c#idn(KFZlZnyBf6N6@SPuGAZr`2nOzFXeU3}w` zUinb3A&ByWa7qlbDBvQucIj38ty!R2?9=2fPTnovXRQx|I0jyWNb4h>xP(Ruw71*{L=Rj%cvuLe4`9j-QX zm0do@dR&eRmq&srTC;1@e3dJ?Fo>$AlQ53x>)(V0lLm0uA}r+9=n&oKB(8~>b}C`a z06xN$YhO@!J=@f)>c@UIfk4igSaP>d26iMbW7Zkbbcb=upXt0QvTgkS)(6#Y49$&C)xc}DlV z!!Z4Vm!(SE`B~T8D)(04P^-4UWPoCn_OG88{H6b*}#dqq?^DgggaooShsfdtyiM_jvYJ>@l_XYqto_yAZ> zS*lULTzy5TS;gG8o^oCNn^%6no-zx5&{8VgJmc#y^Up}Y2;59`9lZ|MHT*(bd5Hkam41H1DU?QMo51j9(N6J_*69 z!X||}T3kEV<0U7vo+i(VYI+oF3t?@< zp;}_XAKi@*9RCois}j_S#uWsv=S{JUo)bdPRsp9{%wK>*oh&k-ra~B7lJG%oo=#l+nv0~8RXuG&&C57Orss?xX3PDfSW^;)(v_gQld-uX16~oh zXqt#{#uBkkx<3m1-2>Vtsn;`#&+LUJy7Ui2D_qr*Z3S3+yCq6p>#|YcF3Q6Ho_~eB z5=xHagI?smEbw;?V5KD*!!~2hCfwbCR;j2hq;>0F!fFWuNl~Ri{^hy_MmDfBdu$1F zU$0)uvJXRXp{UzT%r8QG6oesG3wd+Tgpg?22*_bOMx0UG(wZ@g3`RZgMc zO5h^ZNKKTDElI-s;6spAhp+1xN}V@cU9dSG@v*fw$|y2&36Y%cScvwdLhZx2U+IdD zGe+xO;)(=hbTMQqEa4M@GkUofEpl>&@B8YRZk#jfu>j1bwJ3i zmKdvlo)#`W{Eg7n1XD;}>O2xQ7XIF-ASbj%5*z?BBukNmo_5q>vjU+dN!NRD4yDaI z>6&eXKZxf|l;!3L7)cIsLRUak>gPv-ZQ(J2DZ8;hn|Yz1J&HYDzGt=yAL`>E+E0GD zU9>h#-LVA{wehBGYYvLCP09)C-%5dbj^J54glF?0o)7*b9_vj!x{pOX=0}6{I0c&f znR)Dnwm{B5(hslJrTU(E61b*;CeQ7-cG@P`gILrm#E)Gz6$o_MLR_Ndm`*H5?p_i1 zJum*S0D9iD_D(o~xliBed8>JhDR;x2T33kMUg6nLnpRPBs%-0Wx3i%-zcj6(=2Ttd za=Rhq0{br(b-xp<0AeU-Y@}aFmH%6ChET4<5_~)ndv18}`w4LCFb?V* zwpRR_0`vd!wQ*WJ^be!(mEyo%X$ou1!~0ujlpuCKALn+d)r-*#0H9V)P#=)4`AYah zlzJG1a8m+*FnQZY0Dyn=FdD%wIpV<3VPx0(R926|D(1Ev5v)+F5mrcV$mqwL;y{nlWsOPTJNmU% zGQRmb8B(df=Z_717;k`S)LNxI2|`WTonAYsH~!~NLFtMH4?6I(;Z)bZh1^{6oI+wN zHIP*=WR>O!wvQrFMujrO|L%x4g#UB{>(BLGPTNU1LN^79Bi3(TzxKmlg zi1%E*-H7k6!f2SkfZ@b^eM-nR_|+xur%|3iCUS`lnvz*O^s*p{f=V14ug?*g9`{7cuavI|Ki zj>)~A?Up@P#o5z`A!m<`26Ojks~dwWkB<4@8+z$hXX%n%GDJgz{aW4fpgkEU z@LB!ZC^^a1AR~-@^(t|h20O={Me41CJpAc*JcNfERK=ckH|bFY?UZhmfZ-2g>DQi@ zvenY7iF%-BHr(VP`Ftu8-@0sx8k+?C!R~^{qcCJ#AU$8CttUckw;XUN_$jDb*2Z<_ zWKHb4h}g-Iu_H!+XcM*%%YwQTdt1bAGtcV`JpQiqW?6zm*W&qx;C5_+p4FC*6xLL62=YO@8bGk}2(0>OE? z$r9ZoU&V2Qb!@O2+1eFp;r{8xRhZYi5aW_9&=im)86fI8@% zj2i4e1_t<3*R>~Q{F&aX>@+^&DyY7lU8b?MzmYf5!63zApP|eA6~C?xFm2btul;>2 z%mMnx&Kn=b-=Pi{X(7WqjOOr{GNS{TGz21iC^{$T6`u6roXAd{VzyH)Ru z3fVYNeod$ifwl)fucF!zV@W1H5XRMZn(4W6_I<&8oQAKh&`S|nX4#3gD$x%r?Ng8K zx+2^N{ZX+?UH`@>y1U8X0Qs2eKD`vvW_b|`qjVdr8^TpaX`khlK6?zaEyH-QUoBOS z;HzYlFBq{lIea2|SZE*uztNvi^3dVX|YFtdQDa8IKDVbFO(AG-DZzusm zm<4(hc3!dM_F*aS(CSBKd7qbMN}Jped2mRBh=h6+dlUhCf@yHwBPoZ3r$nB21Y=XW zZwB~>uWS2Y&ZXK4iti(XF^o@d&K9gH2shJ%{I$~PFIfN{(%54tZLUW5)d2G#wB|16 zr7PN>;Q0-i!VZHnij3S7px7vuxfoPoqTy=k%??W~ofEVZxyGPRBvrQuz5HdNJ4@6Z z=G`=3dkC_wjoDe1G-Bt%Bx&&Vj>wL_T_iBVze)@rc#k@@#WarKw-IotZm_kXx>}oT z#-L-ZmN8hC5JjB8+o_l+TN1SSIz!0n&}mVeQUaZHR!fLi*fQu9=3b}tF{NJph*D>5 zB)>*9w+j9V@;gyHXR^5T_d2})G0_$f)<(;7+Xi^~b@Y&+j>nOhx7pI{8R&lqpZMR~ z(xLu{9&BCuRlw<>jH0WoJ2*hB%*8&fPXW2WZPt!bjBRIPuH39YF{t-fNT{V7Dr`g- zDkPE$8?gly5-AF;un}v3cwvP^wj~O-B`BPDQ-y6LUf_StC}L)){f9AT=SdsIk}*v+ z4PX6l{FX4aDn4PlWeWp9j8X^NWXJDeT`d{{%vwnFJP3YL1UHlVz`lMs08^pOmh*Mj z2K|SF{U65vU(VhIEQ%{#8?CCoLKg}&G*^@Eg65)_K%oL^M!GSGm&q@!Hs~ZX&_atD z^KV5bn#AnsE(|nEW@Bq27&E(RgCfExZDNek86*Zxf_sEGY8;&?NhfMFK_tctQs-UO zYVx16&)Ls&c)F;nb+2!&^{sDxw>2q#y>npbm#~^Vwcr7)Zai3b2p$R`#Z*GOHitaI zLx%#ea$X!_nFq+9Gm&q?4;|OGXwKOOW&KF-|1ZX6ZEX1SGVXaY#G2JX938+@Xy-=P z9X3|@g7y~t1998>T)u4$EaO#cHS5_}hqp>dUGDL$N|rpoqY7!C=zQf48$&*-pqCAD z7xcLcP6gUP;M#Ux;)OUX?|{Anh12Dsd>*xTCMmw@j@VT}xLp3kXp(j~pfi}U2E)tPbe~ACJ5iAMZ;xXJ}GMZrci$kf)-w$cWImLnYmtPI>-(-=f9>ovel;D<; zc;{7F$~>z#$m^Y2XdeR7mA2JSNeDSO2o&=3^K9p6cT<`v`3_X^q}t8-73On{dp;2W znvGCe`>l70!n?f%uM(N=T|DK`EOy!K){d|{2y@na1ZcL)xP&P=j89pCpk_@7r>wB^ zatQY4%bL!^I@ZWWgPU3b5w0yM?V^&(xPN=*JR}`6;7eAZbBzn5(RR|0=nKQG+u0V~ zM{@J$*mP9bC!n0>Kg&!Lhf06AT`cz|Q2|YCiwAlOElP0k7g|W;piv<(Ytq~@{PXs!Oo7=tJ($2~%>NBWtw8|@u}jU* zCz<|r8)eG3{SrXZ4rbSk{{uXeb(7^1d_K8N;4>`%AArVqm*o;B=P(j*RFX8?Zzk}8 zB`AIYEB^+W z>FUkWcL1vvWdKyH;vuBWKSOGA08$uX5_L^<6a7C#ls^^;NxQ;$Bg)5-y2_^!h*4DO zMbk7ju1UDw{Sx;kDk*&8HYSPtwYP~$;+gyc3fy7*0SAKR)#Z`3YnmG=n7$j1f`&-p zbp4~T<|tu@hU0XxX|{)J4TtYa$QXJe3KAEA>=_5{qCkn0N9#Ukv9y!4+&JS6(XjkB zS+|*xK_;l5QP*Y&3|nh})7aWDjtTC4pogbq(K1yMKNXV^WaQ#N9_=7Ax}O;(l*$Aq zTqs2;VL~Z()}g(hK{t+#W2Xi<#DBtAu8zbMU2q#80*XxErQf?-?^cHlsz>*$M>TGA ztJy}UTdiqa;Z_GXmb%qJjfHNty3yoTYa27%>hMN`TOHOICa6Pu-xDPzQe!PueMN-$ zHLwWufR8%BNOT$W&aF1GnqImm4tkchv%Po4j)@-`t0i5+eQcUb?dS;=`sj@rx3?|h z)Dz>npz*MulWWMmp7V6m_((xL0Y*|BH@AnENEm@j(G`fEo-m|w6-fHDI-(Ygq*ns< z;=o+_c?%nV=>n>KBT#MBM=+)!)-`UeM$iVYuZ_P`lPaAL>(Vwv3qf*K#Z;k>nbqi3 zZxDiHMHO*L>*E~NruCfAl(ilf=)iqqjr+0cLGTqQ`+5ZzM0LwRF#^mJtIU0(T~V5@p4$gZ&MXpefj*(k@C+T6Z}9R7Q5f=11z!*D{hJ7;eVy;)+fcWe zNRmAjylrgnhvM~Mj2E9rPZ)BGiP-l*A-k-l)R{=qw95;>8zh*XC}%y;8@B_F7F^?S z-6!V$s}M zS||<036+Q;;72bChETU5thWh$c_PU7@j$!U*+tqip7iD0fxZM1KwtXvrM|p`zP#b= zAZgH-YX-9w0SB2jz?$YKeBmcwC*eF_^1cxB9pw_dFu#k9&)So^ta0Iq(|R zx}BA|ZAo5Rvb%tQ&dnv&H<;W7bXpLkwR{0%E+869d^ps8i1G~2K<$T6bvy^PAENER zamo4=6IVb6IUA0Dfi@ItTOxB8IRu5Q=(2U_=+@sMWg_ zRJ{xT6^=$)dQdqU!z>DA{Tuq?u?HsG6-<&Qy50oV zdEzGf_;tGLBOK@67p~zvEn{1SlxPX^y4L&0X;=&r`EZd0XeQ&aLD*74jzX+>2qOAi zmOJe=ToJe?(R7a%c9Z7tV4LT0OSmR?fy0bxlf-oYo-%WEL4-vfSLA<^laC&hYx?EE zsTrJnRH}iKhopvaa<=z6Sz;t?0w9*BP-};(pTnH7$z!cnsD4e?O=SLG^L`mn-Om3( zq;lWU8(VTL@)!c@@-T}$9_Ligwd!s1=#mBaKgMCFrH99H&Q7KLv63bF^q2R#lNljd zW)D)>)lB{bs;H4!61}4SJ;3|@iGa6bM`vei=TBFe{P7eRqHwQw7+T9PX!~ zz@rF=;AAi3A2+ZpyA2U=BKt_^p5OB~K~57JWobIHaz0$ce>4zKPFt9~Gz%rFGGc%-&>VbjtMe0=Ig0x~B7*$RLxwG>NSY9R+$nmBn70jQb)R7C)) z?A;mwO0$;AZxc6KZVE@Ey`Gj6YId|cHM;*u?qPZBcJ#U+eE|4(Z(|-tSYrpfJ>qz| zw9fn#SX+DvNo;W31~y`s$y-RIgYJan_p|9=2XEbs6B`P`g~dtk#mNN`ZO==GNJV;0 z5ja!I;eyAqFw7>eNh=UPWDj-%N^sTKXu%YTf`lWaNtlGAyTUd7M?&LYKP-2L2XF1E zrJYGPg%o0}47ffO+;bJYm-FHrtToL_Ns4{E6zYE1?R63yATzDL@sO64U>oZ41)y6{ zkdbjKeL`qX!P>7}z5+FQRP%c}Q+&LcH0U9_h&l$FsV_lu$fud6Et+ha_K8JW1E5^$ zoYT*l;^$a6)0k%~In!fv+??rgWSwKqSOk}fp_Qd!oJs$zjWdyb2h1M3ls0aG&aN!g z4BL{Z8IzvNUEzSfu8o^tsUo_A7lWzm!538Ra!AYW6DLt$iAOF4h#-*teMjc@A4!iN z?UJ2Pz`GR;E*5-Yx>9(DtiAYF5|l0LJp<+E`A5zKwAwGzpNXJz1%_rd`wJcu7@5Nr z=4BX9gI-|dY&b9tPzwa*TU`#{rty-l5%O>k^0500QryVX80KS`y}L*r+pdBCa{%`qT*q;tEazQu?G>ng)^80p-4-)W{0LQx09xe| z6xO4_(POTCg|9IFE~-#-!=*3{h#8O{rA*m{-Amx>ShihOtT+HW(#b^a8rljO-9Ki_ zbSsDbif~=*aK@1zkx-CP5TRI8?ltd<)_r=nT7{_-^?Xdm&7anOg34it3vDcd&g*88 zV&Tn(hposI-8sqtK{t3hW@rle?f4RDFLi=p-(p`bKTcGa$leO?pW*%3!*}OfKMJtK zJV0*~T)qaiE4(E-`6b5tseE>;amD1i$%h&LD^gi)fJ0{h7KZzTPc5J>C}7=_R5%uu zkBuU_8<73$4wBZDr$z~6BMgt4TpkI)PnvfZZkwi26hj?CS*P%vi|2Ro{287#us>V( zDfD44flWpGXZ^~0my)&;uxx`BpZ^qujD~;vamCsnd2URxN_SJ9@drS#=ktt%cVGe) zOxa_fB9-_D!VXP_*%!?hGOU(2Js`e}H%5q(W44O_T;??qUTWG{Ub_~F0_|&VAL;62 zcs3$Q8_M_5Y=~L*tv%vpUUvD6b`^G{+fxtIralpSOh`WG5Y+gbGBGP1SFF9!1;kqM)u(ZNJvc0#Awc&^Vw z=Vo@A4vB#hP5Z^t?ZoqvRLps8?|z7H0gHWv?DBskcE3nfSbRxSD)v<d)5!}S{BkdZojjpG`DKQ)mZ&nX*!VY;vi!qP&+?MJJGZm(p0-YK zg}iyB6Dpa%bo^1Q%p>H;poS7O^k5#LNPHSb$W!|A2q6M$sv{1kwuhcSxO}<1ZfDo* z2g3{U(aJ~9GHUHr&+1SVg}#zPx7zWwlkFON9vtcB?U*fraX}k)F~&)TL@NY9?~VnM)SggCN{=Ifo{o`v8?Ir2%j(+*oDCD0nJiaY?}5PdG56N zxLn_b<(6Cv%RNfw819pfP#J$2Vv`GGVDs?uA!w);TWrQDSy?#4btt=d+vo8?l?kc! zTl8#JWgg-7R3=Pm$#O4FWn=2M>Kn5vQ(ic~8XLxM#lfaLY#6^0J2w0c$;#m%NrZyd z{B0F%{9x1cOaBr<%!1hOsQO!TBZ;G0Rs(?E#q)hfCa`4al+RRpo6=jG{Wmob4qHwm zh_$dRI~$tTTsa}ko4?iJ{j;XOi~etPg_lMI1!cH7l5uh5TFYU&T?uUqBqHfHY1pX4 z^e)*i!phy6UE0d0Np!KS&RDGN@smjdO^6OZ(Gx4LCwMZQ_ux1qU$)GZkrJM~QU%5J zkK*%b5WkZ8dUhw57^}xrZ1}+dNK5r`iB!fbBVcuLQ&0@(dpr^`!&E#Hw=QzTFMI1V|6H_#oqKbj{_Tnl7-UDtnWDjxN7+s%S z_mMhfrK)m9L7oAg?c|Qz)AcE#;D6V+L*p4&gkzx0@gIuy{}KNmj=?I!4`K>a2i4;O z?SY8j|Fd5^s!OXuE#STN>LiSN=qtmw;;$JOqiYyR(WZn0ajPzevdb{2;Qzt-pMS-y z)P0J@7a7@DV(+>v_=F5(x7?@Iz)wiP9y%XmstIFBPSI38?v(x+YRhDNhi5c3$w_4d z4cV{|RuWdmpf|Lgz4V;edsFOS{#O2yGzL(cKYTLu@t|x&G*p1au8Ym9B~2s~Glb=| zZGwp=^NT1k=B_$F-!yWwKwkH?DeH#G!S=!yd`g;z2gsX-3eE+1nE4Xzg^gdf>Qr=ft8e zj~p&0;lD9Y!=^#Wku=h*;hCW8a?-+_6+ENPDdibeP9e`Ib4)y=$jRUtd5(b(*QTeR z8sz4aVfCSy6Sj~nGR@D6-Ow+D&d`{S1EMv7;W=4)I<($&8SjZj`$DBkP~B7*-!@mm zUc6&3Ox%svK(!DW<5kG`P>R%R1ksn0_x6kJN^tjl9(nU` zMCgaI&|L0peiGkc9xGFoalTLG*mr|xL6MPV4%f1EhiUe%*+jOK_?1r>G50a7j!m;n zCmdv-XRo=BmhhNyoW?eN62eTc-n79~`h)M=bfvG5{uY#kEIOZE`qIq`WnDKoy-5!c zGy1ml4v7@SB ziAdi(q$u83j3d@si?tPzmEF-`?JU~qEzNKnCJBbDe#6A1uM52%$BeO$+mABU_o%es z8DrJDvR?(hAn8=`vx*C&iXzhCHNdksYctdBp=Km#c#j9q%5RADkoRKx7T!lg25M|Qs`7{;kCr?Ja$I<#PT!g~a;C)mcRlV->qCP$w%jYS#Ekde`VLCg@j!lZD;&Z9vP4zt(7j@_7nme-+ zzhdJF&Dte7euq^>J%K)D8Vkag<3FPL=^UDAHl*>1NlEjV{FlM|dq3~65D6bqzcsbhqv3C_~Ni|JWjad%t7 zebse&pJDuAC4BgkoIA0ivCnox%(uJxuo}(u9)UcV6zV)gS_seruhPc21s0PLp@ns~ z+(wCp3jzdwk800PhKm_l@oU&5o(SGHSxHlMm%BVmfn>2%d_m-W6jicc4|K5nCw=bM zdRB(|QM)&3O7nk;T+$SvZr)5*W`q^72`kzcvgM7$}COn z{0eGn>Xv9}!beY?8nh*vsm7tLa>^2z=OuVZN7ebirN;HlQ6EcTpzaSTt(gS&zC-A` z>ImvpMZT4~HHYa=q}&Q=)31;={SrfxZ2A?72V4Gz5!9eOLn%K8G`ply4lBG}nJC9; zA>XZ>j)>m>0PLU&2tgR_zc!-8&UIs*l=D+cP$;b_d^Pcv_&-(V7}?UmbRVHioi{Ma zbypO=Zm{sl$Z%$nx&+#YVG-wo$2ybxmOGIVq@jVf0MS@qR<|qMiCR2vnFKoYuD^4{q%xEcw>a1UtCBLtEVt zqIrMZ>h3!NU8Lgj!}4~eyOc2BFysl2T&ai5f%l9ic=X>Xiy>Y29>Jp)1x*Ir%aje6 zb<$&LvMwck40!ZKYTIzeYQmc|C!CuR&vdVV0noC&`yAeG#`*1ssMNYBjtuj&(95^- z=H0vp%GzzHPgf%|doILZ#)^}h*BQS(H@nKBqO;y(l%>=htYQT`tHWx~+hj;=)G`F= zH7{i#a)E)4XLqaDpUJ$S>N2FW_uCntVZ1R?=1E_*`gulKNYqLTvRO?}@L?s$%JNK^ zDHotp6_r6z^~tOWgvtU|lU+#xfAsO`P$EknIDhLWDFVz zcJU8uNj(#qFT;q=)DI=~L>Y@6HcL>r8~lxt$sY?# zUl#V%!1sfUq94>O=-MDc*Q!C+g4PWKt=oI*J};@!lph|!YJN;I+B0?E$6+48J!lgz zH^yc588Z0k1dHKkTuuBRJ(!%;V?|}UxPKXwpD8+-8$yafr#`tK6LW&t{S;)wl<#8| z3Tb{)_FzP>4-`^u7cJeK^4bVx>5k983X$a?hUZw&SfGdPUo*;CP{8*TMGzU_c^eyl z<2KoBUNR5Etfk}I51V}>gvDz(Qe+^)dQEY9C zFnv9MdLL{!CYbsMu;Dmrio5o3!=VHX`Y)2in@E|?4}cP!)mj-vFJFEtPz9*W8Bt7i z5(NTwP1*S(K2c`{)J#VLuf3)OHgzs8lCSnKccrLIaXL#fp?#u4S( zSzG2>2cIq=u_Xs%Dya|Hy&;@3PeN4J&GCB_?(`Os12lAhnS zJqKEJDZKsplnHPQFd^kf{3m=wI4UF6pC|NN187eVDkHY+(*R4?pI~cu1T&h|SL%!N zQp)xEcV~8(p1Ge|m@*&v=j(ndWn&K0hRp*lW|mnFsxx&{JJZ-p+VE>d%06hu_7>U3 zSUipt9MRb>FCUeCA()fXdVRz~6B1Pq;k#@G1?L%!L#6Up(Od!zUb>ueltk1zt-5D% zRH2kQbtBt_y{*u7+p%L)TkL_2!e{p#ORKiMXp_rTc3rzXrO?+ke7pl?me*>k7t>V5 z#=7d+>uO6$EedM(<|Okd!Jx2tqG~2F`OnhsqaX>C?xVqLmF+<;XGulqQd46S5*rh9 zUI@0fs_a5a)8c{hgq&Xo_r&J^FYH(4y$2-gn*R-b?>~(kXkwSO%Z;xFD`(O^MI0OZ zwsP;*P4)Gw>OK+nXx49VJiiGR=hheJb`%asQrEv91cIr%N}gej?);3rTGv0*G;6>( z234|Z>-rGoBpLv5E8^;wl0s^&t~p5p(`}@jc>}PCX)AD=$tc4k+2m9>a|c{%-EGSG z(s$4x@}`izZ?&9)^jI2;DU>s301=K>hmf?uYoCuTtMvG^yxl z=eS=Ko$X9T(RIh2w(nf(2L;RE^Oq!PQ9Bb6xxUqFUw|`R)%x{>{OLJ+z~tUDck5gA z>o@IMp}R~8r)i?8MTl%Rx^{(dnrT?TD(e4J;VvLf!f<}eC!K7JW8o&aG_ZzKZHkss zb{QapIOn{v5@sYk@%Ke#u zzPpuLwqfh>!raErEo};9?EaW?d;YlPt!Fx>i0st3)><;MozIfyxO#Q7GT|}`C)0TL z#z$`1I@X;$Ws5G+eVRV7!W}fJ-dFX(3Zw>ULfM6o<4*UU&iZYkUQg%Fm??{PxIMSF zZuJIHmX9myK82mVWcfgn!|}k1Z9#51owZa`@ydq^1jtehhM&q!)cI6iY z2<10}`wSrvNSEx;_mz_4X^a_D&h}bc^=$YY1tfiQi$isOxg|MuhM%eaBX!;imSF$g z!R^PvYPd4eQGKv{k%WW6^9__D73)8H|GD6^y}EPp!k)hY2#(rN;h|1MHA6LMp>NU> zvwnST46AN1*{_VU!_rm$7^<%Zc_RmDepr6IQ0gYXz&sU#6Ws<6Y3a3o8 zhreK9LmUgjIF{8f(6fT+*a#FqW<)Z|BFbd@!JPPlZbIaDJ$3z@j#;D}y7l)RZk)V5Cz^M5dNFR+5{-7w_SKFS9+fYMC$d~8_s0~jcM zGL9LtTHxX(;q)MFx_=L7SH(qWvFn|$Mf>sd7of$QN3Aq4ApXL^d+1Is zax~XPa|=cX5;uR4<(WI=D&&q2&*g}iJHFB~j|_<#-{OXi)+-8#MD=>P)uJ$Cyq8Ti z?@2E<&3c-R%r$Om>vNen&)(#Q91i_s%RE7rAgRkc@3+k&sG8gJH%t>a7vLo52YRUA zz@F_mPVlz>M|dIVhHs_M$Y&_ck)z_KcNyg71!T72*IXl#b%zzk3~(OeL>pfP{W0T) zNbK1TG(8x?PT>O#-s)o#Z?CKvHbuBp=@j)DY$oYkirS0&N?a7Yc~F#$nN$j{-uD&r zVNgNegOhjPfHYBmRy>M%63`WjVc3&pi|*RUe`X?|sjoq6H&N5{Wf&OsYn@vL9j#tZv&qxy!7sNp%zr#`P0@z6lnBH^|0=xV0%=gMgR_EYin}iD)Wj0K~8FMyi#lma|8V zdelBi2o0$lX)fm(WszBv-$;a(ejI{04~>bdHZ z2?f}2vuuGr5inhVhLW~^GYreJ2&(c7&8e)|qDs(nDY!fb>F`g>Xj3sc#07xRXa#xi&VxkwDy%lvK$+}mta<#h5XriE8r@)f_j-L8Tl8}FFa?J5_f_EYi1#`+W|{SzW_ z=TMO>+;-RU}?}26tsP*A7RN z_%iZYlJ3vciviuAXG6ksTLLnfjL)w_@vBQ#aGrHWUIEe7aR*ZUrnW zkQF#MjDP~tV#3P-&Al4?mSwq(jT`A#BJS<;hLV)BdYYjba^-DB~ zW*$Z}zlhGJ$E|z`k+RA>@;!L?e?*>tw=z=xFk1eL=s?l&RlJofTiLobTH#pq8rreC zv(id*)a(&sERoDy%LM%b4{zLBCtJFbPtus)7vW4b_i6pic@TMwI;?DGSmM2;`FBBG zo|EL&?^hAg#~M1Wrk3VskO7)a$Dx^K>H7lDm6257dy=q6JVs_t{$E9C-;u%p4rna8 z{N#NgL1_$}VB*KYU+Df&bk_sjh~0R9@8P?1{&kj-Y_evc9K#!z;hz6x2>2dI|DcNi zKJnJTe5}iar1tzOHol#`aaX+L6p0>h-ht*%0-Z})BKErX+^>Hlv@6kCvsU5uFi?WC zB+g^iIApx$4c_uOEkyjq>xp*^nml4CPL}CY{MIoy;^f|lxgX}8>^E6F@pHcq^$3P< z_bBGR9_j!;BiOS`zjIjO-SeeqSLpj|)oxENB(+gj74AKM1BSWi3fl|qJ2N%K0!^j( zwXDW%rXf2plODy-x%&W_#zE|u78!qzR;q|4vY~!&;`-O9xLoQ)lTS^>HNx|hPtrFL zWcHJ;u$t6!!dPn!`gCmZk~JZ0w3|^u(@!uH(LBP6Zbu5TCA`dDqh-2NutPvF6w5%w z$?UgMg2?~uiO|BO1V)t>$Bto_zE~T&w=P;)XcKCWE6haqAl-)4!U!f^ceF=Lvn+Ls}d9$5V?*+UWl|ZTIJ*#sx^Z zJu-f5F_eK&|LTVc!dDTZjE{gmm6rHFla>{(i-@qvkfBasPQQz5YhZ3$#+Li}(1Uktb!^J@AQ<`91ZnzZ>0UYW zZESe&H7I#tumT%5-^U!rpQAd|;WX6O%L&sm9R#tg9fl%u5U!GXx!)(cXvaFebxlx- z-84s@Tb)-@u*p2{0cXZPIqur?#wl?-o;R4a#^%X!o_|xc?(j4jX0(R-a~{2UrSxpD z>CI~uOu_USI>yT+9agbKOT`}($xgRvb-DR^TOVVdrvoO>n}Y~8f{MzeRyCQGWx1>M zmHC9Nk-}$H#;eL>IFG}u)geGh<3tf%JVTbrc_iQgk~~M?W}?!+((kX*_749%NsVv9 zks7{RpXo6tY}5@gUJvD5eyxEKQsh8+Xcx=+5 zWT=a;kr}L>q2nljmuPtBTaKfeYT;FyNbT!kV+1_C6mwV==tGIAbdTU?h~zMZE$^4V z9+<+VbFUHnye5BjgRb&4LAb}FLMl%KKUb1v!LlcZz4D4>nqk(ygB+MICL65T+Yb_r~+e)dq_D)+7 zLBIHw2TM4WFf;2VoZ8#@GwjR%J%Atq{;SH|Y2CQC5B z?JLfkJ5P}vUZ9=*PKN55wLB<0@4T#c5bE<7#L%CADbSaELJnk{x|LX(mA;SUPUn7L zA6+YmPBey=l0Hxi-3X;@XoAXA&;*q!p$RHeKoe9Z|8F%x&jlzq<4V+G_EyZwyP#le zU#uPNlGjzVtl8;iaLQ2Jh1t^~-AqvQ{$129zSS(thDk~92~pSjBeLYqaF756|7K z3(uV!6`p%i6Q0{PIy`rISilx=IM(~A=xMF@OjCHaJ3LLR*qf|;=f2VLH`$1t`_xl! zvWYwQ(d-1txZ~peK~EWIrGBx)v)^AL4dje3Mc!u3c>BBJydrmS;jZtB6&A(Y_DF5< zydt-;u=zWSk+S?lW>MTriquI&zi~`3l=^I(wXjcwwz0)V2wCGm?5n}e1FkxXOZiZh(wNhyn=bcP;b|7qc(Grs>(3X6{aC>@>; zWoFA0yoCtnS#8rEkvA{l&33%u*X)g8$9X-nBdwxe9>HqZ$wzjH<#>u?CmVK(Cm7?8 zT4+Whym?4_KSi2&s{wPOaZ1aOu9;;Q|B3jZapDxZBHXoND%{O)Q$ z*K~U9_t2(zhE$3ldVND6-~Ot|-uogj-6KN8KSP=q`gtC+ajUK@h*iF-gyE5^%u(%W zQTI%avqR&&vVwBQYJ^0nsdfrw1eWESBZLS!BxkM!VZIyz)`I(M8?bj3- z=M$Xf@aZ^)p0`X#S@=m~_cTVVEkNslaUZZ*x*;LT{QCq^s`vXRlktVM$>9n!%NeXk zjzgyRFDv{_gj3KAk4nV4dKBgW@Uq_h(C#S8&mz5_<&kI4*hXg@6r!3TlnnskN`@fz_u4DWkSi-%%cS}-IKIIjeTjU-r3A=9T1@vNV_UyUovxtI`Vj+tQ%PB6K0r<-1Ktx=ncmF8l?3x&98hMtY0B6F#trkUyjler)X z!R9`uGGhAj1$xVTG7MNUu9P$eI+lEMAsLBgfMb;<4DOH!osmAt6xxf?P5V5u;uY=8 z+}u^^^B1Q-y{V$~DLLxM*LyOGKT;xo%^$(b@t6S>#>KONJ(q;KI)(>5 zgMneO;yq)egt`O|1^gh`iUrGGkkh&BsyjvQ92B>>l zf0TGoh^)(5+ovS{yD;_)_N!30fDT|AZz#KpWYCBM*>}F!V7m(HpsvsGmLGR7NBHjD zG-Y^>BuZh}@coOS&n%UZ#Qa+D)FfxmaL&H^jWEhpaOk*@l5=Q{0~UE;^%l2%!FAo% zI}-H?1{mpK&#(=04%|0xGYia|IF~zICz$7~Jxds&7D5u?C(X0z7^fGZPgJE>3k;Tr zxA0ztYYDE|xRzmWO88|H$v2pxH82i~eG=MyD_smGRQhB`lnw<0)D7+S--=j$wml(&%RZhH%f z+%;=;FdMZzlZ+g^;jvNAhcOs$F1clTI@0s=$pD0{WnJ-Q_4#LrOP&Dffzaj~!w}Em zFXPgcHF>N5tl;HODwC<~d0Btvc+a)T-^Z(1S;Hu(9yO@DkIR7T_GgW%;>)ET?RSima>J&)d z+g0u)%AGnDkFd)@FjS=7sZZgRm(SUs$#kC+^8@Y$I+KH_Z-MVFZjuvyK+~`|Q?_ikbDHeFex+E%lo=j$~pZ}Rj; z`in5cV*WJHXT!R|GhR_glDDWBvR(vjAIwj_hqI{{!y$3wLag=TY zbKt`RaaTX|eAxL-Tx3^_@^DaG&-$Lu&Uc~zvNcCZXe7;ry{q%;UXY5grr?l*nw|7Z zvbC2fU9Q9BbW9JmZoV;|AQ)**T@0*S86{T^GZHsv#xM(>&G}-mlioRW>CJnX2aq2Z zz1#d>(mZg!^|edg^v8Qb%CE={Ia_b{MD&0|%{t(EPQ=kb}YDX#m$^%Qii4?d`e6>2otftzW^% z8N)F%>cY!!kKn+o zn~4!GJ|gMe@-FfbaKr6RpoO3?$2!w3SBBo@@uh49Y_m1Vixawnw2q!IAt>C@qwhRw z65^CK@uM7doesyl{7$hZfoZPF0px%yarOx~X^J)6<1&paMSImXR_5Y)2@Asj z4Ax(d6e2%EJ9CNQHKiu`$$sk6uh6`cXdd`oBm2q^NHC@ZPR4t3%^X!DgA#i=JlF7W z1MN<<=I}FKislpN^=x+zU50B2UOkeeRb_l95--vGl9z&@gn9Y@P{KTlI$r^=jdIYW zZ_FOr?VWF*Phm&u0XiS+fIlsY%pvC-j>qz^@5BnwV-||oW6|_BACC&JAPzf1HOh2E;lf1`yg?IO{_h9qI2hES3%oZtVL}u zpSsN+7d~FYZV=SFrKwUgMbhK2j|)Kvf{)+B6s-C1S?{Ni``j8k-?`;-OL21S9H<|% z(Op5e*bUVolqbdG=BBjOaxvOAo(pQThmUp#?bb9xuDWFkC}T9u&S|)y-CWl__^3_( zPy_UjV%t6@Yb9}-JPtJQe)LfX^p)qV8J_kU?$>ZlIzsuIM`yfz8K+{rDl3?0Nsza} z*$@p^TwaE1ULd*X>>VIBFSFTL|K?%dOrSx8)7eGo>#G`OIF_&K$*O<3s_rKc6($2B zt?Q!jANo6~z30O#Q*(4PMg-69^);2LNwvMY)%OKMh#MMLo{-8`Pknk{E-6Lu9DYff}JzGJ^=PU=eQ052jP{VKQfBQ7YO9>N z8wJ|OglJY1$Q&XVT&bWckKH4$Q#JPS>PlHr?T zN`nH@K3SzlZ<8V6E5a*Uic-J=dR^5|>j!CrTS;N@&JlA;Q%$?V*w~qcwHYrN8p+@+e}0&!^_-GQ z-lXPf13VHWYi-^m`sOvG4JH_#*{SS?-f1|Org;fbm8B3}?if90z^y8BT-JL% zKvp<@3`3`PlGyR$%|==p6#lYd_KH_!bzRyL%XGgiDwpG!iY<>b)xQ(F$w7Y-J}6)a zvTojkGseKB4HLru5>=s*yf+q^C6zZxZ4K=-9+$FEdoVA-a-eV`gDCzT*B?hj%0amP`n!On_H-9#P({q-7O^P<4SfnXWC zMELpTjm2&cn-%mVAO4s0oKP2E_mMp>HsdQ1hbDti23-bMl-Z#hSNrNS_0=@qza;ND zyo=AcK%Ttef63t+n4;%L38~hdYt+s!2&o8-yd}?%=2NZjuM9%Um%l}EIXv&VAjqk# zhR0`5KmQJ}dnmwH$^>YjKW}D2+w9U}+>A9-{Jt7!$YTOCEJ4JLnd`WIV zea8G@3_Em{`DubrTD>H!UF=uVS&xP5Zc^zt^}XA`vQw}d;2$}U45Aj&j}%2?O{n8> zdhPX$~PmF>?C5{}=Wb9CxV$IL2k z)8a0VgP2J;xBIYk>9#AdM0!0JU`aE3lh5!~R*-x#Hib3YMuDx8 zrXfp3Y&j2b^fi_B?{N%z{wTG~9i(>TCeB!>Jm+RL`6|@=Q-dEc!>Z}YEvwnAz6fXc zlJp?%!??y{zi)f;?%m{nn$zL|-xL}q~%@)jLBaUDBZ=^gO zsJAZApL)`tnFTWIeDo(|OLxReS8tR4Z0qnRkh;4(Iv$x!yxIJ^Z&40c^jAbkM{ra(Kq zO$XUA5Wf=o>VVlywsyZaAD6*x1zBFIVZISe)jP_3$DP~0tAF3L?JY-@!LiN(nN)p! zA{CQMz1_68Q6J#@#AU{{3g_X~_k)^*V_X;6kP>uAokPQ4`)~WN@iytd-*hyO4)ots zQvd0G*?(|SMEdVrl=1KXZ5b}9j9DG!8mSDalch1B#1|4)#sAsM1&?~!-~YfFoMS7_ zzcx|sa83LoAsh~O*r1x$LRUC@ttI+!@P<|)SPovAI~YgF>woVO7E?lSh!nsgK$wL{ zlQ8SS&~aWT*0l}cu9yw3+L*T)0p1Ue-xj1Zc*X|>Aad3QJ3}W`tSyNAZu-nfiwE)< zjrr)HSyCE^| zl^q}55Tp}(oPQ)`xJ-2i9i2mml>z(1ua&Nd6Y>g`FN}iyp^}QX9@iGox%EQ_;%9y6 z)oSY3W2Z9oLA)TfyJkN(W!>8D5*N^G=aj}-v+Lgsof%)hwKL!n1r4n}E>fV6{JC0A za*#&!U?0s(@*%?_#+6r1PIa=FX`h+#nq`=oyjfv3E0N$84*#UN3?9pGPMtgnK`%QS z>+C)8g#57V74Iw4ollbwnZ=QThq9skxZxjCI-lnP_N_Q23dl=Fd(DIw$IBSES-|xsOAsCUU*PJ#tB19ehetdj{!ru0)7l+kNg;v z4{nLhtH}vvnT{XMa9|I1^@QZ9;R&)y6pYgSr z6|L`9l9pGomVCIC{_h*^U_rth2hDQVH0}HEq37&`=$W#41~5M0^hyDxKvrqLthLGL zl9CAfB$Z9}m=bWHa6Z|0bv5rY)gVxt^v>Qr=mTIFaB^Qo14&*?k95Dy;S zo3jaLI5wRTTiZQ9U>*Zs4ZOMK7RN2_QrUd zG?%?ewJ)Lvudh zsHObR_ec!(kFxIvWjFd|1MsvbyH3(vtHqIhgE+ZB(mQB=>z+C9CMqmo+BOS`AY&8F ziAnbPkCE(Cl9iK z^o0^kz~m0tG!yz~uxae2+mOu@na#32{!RBl|A6xGhfUO71&t1TO`wAx8FSgzuN2Pj zMNC+?iG*REN*G?|mRx_hnU!7>EttrXw|^l~Q-R*=!QQ?Q>e!g#xDPe0)?hJTNl~ve zTY;6>F7+x_0|Q2O76?N&8gLanp!LWg!|qC8GTictnh!$U4_nYpV=fAT-hvOy=|u8g zeOiojDX)p7d1Q@Pds~;O^m?iwcbwDeEnu9-$e3qw$`f*ujk3_Rd;L1xmC%=g@0qS3 zX6LriUS?g~xmItVd0p46*AN-knEpwpS4Qtl`zNSZm-`g$4r<-9Tm3fqG8|d*z?XGG zK<*}RX;wAuw0-IIIEd@m-=IdXEe*jvXUUodbMbZ-8IAvb4}Mv>G?sCsZX)fCM}F8b z%Wz$w>gQzQnt*5OWiob~sHGWmVIsj5Gv{AJKY2%8`V;9V;s9U|DeWcFRTPw{+4s&E zv0&k}TP+KJQ0v;|#gY9VNALcF?c>Bo=NBuklp?wFH3<-7g~(80KA~@f$gs<$!hG^Y zFUha0D&BW?#kqf36zT>i^+AGVy23J@@On$0>s&aWRlKg)yM9xBbJf!6Ul?jly9yW5 z3kfHqx}R+D;|^{a$0)xP)BXWs1g^01d#EiFGwlnBTFtGcVfGjX&Z=|^RrVaFJBjN4 zoJ#x6`(XDNsI=X)N>>#o0=L-)`+8& z@=5fY=Z_>X9qBZ4TFK)$O8Hk3egQAlJRXL~!;@6?0j%iOv*X#wrJJP|=f6uWeFrVi z_hBq8B?F;*4`nOUl89XRy`vcpxhI z`F}!tY-QmjYy-9QYjUqefFAqPThB1nXQ*+fiRZ)Cur4Ljc~I{TVW`G15H0Qy*ksaJ zcAByW0HNxyNmKGarkb+Is}e>TN@Y?EWBnq-Ru+mV448lJKK)AfVO5I>Wh>*M>-@~J{{e7vskG%(lMD~jl*>uqOB0GE zxu0+Y?C`FoB!;$b3G4*gcScZi{t^o20+%x`C{L&k6ne)JTT-`@9@n_lUTi3NgILYy z$BJv-VYxOH)L0lnMb|){^(++HtA9hWH*HGVk=eOqyPVC#;fdLsd$d5Syo^p$X6=`= zRU~X(T@?auWn$x)vo7OuKRj>L-Q*Lsb=#$2!iYyQJ~3?DCL1h0cY&YI%czpYo$+6l zeBh4G2uhdw9qOMi~v{&Re|i@sJYcrGB>?8PuhfT_*gNvN0MCA@>qsivWj z)uYW1q4-4ows}1>0{log39xt~vffkhO%!Q{T<+JZcka{m%jN7GpGS=avUl%DdGfu0 zz5B|vG;&+G*FjTi5dx=rH#O^o6jb5Zqwtn$tzDIN?h7n|t7;atoZT0J7(_8)SvAp#I ze|4i+@ve3MW#tZW|Nmx`abms!{9Mo^tgN7qm)1|~sz(wqxZ%Uuy}VqhPkikE$VIgK^(?tGKQq+tb5=Mj0oFtkNrOGI)6iG%N%;3zi)su^GW1_MR#n zgd!+LaHya6=@CS5UBhwq*{gSiyRs&INxmzLGOHmn1LaD2HtTscLuH25=ix!-$b{D{ z{kPg#KW$Rjo+c+i&UX1e8;;|4#04GWk3aAdxpr_bm#%+OF=imQ%G0GD?duwGObvr! z#P}}tD0WhMk6t()dMW&e8`>X~H^e@v92Av*O`Jez5hv9#BF5K|y}_q#WSv$M-PNGb z7EeoXax5L^3h3u!U3cd9gRS9Jk2DVxKgWa-I*ens=qwyY$G~<+>Rk%62S?kANbh1m z-i3H(M;=YyQFcO298uLnsXL=Bf} z`cm{XhN7VN8{-Jw2rtxu_hXG3=V+2z@X%mdO0uV)P!4|N17dPu*Mqfx9xgMkw?Vh1 z81I$j9nU4Wv$#Kl zdlc?daeozix>1)*)5hVRj`z?+iqhi#1MFKrA^ugGs8<=+ParR)5!+Ktx(_-rrTv#U z==YwcEvFf?N6tM>ad{(L-Z1A0GUwgro+hL}#+E%I@)Fc(K)bffdp{x4yXNb63G<7? z5aYr?K*AW;!6hVxNA`As@dnchGK0&%yUQx7%`|)ioW3Nv?yv-vxPwX;;>h9N55%Oz z)$XJBZ{^?!>r3y8D4T&7H`1_gMap|zj%g#55Dss@c<;tv{S;tFdXL`i zZNt&%_Sn&vc1tbmZI$w#6}!|yY_MZnnEB}KF15P8NB?$kr@JcGTZP`ks=sY%ujfG% z$@sf;H`RnXx>@X-XLWAfl)EP*7goJ{6k8I>m|PTk=}ocsP0{)>%=~t{Qcb!vMH+~7 zG7qH#HmX^wU459Y-S~+ULFJU_+IZZV^r>#w3cUJH;`P9a<4)Zo$|vP9A03|UisJ&c zoE}RZ<2!LOtcjD2*tliD-m~YzFtb&zBO=($h8c#7 z1*649O&AmrwS;U$%X0)BM7v6!G_zi2FdTF|M+Z-$VhvkI0p(<)8EL#BCJ{mj<&ChT z6H zz%oz*!PJjgQJus&&+pP=zGE?OdM6;ZUS;=XE~D>J`s>nJ(%-(ghqsJyy7JZWX* zr6Nkzqn{;(#=U?MmnM1}g6>G+iDHMFDBB1AP>koqv8QQJwOT<229{QG+#1j0u9h^G zQp|+ocArzyjjf1w-}zXAlWlBl^T*ovMGW2Pq)xGZl>wLZLOLjYiQ=`N^=CLJsqsI68Bgp4|0*<<|X4ZYlSGQ0HIssLGzMy9$=t^5zuF#=MzaZ1wUkMS#Cd8$V| z$bNMjcgKg>SlyHQ4%iD4NFlK3d0o1%xTNg)r=bx)bY1Q~b zW-9wHy^dhIaul=8kv_mIb)*mNVKODPcd;)?p3KzXXgF;#Lq3)=)5*Gt9xYpUSu%IA z4>J$x0Nx=UV1DR0Gnfdp^VT5q4X1m#Qn7WMG@)~F>o<>ebdt%{e0^~1xQK+W1~IZJ z-_-a`o5AGooELBMP#vw{l$J0X%&z{7SlC8SYxCU?az|g9_iWCHJB|;!cJ*hE>orUz z2O|Ovm!Ou%dO?B6Bt8aCow&t^DZ(o>^BXuSaf^h>N68H+Nub@pIIzdCyZxdhQo)!k z1xtL>)*v<`Aiu*gMoru~RIVbsJ9(DxfG-o0QO)bK2k4Eo%K9@B=jNG$f0!j5z;Mmj z$lLM;d@v>_1|{o)Fnz zt9#l~FquHIP>90DP1~hUSaH!Bv9nTd3$b5`ozgB!J+J~BZ{=#_OyO)vw5b*2!l%5S zk(Bz8E)ilFw)oFC+z|Okr7CZEH+TYFu-(H-eVwp;ZlB zT#geK)W}`}&IePyoIP=0Z7(tD<@S3ZZynuNYdKMi@~pXO3iZ+b&7gZ{PQl{^z>;(U z{&u5|4Tj=n<{bu@;*3(Tgsn|HG!~;7;)jP9L2|R1ph%GRwa;N_UC^-b8`k54p!r6Y zmm&_{iz7?a>V>4m5ea-8k;9pw!|9?~bgJM>C{5nJP+zp_{{dDL@Ac3hcW-F>IL7Vd zp|fqzJ`}Tojsug6rGgZh31MibULXNbS)c*<-a}udQKlSn8P;)+2ctn9qkXgAP(>(8 zC=X3Ne`w9`>&6#m2B^l%(!>`Hk>UHr35tstSU|mj>U2jKTH4?q9yiR|@&y_!tYqCm z+Or@qDWHx93*98JxrT$Uae$zGMuD#pD(wK+*wO|x>G1dm$<~5g>v}N|x`C4oBd-M2 z(P?qtf$8xfdn}j)z}Oj7kQ7mEw6|)s-;sNNzl${COlWe_Jv8N2iv$x8+E@C$%JSa< zt}&+y_cD2uIa$0w0TTc;?g!8kaZF?FqL=6m+L~Y@ju!2Y(pjdFHgcSJY$oGwR@eD% z6A@3N&~P*qT7Hyv{NKiJX?E%>@MloHB1!P8O(pmMaESVbCN@cenUqFrNxQsWq)j4z zU5^*Z7OQ@e6gkUR|BapMASq#I*<9ySrXkQ{2y{@?RwJoRBP4Z=Cke9*@M-W7e3!BCgi& zRJe6vO#gwc<%Gd`2$0*jz#Ge~C$U0x4jlSFi9qm~@5BNehw(3n5vIUDt&YU$ig4@3 zkkW>QS|!0muG;ul1RglBf<%cVQED~V9vmN70Kl8PUGBygmi+5HWtjRt-rUajj(kK) zvA3jM!L-81tR_kO!{LcWB;H{$MaG2)!Ot~@1jd9tND-Femc8XR<_(30kM&XHge$Nfi~Wp3l*hjBc? zB+lwUWaBt35egbt>z3Kv)3+neHZn$BfrwI}F~#^3KnEWJid*lVUf~5K#MAnF}PeFUZYQ5Dj%bBLnkC;tmA~?04eL~+mHx9?QoKHtMX!me@fA-D{ov}36yM8?cRF- zT2~wc)0~!)8VpX(dB7H2yLa_JYh5#$dJ+7sOZ7sAmN!YbwMtTVrWe!oCCUoI0f^wtvkPA%>qfhG#qO-NJ9& z0Pi+nY7||1nq#BE0n3CyNw%JLa*bpqEx}@pa7)AYz<01QGPpCpS`ZRp{a~Qj>Q@j# zyMmQ$(;zYS&JNeyzQ|S4FrH(y2VmJE_tDF|Jo4()6`K9ugA2nzUuB&z&$rm{!2lG% z*U<%mlU&znTj*$8b{veyKjLuSuqxg!wt?PzW0TDZ^)9WWJx<1b&7*Dp`cmC2agYE$CKmy2&U>sQA86 zTrVn;?fiHvT{@X!j<_>+638mdNirF9CG8|9^Hti^uTq^3){9xX5+;L6=TLQ0m-{}X zDPXdPZk|mof9Ly`d~80DNUy*F`d-pATX9kG1T?J^{JeV+`WC#CVdz#pk>+>P#2W-x zR^IP5H&u6EFS33kDqvIhpXR`LrkD#8+pGf2Wf{PY3L-|~eTcX0ov6o^hD^dH%Aa!26M#I zN2})hKu`0LdXYO_Xbdu68za0e;W|&NmPt!PC}7l_Q|-A;t5!p&3X!`i6y;A9D*KH$ zQ*Rz_5TJz>@C=<88ZI2%4QLA{zNsV*a`^XM-_okC``Fg-$2vt`BZ(-Or=5l&_D%4` zerCv{&~e5zvKZlKK1qW*ksbZLRZFFf`ra*)K;2Z~l4s&OT5HW7P zRT^CUC8?1}r4kv>2udcV5OzlVTv;+! zLlr=k+8*NXDi_LWFQ}6dUBj2|gQZ#F>f%{xFw|j&sXs4gYTe?>Qli)ngQ67TuIDhi z^5y*>ze@=5$O-&+Y5v36-#soG=A9f(_tA%Kfn7e3|g02^# zNZ642-9oVp(qME9%BMX_?_UowE@*v}a89lHEM2)B?F>^O^WEvBX&Eu9?HxnrdI*@X zQ-k1(pV>`=mP*x&nydMGk*tX?2sk1e7~&hj`r-P3&fb3k-I;TmsM2NFEV4b%f;mvq zSIs2`#rw>B_-GH(zqH1Wb7IX*9p|)G#jYRcz3a#7c;pbN(H^q@b&)I&&~2ffTCYht zN)Zp)_LFIo&5G2?R=91tCzu*BYokJk zae1T}J*@$pMp(B}N%^rZzjT3RDPVr=2LtLy{UFE2%(TqEz`Q_87gUz+Yh+i@u8j|^9*l*bDv0M1DPwKPMPK2m~J zg;X_|nTgIAD;^L#wdLr`U-&wy=fCeVe`jXerZ6WFrA~^RI)35}LJyI){PZrf-u06b z$a!i6*~X|w@DtgWd+lTRUI3j5TypFOd_|@Tf+W5$Fng?zcjS`tljPhmVgE>@w9k+H zF-6AQBp@j5b0b-&|6cY#uK6QDrXWidCB@iN;t9T=HkrqH;|iWoj6;r`Jw&2ObX!1a z8nwK?TknG*V!6flLUqqSMCP|?cGrtBF|!n~9u6*8RIzlAF7TaJiPTZ2Ox)msYF<1)vW zFv%!7oJ`Nws@1QqU{v5k;OO7;oucAZYKJ&)>-JQR?sbTlZrz^9(YGDq!mZmMEpmd{ zYYiLtc+@tw7pfTNh`8N_wY#l(1mAH*@h|yUHNh)GWwNJ4$VTRl7}fjSFl8+G!{zES za<-AVBEl*+iNzvBfz zK~E{=%};BUA+vQyCT?Ee%|=sfr^F!6^OVSDDa^$(CM<_uKOng-W+u!_$f3*TX;|9U zQOwtf>KNh3W!`c(qY0N2$i)i|i2!bI(gETl&@;Ox@KvxDS+F!(cS*mZyy8G=6am zMz`ggJ!q=@ZzbLH-MjS|JWbBj1BZ&8yNfODBxL`xqR1{Bp2cpB-y16AYWa+KyLUN+ zVTGs|X-9}x$ArB-y9?n9s5hPJ7S+|uwYx7$$&LVZQIKDg5ajg`=Zy83Q1C?X8l9m$ zv&rjry7>%G6xV8A{+^&`s@i`OG*gayY5Z9Osl0#fSyJ(`xeUknyoBz0S^lmUmmNhF zER`f@TwH|Z)4}dtg$rAdL)dBos;Tv{{9TS^i}UmRahApKkTHaCXZZ(7l0UT*?<8T^ zdL%_U7!rYxb{5Km9qs_~<-`lyTH};p!Lh`X@dQ4bIGM9xQT}2yNP`%$OTXvy#f67; zN^a_HeBzfO0%-D1wy>~P(p@-hNQsYm8tJapI;(i;P&a?dlTlL}?m5akFRthq7=&2J z1a4|CBG-|kdW!DKa7iX!nlJcShXz6TP=Tc+qSi>DhH4yvkLQ0`3Dze|{spiSE~y`p z?_8Y!?9ggkDxxTd@Zli5_&CpuAwa&wxp>XcT$ltnm<^_7VfmO)y7AWL{=<64+C1=* z0{SbVA<~|~WR!wVl^-!0zP(8_q4}Efr}8KTe$B`NM{@EdoV2nY)SdXa5=kkKsG8_t z>$ehNhmKqA7QZ^Ah$Sxoam?3T};gMo|jE4BEOemCsar;JBe z&%}tfFvKTrL{nrJ41|gunt&cG7iD{zmJ&qfqKq=_g@W_I?sUQcMY6-sqeUTAS0&9+ z6yRb3ZgQ&0TusoWAQeVRJ#eV77Q#h3yc6(E6wU?Uz{*ZxkbNCqF{1M^30cD& z*J{_3z??9oRzjL$_aM2w!(I=3qfQJf_Lbu-uKUnyKI$iReTn5Ol8A7vcxh?9tpYC5 zG068g2@};xCR97)F0k0YDb4=|LGyw)+wcrrDCCJhSg(s8YLNOkxNfx1B72+U>BPEPW2~6Iw|6%zL z88j6%m=xibk)u}ljpsqfl48#s^i7@I>phcZtG_dxre}#c@zkjKDzNG7nR$t@`w$Zm zA$vSe-X%Ob260BzGa)2!)}{ZDz#IIB{(H2Ogg~JES0$dkbVAM<8#HGbND<+tk|O@Z zTcHQFkbI}(+ZqAYe2?P=YMHr+Md%&RZ@lMUi8$~rWIWA&o@=~I9DoaR)9*eBlh5Re zbF>C5f5c6NoBa)+Bc<%Y9#ufDnA3`o-o6ZyHxZA6KxnQ_Y|Gb*0%bmA5=o|-vP7<% z0SRNz2zi+iR}}*zPytLJ(a#l8Wspoozs4KXhAC6gI0i`uF5}rL8F&}k%48fUy+-c4 z)2OttGYO}=^pa{Z%Lb#Rwcq-URb=AR7^wnE7JlQ5mu5gH0Rub?!-5!Kjz!0Ms=(vs z!as%fS()~0ffjHFZGQs~;3gdF!o9c~E&rX+xc|IVK^hkVn@3#csG-JfLfdzbByIoh zrA;LGH~vGn%dGVVb50Sd&i1M`eVc6YKn99;_!Zmz9O+Z7>7**AWGVi8HqxG7{5`jM z&spAe&`&#tF;^re{CJx5%i^K_8c+J`%|H6fIOLxOm$dOXw6W)yA8w5CcR$y>B{AU) z0Du=8ExX>^wE~P~0*p+YtvvVrNe+L8&}JlOu3s=~{tILAd4eb63`)zv|1J9P9&hQ} zjqEi2{QlPr^fg`JG$zJ%nrDVHlWx+d+C>e+p&~BO^_ri-cn#IU4|f<=`kLyBs1x)T zm;gR?UED5aMw^%WF)nD) zyKs2Sm;KzUqcf5y$l@>ZD^MxcS$%}<#N`gmH*F`*1Di`?Ij}wkc^KqDr@pSQI zb3rcRX8h-z=r5l8V8F@w6@*IKRGtfi4~R(e-Uun@q+Q*EtQNeAv|B{Eg_VO4QxrIG zw+id)DbX)SJguTqU+=+|a{VGwbq%txeexi_#;*%Z04z;V#%4&`2-gZ!=4-H%hcW*2 zcZNTlCE;Rh6Q`F;pvEvdC zCg2;NscZb}Mr*jU3?gNdK{fLkErYxwuDafct3!me-gN#4N5 zCbt+^b#jt{)g)&cSk_P&Y+xe{g(?FZnVf52waJCB#F}g~u=+3M+w_cY6B~Vg6%%eK z44mZKIH~*-*ffthaK0qD!oZHL>Eqb=Mm~{=VzAmc{pYhMZJvtUdK~}^IKeY z+T>5MhHk^N7SA&8da8XzWo#YHagGE@YT8Hs%xGkKUBP?+jn+!@={{zc`DN%Fo3>rz zdRa9~!HmB2D_^H%CSt&tzv%-7X#_dJDw(&LiRR9}?8}gx-6*Y7VsY+aqw_BD7fVYZ zSqWtqu2YCzr|e;4n28rnB~YMpkvD(cr&)3yd#zV(tnY>5|A|ex%ziLYAlBw$E3k)} zO%L<~y5xPXXZ^I1wCmSC%k=XM6lvo6&5`*t+F5AoTykDbqcRQM5)?5GT7$4*m|MK# zH(}y=CYb$(ZsMSsuMRS$BFh(~19FT(j4CJnDj--RqjQW?80k)F@w+9~-IT)!+wA!e zE6O3qE6wt6ZFJR|e#m;`3n?39P*8aNGRr1oj6Bbz2xN%L*Xm&LDzG3z0#Yi7uH%%T#A#QNwac}V$N9h<)`zU;W2GyH zxLaE*6JeM-l(p+^a&vsi%d3pN!n8}8aPu76#wz| zMRq7ArE(FY&;Sah_-*aV=5#uIH|Xv_r)ldGeJXo3=MPF;KZyu5!+ zz!^XA;*FNi&pDg}&@~E#YF$|%s!OO`ptil~+TB<6$vqGTiN`H3*~`4E&)g}94N_Z> z{%tP|&B1e6^g-%!U?_0steqlG7?-$a07y(OT)@v{CZM3^_xsimGozrp*>kkt4K{D{X;$7N3zY;_!9s;5cJ#UaDf4bhKSEad9G=;je|1D|*@8J(m}5QyldsJ1)Si-> zG%&)py}Yoe?^mB$Fs^>vCm|O=G_S&&NRa4F#OS;z#CR@--IxY}V1r*h61AJnY zVU@IK+03?)P0MEO9KrYiFzzhteP?XzRF0)CM7X76siP4ej}03QVf)5V z?wRLvQGE-MWd!%7cG5RKadw`(e{L9&-Mn>Pxiyi{lxnG45%tFK|r+ogCYy`OO zC9L_mKD*`)r=00LAmO6?Hr4tafN1Fwzj|p?Y+&Ot$&F9_*K{|w`WN)g zBu`%GH?T{eqoZ=PaX5~|=PXk9sBLKjVlq&e&>W$&8M>p;D+!~0VQ7EM%)y4K+8x;B ztY?&YooAt!Qf2^+ayDsqO=~~l7@0*zq0o*@boPr?M^Sg8F=t#w*rNTr%0$2L}OO5NtPvJ zR^t&Uc=37F=69eP!p)@fg_s7NKBzZIt_ul-k1T}JB}MBppKcC@jXX48@p=fAk+>{N z%t*2H`cFFeE#@Q1hwN z<@{UQ!Dw*D**QEXKR(|%pn37Ep3cF#SkOPWl$L!$;bg|-Sj8~@MZ_aKqeKoTzd(_% zcu%cc6x{*U@Y{atH7fGnxD%tpQpDzsV0~;98}Y6cw+I$?8WDJAN2azFu}(2NHx*x? zIY>6R@M8I*3gv0bVxuAxZ!)ZSwLKsEMfz$4F)}8=@x}NHGTX6YUAfh{d;L2&q?R3X z2vlp?U#aS21WachA+%XOB5*o2{-*80Ga0Lf;ab$7s8~KA-a8eh?SaHEeIcT9ElXgP z7cZ`+0f)xFO#$BUbkg7spH-D#f|KNnH*CkamOO7H%liZZ6ONdst0QnD&x8~05)&!M zX|db)c`hn^?`T-Qq};v>xF`*Ie**7+<)R|U`#8M+14~<4dp&9aUPfUC@a2r@68_N_9maoFWxJ1 zE4SHxTeoAGvr6jlU9@*w9_ioCv`Exss7+L)uSpx#h-Os5+p?I{rQ(u7`aW7}YoKpC z-5_foL)J-L)?w#2KO!=1A9Np!42$uM5t*(IG$$gAv=3=GG=kGUy1;g9yRGcl!c8k| z9osIg`-INe$+x}4sPct46&;0_mTdDOpQK3*PG`!qMNT^-kT+#izLqEo$Zv)z!`09B z>J;rTXl_0_AVV2!9q|iStb3w+NyUo^(|HIHtox@ABh3+5;?+%Rph#{p1(4?Y0Tx_& zum~Xp!3W1htSJS-lYj}q$nZ0tj$;T$rP+T*n4!Vyk4kV}ugnszt1xj1*+SR{kqSDh zcb#F`mrVIEr6k+|({lT9u8KYkON)An(Pp6j!A9B_UdpwHGwF;7%Ch@Rg&gV?3j$>Y zqh{hx6r%i732!WXiCMyaP+2LeZ4rr7xpm8Ah>^)fQ5A{Z&*0{}R_iM=9iegG88_c0 z7yY7;jlhL?SidgNBkPCy6UB59D_lSq1b<WnATnwKC7I1L!N@*skEgNebfe+W^pl z(0Hz7=+1}(-GJ6k0x7t8Y1cd!uUTW~elb zzh(MC%mg+QK6xrN!pUJ#exOs|d{K-~Z0s;F<@z^ycfnf5IIy+prC95*pe1rAQTbGs zB>ObQ7#p7O9p4t2DYk!(B+zsa(RjRsT%QVfjnn+ypWt-~@cf1|azUuzmbWgJL7o+4 zVsHPi@BjAV%U2Bcr>u`sCPCR1j&4wK036dS>O<#dfL#_0>c4@s;AJhZ9)sq`bRq5g zFK_xJgFW3CL7Aoi-vsvzjr8QaJ#YstgEpuR1OaT80(5U$UJxBt9cbTfjiBwBmK9{$ z;UHfmEG6P(!$!$i!XYN~)l_l@%7Y3q$Oo$9S1>LjfE|$d5#1a^<=sEM;k6W94n3#a zvoOk8%~r0qOza?HiE-OR?mU8x0dl>6g=ij&%ZK1PG{*r~NvgI4%C6Q0C>Jhqmg$DF zN-6bTC0CAu?Vr2y?=Ko6Nh_TX~$`>ts;T> z*E``i+9k2Imf23^o}@EA^Jh>Pd!4jz6SyB(DF&taChujis6-py7`Qk29k^z1dvdI_ z2V+zL$yFv+AojVg+=1jfMOlD<`vP|L&?3L|tJd+7*O1CWxTv~eSiLucnuw zA6Q5Zh^MH_;2S=Mr>#KHqcMqQQQPAxqnLWWep)@<{4~}~Z<}xHQXaKJ8X$BhT`X7=tNhv8(pAirIB}<& zq<#0c=Iv8MU;$H;b_NUx|F{i%j3m)$N6R3i*tD+%z0$Vb(emR)Xuv2}$$fC1T4p&$ zt_Jp}FmXL%n7jac#Zg|{bVs}ScAZ$W^c0KJlVIana|nnPSswp!d*o%Q_nefLcWTW& z8{s*y>PG-dL8!leoJ@?l>`5CKs~EZXY+5dgl?g?laRTw%wc)mA&Yuf9<{X*i)!+lT zB06~!&mj6~Ck=+T78>$=zM_J#1-;l+VyQ{A+gDtC)W64TZdPzcKFJe*iBVwlW1$p(arb1EsiUx@ZI!j$${;Sn z_TuIin$KYqRwmFfT{RdJ*o>fmBfex)yv^BwD_P_QX1r>(CFy;%x!w|eM8DaPrH*c8 zXk(TpXTJZ^?M3s0FuL#yzcG0rZSoD!Bi}<=-Ukh>8uZpZaQDOg0PY#MpTa#0_XOOK z7568%!%c#F25vIki*QrmUWGdW?k%`RxZ?R~lc&IC;7*0Bf|~*N5xALfW8g|w-k+QT zR|+=^t_-dTt`FRNxN^8J!u5swI$R~(Vz>&p>)|qRx4`v-TM0J>?jE>r!mWe51MWv~ zE8!l4yA$q7xbML|3)czvGF%SsFK~Cky$yFaTB$u79QaQDCsfV&s&7`QcP4=vny z=o=ghw;KLWz)i)s5pEjXr{EglJ`Z<42?t$zEzd?!Dg*XE5ja7e5Hc`tyu&Cm7K$h; z?Vn#8Ayr$+dllnyizw4FK`j%OfC}N_c0JZ=QO2`~oAAuHO}i^WS>=FHb&#I6LM7ki zgn#hYX(k-l4xP4c%biL1n*NB+ds@IX6 zgnz@ET4xL>t}8Tu<7b95FESHqL{-bZfu0WJKV1lXm5&>SY-b?wMQ@jBrrA0 zc;-Ja+VLC$mkb4d#hd152!Upvc_G@L2^gR+&{cU}z5M1j-n48A2~PMcPXd@Xk@k5$ znH0&~%A2+uNv;X=dFCbLx&eVEZ=kCMJ{z2WR_Hl#H#K>Ma#4L6{U1gp8-~ zNV$eNiHuXd8EcR6rj%r%Ehh7w+@}cr4xtl<>iZe$o5(zZW=!VW6&FS@*AV!)H_+UG z{&`#o9PbUhhroDmpzA1aIx?>31IvIy^C*uoFf2@26^pQ+h3b65o4OK&c8ump--V4v zQmr?s=VO#NUI>ij!(tycvU;dGAMvKpM}dQ z0XJ`29fRh1L=(hcXD*Bgd#=h4d44Gr=>Tu4)eE&5F4QLM1*B9C)#hI)uTE$sUtyqz zy@^2SPr7v)9UV>m&i>ir9J*iodtF(?ctmkZ?;CPu=qgJi6QokZZZp}<`z z?`Qz#6Mh~fAj7`HfZA2%8T1aAD&BN;6h@m^^TQx|=32Br>s8?akTsKGKh5 z#xPH7E)0?p6?PAWGgO`T277iQeK0cy1v^Xf9v-Iz_=u`+hQRN@cnoY;U_5pX#*Ow? zOWmoiY9C~UneEwz^!`G{P6}m@i$&R6yk+-nMgRHHQeA$tghj4`;hXzq0TSkySEDiqjIp*xfTT?y<6c8gzZ6sG7>!Sey1MX8p1hH z+ZwdL)^fEIZ1m@7t2}sbMfjS2%dJkEyfVpp4tR~$USB8QtosUrxzV#gnk6tIv8URw z=KI3^6+DJt;Ms*|l~ceY)eBd|xv{q|0T1q2%5yx3p%2`A;Y2+gTZf@w<(5mPaQn|=O> zxOjwyLPSkVXXc5aDASst?&~8p$7&H5=X#lfM9(3}fNBL34<%_*&F}4*wC1Q6s4;T- zP+SQFrx;d(2fS! zM!DZ0MqI@7--(#2#Zu&UwjCvW|3MX=>J0X8Ie{!m*?z#K=1r-ZoPw)pkk32ncys6- z&BxztqQJ8YHrx=%D?9tu3x8BUC{f5AB4_2rKhc*ST7vVFc~ERw*GX5Ffq=l*<9G^I8Qr#gEuvm zyX7Rv`~y(lYVFIcj0XriGEtv~>fSl2oIUm1uIZ>6|p5 zoHQ8{&l4TC(iz!Q_6>eEi~fZdx>&QN{e@!!W*tGYXmHigfy1+H@&bjy^Wir&Gdq5Yh^_~xm$+7 zd?bMJK_V84fkfn`c^IWBYMR&|)WFkaCGAPm&S-;T496IE)kYw?MclBz=KuW#!o%gKOFx7h6nooGthvvUPBj_4!gR%(1 zd(!`a=i?X!-bF!O! zIplV)PR$8E_AG}!@^G|vX0jvGHuGmkW?3ApD6a?cF>?cR-jQh!o9@WmNI?2J3L*3< zR2HNH(q4@n!sWC<{=4}rapDn;wnL0zZ`t30k&i}eP=wWD=__t3*B;_CKvAMV>L*hP zGsyLoc(36r#Kw#yuq<_GJBR$e!%K;ra^`#gR~=j=JY-heQYP7vOQZmYin0;w<6rT# zkUq3f37u`+ZVJ>QW8_>M4V_tPQz$tPk!cP_dTm#)a2RH!%*dCd>Luk(V7YKY*W3!r zaMF59(`LW3X|~O|x%$2%n%Y$8yeU)9eMALz^Vy!EfySg`VXgD{koYtaDw7sYSDSWk=I7*}}tWqJRPHTAeW&T(tHg*vnd#70sMT z#tHge9hjoqBC6lr3j!ZV(yE}XwZwcos+qBbGMj@`SLK@*cK~H?(kZcWqilfI88cM| zX%zFKAghx?cE!#WCdFv=-f~lI=;nFI*XPgqW)9_>B_a9xJjmAvnMNm&98+SEUFi(U z{FfkAzi9JxlHG%hrZvgp8f0Dw_86iYsG`|+*eaAAsbo)5p7}z(3j;~L<$;@%k-y~6 z`DZ=MpYx|tgn6WP4uAT8yp#L*?)A zo<(wmg5)YmLHZP%M|QyeOM`5v{ac!QVxA^&hEw;K5j~*hr-M{o^yXn5sh_L=R9uyR zPs~%^c=oG$F+_O;YQ@l@vcVR z)y|tcdF>8fJ3R=Tf3COrKOm9$t+HN&DjXGiEra7#O*fkxv2B@}dWi-OVNP5*Ws|cZ zvjlo2=19WjQ;=$Ga@yTa${GQ|u=mxhQ_bGE$okiEwbfj!pXGV7BoNU>wptv=#MEMJ zL#)m+mw0QMPcb3bX+Xd0vI>fAVbPclR=1S1PG`M1<|rD{^#<2?fu zkEM$}6N3`&om5>Fr8S*`C<)dQy|z+8c%S0;$-8SXXJ% zU4}p^g++^!HOG3Y*l~c1*6);WRM^+ohi+7=Vxh}czKK1+veEXUiU4~usFE>5Ds-p= zpt;n0=`NN5K3a}{$qxw1fR}dgB(|1>7fxAW;D)f71|vVq(oRR>?c+h}9CeH6)78w5%3EOCyfAF6;?rfe(CLbKA=y2Au5|xD>xg(9_H(2o_5N-c zsdtKK`Vd_cerQ2+7x5+o2IZ-yE3<4yYfEHBK-nluCvTJ6GYJf0R@QS4^7>OiV*n14 z_RdL0jFKmdawQ29PvvNXtHl9%!j>zg7Kjsic_4d~t+q_uCQlBu9kFE=mq?>3NX=b$ zd2|FLFRQir*T}h6eU1N4aW!bm12$2mK53M`x~95JY;Dn(joL_?I&mfe3#*i6Riqq4 z*Fi#A>uAlGQy82EdWz~b3Z17(_y(6mXJOpK7LZ))g&iP>j#+B^Q3cJOekYOkK|R4l zvU$WQsD+e|2L+4qb?awu^o6cHy`h-(+Zy1<#_DbKrf9Xb<$iHY2`Wn3hq@u?IBGf` zfM(;GeswhuQ!Nv_V??&mR;W}RZH0NWmS3&&i8c`YY)aT5fk*MCL-=Y@T5j;*afP~L ze3dC55SP6&u$o`Kwz5lA^|V%!pqdY9+of1fZMD|?rEQrBcVOji8P;p$YF$S9&eNM9 zyA5rKWWR^brGrB2o~jU4yDdL`U7uCFf#gz_WqYl-7zUjDtBWk2uZ!0Uo<)c)|EX{W zXm^3O0B8+&`+}W@6qoa*-I=5Z9ll~{aC8WIH|h^oyWwR?-H!HyVS8&1$sdX&3=zIw zh0KP0qVY#)9*m1zD15FH8MGqY9U23a76VCV{JbAGC06k;sCspXiNfa>%C`mQd{Ar5 zQRbxkb8YGzt0blInq`*OT9Gq?byQ2<7g&G;G-IYC`5K85ziT^bZ7i!D<~P<(BpAwUUg zHfrb`*Ju!8IwKp^0%wa5AMhYb$hFFm(2__J)$Ve}-?Hu*j?1HZqO(Zt)uWZVIXLwY z){0fz15(ki7A?6$sKQlaA!RKjJ>#kww)rj(4*G2mmJsR+*2pEw)X59+p#6*Ti2B%t z8L&Z8c&wX);eb9Kc7cQ&n-6og1BP~1FX4@+C=ON+R+BaG=I#c>W=ELdRwKIh9 zq(G(x<0vW6E!^D}*cMx3p0V3>AqK!`*5IlYri4hD&;zm=yYOQ9cpIvQPszSrmP6im z$b5olm}2y(=8H3Sc~g+M`-kGn5Vz(*x@KQ23;#PN_aGE8-}k;_&*hH$S#}6t65}(Z z7;5b%Yrx#mnT;^mmowL^O*=`1Q-)9eNQO=NI8%(Pl4vozn;Lq@{SK0O$88#w6u^9* z6bOFmX<|a*Ujw_heo5}6^u*Wb=0~Um$=8l=XyYhaHQvV zI!_hDO4C}YJEEvgVw^`C1qK#C_k1vnihzp?d^>&ZBIBNe5fR3EH3H+x)6S>}#xw zAh#koLxgRSaYM`d1pnfLY9<`Z-})WUE3>f#jOeC%S;BQbWkUQ$V16+Nx?SDKW@J@sFoLn-(#y5q~Rj#~AouOKD6hPZAfGi!9&6goYoK z@WG-*e6qGwN+mr0Gcyk{<0mx6mz7MCH)H$(`iJ1hw?h%ka58A@zw5>g+cQqbgzQXspbGt5f3Ol}*qTOD?vu2oiv%Sde1M{maZtbOPvM@bqpR{0fZugA-CEdB}Yy8mYHqOob}uTsYyU?Qy% z`pVq;kjl|$5xLy*E~yIqtaAB!RyhTiTr zhJCe>c1LO|?^U{cpBaUQt)ofPmJnFRNR2g!T5rWwH1sDw)Qjlo6n)6I*MUYe{=hb8 zOQ1V?wk@ptyNZKreyH}1Pw`9kg)K2O5A4JkgiTc_ydzd$;aiZb00Xw?HaSU9l(j`| zeUa>zh~RqRHtd|)Mp;{~l#Q|-bc8}#1euuD7}N*af5sPA_}3q_{M24OV`w*@VLQmA zSL70NF1bPu)I72eRKadaA#ExkdZu0KODhMlxWh(7XNCFtZL;`hpK9+p=2zsTF8;Tg z(@GYu2OXt&Z^_RVz%WL$#%xy~WE>8$MU?ZGzl zxjJkZ5ltp(D)`KUnG&DHx|x!EA5m(C+Bl6QE2J~0PJ7qic$zi_(e|}Nv9-V8UP_df zR)HA#EQQ#PKqYXC^<}`9QY*+3G@U>#q|^4cBlJ(-yYWq0L89ykn*9-Ey{D6b9Sy&{MU^TC!7)wqSet{>l2V?;g556OP(@08gjt~IP5}uQa?^2cXDZHao@2LV@1rb5&XA({ z1OdgWh5xP}cuQRSDovrBn@bj-8GG|pv-0N(BX4<)rl8pmNQZmhN(5OpZN+4ML&L}s zJ;*wZid~m43{iV;oxEMPfneyCbtKM4MN!jh%c>iEYBVGZC%;pXCT)jwl~p!SqhY$% zrNmcl<=4%Rv0dJu%SHPAqG*zfW9#dDW818GWC6AOjeyyyuvs_+)YjJ2XrP|Cnn2T5 z@wT)gy{Sf%xN?-$*|^HEQci}uHuRo-eYK{6)|TGo@@V8#)G`P|Wrw5ag2L!k-|nAg zjr8m8vuR>K#$|hxkGQFgjCRX&-U4$zB%kUUO_A)rZly9`Hjv+Z8}zAOohmO#tsLDl+TK0-Xs6Q0o8Mtl6sOqxv5Ut=u+VKg`OIK8SdkeN6Ir8CLp`YT z7WXVULhL;_UYJ4A{H26}4n6_Ax}o#1l?fZhFgmNWsfkR=?4@n2HdJj@t3dY*z#0~A z3Q%9i9lKGh+bOZ{?hD#E!v3^FB6obLxT%pkpDs3jJ}+x#)~u||EMwMFSu?Vx<0#TD z3ESYEPVX?FhFpc2gXa8jd(0>mmv$r z%NeCX(7RLdw}h1!uRZ(KAmiK1`WSU_OhBrme3M%835vtWr`4A25I=K^{sE$r7$%ILJYonaekW)#@9+ZC0eGuKr8>m)M* zrRNkBVdeX}y!G=9Rb`fK+2`}TwVKP!!eU#IP0EXreiVdzGR|Fpxjhzz4O`F6AzMbZ z#%>mrf#N`5Zvnw!KIw1#P$uhq$}pb{{K{&>S=u0eqcWmkjGw{zwqgFzxM&ukAJ$_XId76ySie7)0XYz-MYA(`2<1{vm%WhW`va6GI!Etv?M zn%i&X1`&@d$-zm7Y!+3QgbA(?&z`5ACr+B1dkA=6;=2Je#lY$eQwg?%pOC*p_%g)k zaZUvSUi^%I;h}`ZCo3|5WzHU_PR>9?nT(8I7d_PLA#q2m@0-KWu_)&e6prS z#66fX=DYVWV>W(Wtx5VFQMAj8Q@}9Cs2FOCTUtmeNm&s!*}e zCK*6~5ipF!?n)1afje%XxTz$zEo?)b$d!zRtFqrF&7Cs0A_l^~Kq2=BF+sK?5Wk(O zo=&z}a&YUo!jn5&0Z%Yb#?ltH)+i9EGDQ$`f;`?Iw3`oy%HjY2Qx0vy|9?3=Py9d2 z0s9OGw-<)12d@B@c*| z6qP@iHhDB2?0ZYoaQi9)Z4~=WejXLStOPiRDT7K~R#K_L{zs-EkT>*`M|)c)rSkWL zO*H!9i812#UShj$o>y&9vO04CjU>jb!M%Y_P5Ck358Hz|12c z*PSwmY*RG$R_OeN`i1Bznl-JEIm}#b?_0C%s!NRh=z-+!cHfZ+IR~)t=hRldv)kKg^Y_HC)MesABGJX^FuFt!!CZW zei-KZ@j<%3+bD{rGlauqq+X9|L)MhWF}c^`9Yl{#Q?vZkTEA2@%}6HT;{yNqTBjjqvuf8JL!fHq4>W|$QF~!+ zK>0Ec+NR)kAk&>WB{f~TE=Ww;BsT!apJY{VZeSr))M~$i5R+>DU_sDuLT{||vFzT0 zb<318fcAonh3hQDqm)dbK0vJ=@6}Ej;icMRoO;RwRLe%{={j2bA70g}0)Y(L`DSdW zoh}+huy0G}mw@G*eQWdV6M4R^Nxs#xm%!i>?8qZ@2drrc$M3EZN0#P<#h9OQ)LE6a^#%M-+%2o0d@(d*6~lJt9&XM=CuV2lGcN z7d~4I-rM{Lge}=Re4DuYjpRUNCBt&|lA?Og-mQJx_OvDEH|v)MGP0$63;o}a<$s}H z>R;%tmgUXpS+XAbfw!e0g~v8n?XFiWd_i52wp8(w&s)Bxb+_RU^^LPns0k%_kJ__k zFf(A1qB^KSajmuqH2Hu2fa>N33>i)gAls8nNmZ-n*XBoxuobv6_))#FIiWHQQy zey$%zQK7(>txy;d=*62~qBWm>nVgnO+jW_;?zIBy1KfE=Kf#^%Zb3?O&n=)n6z8P1 zmj@wDk9`8VE2a$=X}g%ZSBq}}f&Q^b6x&(k-23XPVtAD0VSCv_Q0aR>r8Qf>WPFP{ zMK^tZQvQSaYw_K3D?6&VUcEI0MkzpQp7}-(8-B5(#0pV9x!tA@u^U;roNZEAHM01m z;@WX3lTs$)j5+~lRD*Cr9YU%-U*q~l6*D@_W)+ZV#t!}u4GL`WCEID$x-V^tIvLLT z6&h>E8tXhVj`o$ooEurX%zc9u*(b}coiEc^wO!miDG_5~yq51O3*Eb3Y?s#!JMxjR z%vpNM0q=ssmYq|zbpD+6bq!LWy8xp7c(6h0MJJB}n}v_3e$eXkQ;jBj-j@X-Op(tO z`x4^&B|;mdLg8Hh5*0S!Ut0As!Y}$j+6uUx2>6lZ3zC^lQzW8RuX;mM z1nbd0g$0SBSJHH2FbBQ3b7fxxaA2e~f81B2F-At8U923cyypn=f=!?Yc(yAR-$rft zeq8htG<&+p_yB3pz7C`zt+>5flX(k7Zjvmrasu$@`+vrt&-J0Q5Z(z}E97lHG04OU zdAmLv9NOapGJ}kthtz0@1&D`e^ubZWSTggM>hE@#`45B0SaLN8V~Gq#mlqEZ(1s8g zLGiei*urp3Y6bbni#&RcfHGqfg_Rj|Depn4BD7)@Z1q}Ou0FirBe1p*{Cn`;3qmbbzddVd#ilm z5;AL+65IygS6cq(w+G(@9~8nXUELrVBS|P6`2c{`n?Jps+su`iBEwte>c7Apv{MvQuJYD z-yx9TA1A}is@1)vD9C-KI|s@^R7EmSY7WidERT{f zdsk^N#eFVuJuaXywG!3qbhnQ+V9Ds*oE$Q$@dLG<(r`RZ$i$PvwU1T6UvWo@Hb(?k zWIUMTLW4TCJva~SB*ZpV^FYRLI5<6Fq4IXNII5OT1n(9$8tL|}Hrcl9GP0^#Ldfa| zLbx5be8J?s1uS4q-QxVPHTlm7lX=mXWH7$;G=2|)Tj1=D0ueU7! zxL?XKx1mS0&e#3mGO-!-1L!7{V2*d56CbEA=JZ{h5LoBir0+UUKw5ruAitf8bIZB0 zp|v&j+2&P)+BXL`Dr&}aQO^{FW-#-ZSVtxWV-A@jhZ7WH+}e_}hZ~kMV<|3E%(Y0H zoMP~MI5J65%LJ%DkalH8*_LmxIp&a?PRAKh*&NRIr-IPo4&N*GvN|&6Jf+A8#!+OR z7et1$5ZEcSwh*;J$$7o{N8_p)re=i|r}_}9aHg++-D2&A;wDB9TLE+5lscR+X05gg zhx!S%^<=Hn76G6mYR>-TI*~*7QpG1iX)gLww&j&zL93Hv;jz?^8aqVP_Vr154sl^Y zasY}TH9y-|v$laWr{~qajf$M8=(A?uIyvfPUICyvsIFATNXf!7PXum9M6~Al;J-N@ zYYV6-w&iv5RkyLgHSNMBu`R8&rBmMpGqy!qe|R>Cl04Y9{|{+z0@u`){|(>V+$@Bv zhQ)}24!MvJP+N$$f=f#x2HaXnH7;F7QiJH!sit~ zC^7~%XNt?u5~7n{6YX)A2&sN8SN;RerCmO`a58EBhPI#$Aj8>K^{|ne6+M|V)5kXI zXAMGLV#kgKR{txWacBVWb5P9P%MD#8p(`C%OgHB=<~C-Rt}e-5esU0PrL|QG z?b!ZoX4%+CHg5l7DVS1(Csn@}RR6}xD1P?wmSCVu=XIb7on27lIFljsPZ z+%XvZfy%wRnjPPsB;!Ds1>x47igbGmbNb~OGmf83A5C^;Ktv(dgqSkRh`0b7 zm;nz&5uBo$GP@~rfpm5>Q%uj^98cI6Ro~>^4YRL3E_|RV5RP#iQ2#t>bc-fP;9}LI zWtQUnAH9*Bw#xen>rY7zReNVePEfSmYrPio34|~2^FR8Sz$nT&q(M>{m*Dh8oRV*; zSnEKUjs)T2bMVg)LV+{JEcjx^W4*jO&gI@o8ZIzT5np}szvCr{lQX&R{U1-d4=bxT z{HHIMAcEs9k2D@tf^*Uionaq+?@mI$Y2|_)$shxJB<&LR4R%$7*n1IFxCn0dO=AQU z`;^>d5emjH*!GBOE5Ye(|A;gZzJ~T8U!$;(uhEVBClu-qLH=`c1v>=9W20#P-0I0k z&7uW{Stl3O%_TkcKHURfwL^+>%wLGMw34Fz-E1T;S!9O9PwReX#E27Yq}ECMoA2?- zar2&SkgIeQV5ND_I)6Mxo}2LeT>Z8;BFp3Xcxs-L?KkaJfAY{hpKomFXG~5y1y;bE z*i(}7$C|(jXmQUiDVbHWXI9C_Clc0sjw)^ABd06!GfrMO#A)K%>GJ5dXi!mlk#{z_ z_0y1TA`a<3u!^r247+lGykjJPS439`9)vH!unPiesm-=2%71pm~M!7dP%O z%m-K^+)TAzw|igqJhBCyvI4#W*Cieoks}N7_P~F~uOTkn_2hkxtl&yueywswt*{hj zK6*~|-3ozJ>%G(s^6{z$=Bfi$+74o|XFAdonGU^Z&OY_3P|>7Bt$?CVVx#9Ar3Pk0 z|2}hH>N6WQNHdhmv96T`wv}_}2*~CY!kL~<9^v%LMgei8c!kbEV-gqWA z=GigHk=nhpw8utTzLh~}!|2|V8`IzDZp@8|F}mHk8yA%-iM-hU9;p&H9v(H9dSD*W zur~lY4$l;qJ0Y*1Q;bH85ce_#z4?N*9+Ek@u189U7=+8C3HD4^RIOwCU+mwYU|0EQ z+s@D_j5GS0As7ns;68Xcl5>kTP}JXnTfm+Gp-ntbX!0KPW8TAlJSOy`qCzv2^h3QN zV08l#X9%_!?Y3_8Nv%hf4N_anJ(ModJRX9#_*!|*kZy6iG@;*ftSdH>j}7xE(e8Rh zMO4Lxx5#vCA%_Oys{;QWUx_%G!7Yj~PqSX(_(LEpn$X4@kp6+T8~rEI?yrU-K3ngZC`{2u+2kp5LufLF_nq<>+hJu*+-x5+%st<1X9ilM8I#V8cD)?i~aAG?*G6QQk!4;Ju`DaF z^fhJ43Q&^}Um5Ye(BJpF$&ivoi|YRx()|6Ag+pYp-Zdi<)EC0E-8Pvv9@iNkGHtOI z#Ndf0!oFY+WI>B@)~&kOWfH_Wf9$T!!Q%GL!C%77@f=MKURQuMs%UL|$jh;H+$tRnqMSR$tdm@?KB6&vB39wwU!-&3IAJt!A1$yj0gy?_~`k{GpFV>T&ad<*7_ehQ_;3`U8ts+N8%(eI8U z7tZGj5J53yH-8}#YcAeG!PYcULfI*(ofpCXts|aXB+h8NS@DouTr95`ykWy;VTY3; zL43`L|BgEl7j`&#Un8&h(+QY_s;IaC*TX&nk_&=UK{j&gkL?QK#)k=&pUDoe{iPes zZF1)mqbe_is$MlpMG0bnNFx}%iR8%i<3Fj0!oxIXGC8i%W}MHU4Q4kkPv5gVc!LNB zrVN3%8&Po9qr6)Le=NOns>r7~(j|zukZ<5lT?_d(t2S>+Bup=|afy{1M@FDLp59dh zPwO%ly)$wPj@&|B0v=r--G^^8mfx)JU$N&RYuvC-$<7Us1KHub$a2`?Y*yHj_89ER z^dZ?#j>F#x2-yfm1QWuBe+c?#sM{yj>NK}ACEB?Ds8gFc0((`PHwMpuHer>i?ndZ3 zkN*NtL z*k;8u`fPZ<(c}KioD7>+FJKQ^!ouD^LCO(C+`7+B2ajM8SX`nen1}2Lbya5(8zYc^ zjM$(#O!f!l9?pnZ9Y3jdguKc989!;~h&X=IF3dw=OibN(0-yZ#zo8))73|NYfXV9G zLXH*Tgo^Vp*|_Qf&n+q0p@fM97?Pi~2w&nS?N4nenZLoQEV-NACRtQcGT+nu8;AhK z_W*(I5^}A3<64lH_vfEljQrG)Y-)XVNqIVK^;xnW$h@*^N{~Er2I_4Jx z4j`OjTsDw90&B&X2%iw7hxMT@cTHFZsP%6eAL$^WGkX$kFjA_3*~9Y^b<1&fh|ZkN zH74%d$Q|n`oG531XPX{rf*p_Gv`9Gc(I=;>sV@_g5{0z@i!-Te?M2ua;1;B+KR(Vu zgNzob+DI=osZpA>z3VTS5@p-iCCFcm0F?}NIl^0l^okvQj*LnDq=s~+HwX= zeR=lj4R!Ebn0~Nv&wCrk44z)`m*Z4iHucx)`nmq$1N7X8LqEHe8hVt ze@#SGDx;`Xw@Oy0Ly)mAgAy55UFDF4SMrnJL#{qf3hIA@R{x%~+FUQj+hwp>LfVV+S&#tX+i)pEC|M4u#q z?%0tjw*M$x+@$y|F6$)MS&=?zM*Jv0XXLqw>kjJ|DO-LgT*R7=kXqWRj~)^~N^c&5 z;=(1Oy!6oe1WPzEI@E`&v@m%U0E>#K6&LVOYd;bM> zzo8wv;udRG5#m@pdaDBWD5sOhb35Lt*To-Bb?Uy4=dy|q>QcWSsVIyG=z*NU{}6UM zSr^JRQHnZX-h@9gOotOuXti zemn-WeV5L0>lo5|G59=32T=F&bB@#x1q>cg200WR*GV3Nw_iIJ#@n3-oJA$Ijbp}U z`eaUXymPIY*UX4_^0pbsXlqaubCD}na(2yAk*N5oVjeMmk+s-rN$FZ?U&%w~rf)N% z|BjE2Ipbd;ugKG8AILtZuA7**ggKlgG9h=Xh(fBEEjH3Mjqax zKiJ%I*g3YbS@{up<^2Knn#f+ZeH7w3qpApiInaP>ePf)l=NMk%?#g_|m^99NHKfz$ zTH|3Hwfy=rfNpati579Q`8Ig1JyKyf>IxGHpta9$!(R}LD zkxqWm(|qc8~vshD-9Kbhu}VdABvZ^JZ@`m0I>xqRHUE>70urr6PT4MXWS^<&~0KdItm+TXT)@qSOmjpuaDB4Rume8>f111M3pze+Ku-TJ3-Jv`j>!^ z9fB{McM*;ZkI8o9jzhom7aa4rtB5!ogl&o@L9Vs-U9ONA%XNJ9202dQ#Hd>gg(tw2 zu;264ttR&#c6iH7c=|ZLnjHrgne7c4*113qA%=B=({4@GW^DSvM=ufQ!5;iL5aE5a zk*GitZ#^tjO7p~ZmtY)Z0-}NhA*4n6!wDFxPa0eI?~n;gmNIIF9$9WMk->xCP}o1b z{&)X7S2M-tB(enb46!uRa@Od*knzMZ|F%6aSe3qSBPkm<^3aYxl<0Kp>2D>)<4ES7(_210s_f_A&YbTEh$_YlUtLV8YoL0 zcg#t1+nkWNB^vk17tT2($DDapMc}g+&W(b7;_`aeIf(u4hMJdWSDFr=lKF8wXu@XU z<=^ku_4mRc{Dcp%F?n4k8jeM<0XllKX3YW&3&!KXB}}?)b>hs8Gfau)qlLjgHSCo2+X=ivb%^S!$ z%!|dmfq;#?m^mZDGr_jrVBnt1BO(-36O8WI#S-@W6GM@P8NXl7*S!|J)T`i#{Gw*1<6A@ z60t5YAJCU4;_V~AG;|Cmb*ZK>Y%vd6^P*l+d>qY!yg4t1a6aiY8BHVUH5pAKsU4%q z8>M*AizRv^A;hfjJ0{7TYc7V= zfN`72geG*+RF*n5N>7VmrdThi=4$9$6BrLo*#=23yasiqs{q!g0hGNGsvGu(jS9o+ zR34MZTv?!m#b&=-4%7J_=zI16>dZ?sd#LP5O3UaTlo4zXscO4*Vxw^@D$Aq4goEqY za0FCj!5^D<*UvgJphjw)UN5e+OdJqj=@N4D^w#~l?}cH)%4_`yhX|<);*@v0^1F{h zoGrGSZl{YTp`?tSqZh@e#Hmq}MINni8@}6P)u>b6q)S0yL9RKI$b00ROtNT5mQv^^ z7sf0taqhX~j9OI#TC{qzILh1f5vh<|uTY+4=bn(J2@yKN#gd z5r^t7NdXj-Xx~>DB)@ivm;tRNHBs@7dijnXxzite6k{Wn26G+$w1 zkv*Dm{D|5Zf-e!e&o>@XWJc*+()desct1H7Ulw#ts4uY(9%~cMCL92UrAGtN6kJZH ze~S|_ypX(SyZGcq&KiEfBFh}g@meRpU@`Be_!kvr(fz_aP1`U9XXKSC5EC@-`>llGjCa5xfc*oIy$p! zRadznvqQ$D2kzeir$EY3?0%=j;a`to2g7ipCtSKP&*a>J$)gBZXUO|s({J=Ze?9M= zhq+1K?~#^zE~^=d55K=m(t0r=lIIfQxPiGmvKV?ZA{}Eb7`j`2Tc2Mz(7;BfzrPxu zTrK065p3@GCzhS*Nwn^{R2WqwTfEL1n6bFlSQI<87T-HImy-np5~(MJS`0_CjN{Lw zFByTJOmSM0*;x6%)^l4w%<(}_rJQ$HrtMg_E|J>p)#&E>XR66qXgC{MkKJ2`CW}5XU&l~mXT&%hz6~KY;kry z4xl|<98nFgddHZQRW*79yZ(J#x;F@wr2HN$4q(!$2~0s{k13hVKeH1G?|o|y)*dyx zy0|2$tnVD!k^agactHjQe@gC@yRSKUd0;A0!ayxBD9R35OCSu*jxMKkO5W5QAHxgQ zogLX^SYW^n7l-3Csg+kNuMc_6X_N=r>F)670emiSvuxfjo%A(3CkqVi5G>3(rN(n2 zBcsgSOPjt9u`;XM4JtGwsB+!eIUS21OCRkf{YU2=deZeg@zh)KIs21U4Fj5he1WSL zZbckvMVGqw3TT(HLyY3}tvf7ERcGypfz)90f{xRkNU3xy$x|vuDz~GT__f~fG;;0{X8lK#YU`M!u0D3fl;ko&L zgqj>#eoJ7tbYT9No+1e^HL=o#=V*8dPPhxRo~2W$cIhI+NCIR?)ztj`doCT0I`rU- z`^5RLUHvGJ+g*D&9x@`!4;^t$C(C*S3)7{tEz7>D_`cw02NE31NjEB&ovXN7aMy8t zF?x>qa^`!4>@cDERY?DIfPeZV@^%w2NApG(!6932I1QWY*^yE~jtpor#SVk9XJXZ) zUr&>ca$OL)B9xo=)6(-Ht6OCGN5t){VXc7>eCeV5ptDOY?kS={M(3o;c$Z5c4JYz@ zE)_+QM!)NnoMLLBL=)aib?eYZr^M{`E_P1In+G=_<{WU1PE5%IcSWYp3!q)3SjdyA zboqFPc&OZ5yLci%cTtCagTC#-weD+!4t^m^^IF1P6r0+@q6o99+GTx`an~U}>XC z%oIVrO|#ejur4N3E=+_!DJJc3Jm;GFOWrn;mz_qMPujCWpnQ=t>2P->M^3Z1kc{8M zv+MnCr<_>*It)6Um(c3Ifi0w^J72-EDVyry3kZ4Lv4D)(P97o>Zs5s~ZHOkR%g7SO z;;PwXdoY|=N0(p2;c9#ouUI+$lgcQM5cA3FH#J3yh7$uFj_&JZ_&4!TZ8fIhBr=Xb zDxVy^tJ&|qIJL$lr}k`V+^}vnFBhj24K68(EsP_D1w>dCRAHtJbkOi83}ZHq3;E@wV(RXPji7>bth)~K2vkJJ{_erVM$sj?K^u2O{y5PEP)N|``orC^bC zAAxDu;Cy^sWSBn-F3nLw`1{MdrZ~2K$m`y&YIep|Is3lHaZj266l9+81v0x}e8Em4AR6T;Sy%R^ zg#C*t4koBYA8&Y5S-xnH?OFEk(pZWQl#r-9bC5twezgeh}v9FqqkC35G zjwPxKW@8>Ath~+5vyawq^F^AIqnh`b3zY1@Gm#>^3Sf&+E(LvMi)4nAP-!=8SkO_F z==$i#4W+B&YxgW}{8IaM`82be#%r@$shWgN;jw-VjziuWgCe}Ca7-DMKlwx6_#C2E z%@V6NcGnkkCYe7MnI$u|vyv{#{Kw*&XR`A?X=$TJQa6$ezh9UC`*mIP@X4aNWZ`12 z$U+CMn`#&-UMd`b9{2oZD-TW1&y(?@Y#1EPDhP|}fVKb{e2t>~^_F!g*91cXmh()F%%Aqc_-n8*z2oeKAwDq#svnZSg$?HC z1~$2$aBNzO8TW8Qs%nKV*z@FzZ_7VsFZemKO&#SVL%ZY@?OfI^_0UWCiO(b1S>TyC z9)Eq}wIXLebuOvifEXN_<%>UrAh@K@Bt0w%Bu|HG>4NEo&TqI_3gENgMkAc3*Hnox z4mbyX`hZn{Xu!q;K=J22@aJ`V@_JagXYxv%LHt}g?C=8h;Dbb*VRvyRtO0E!54J(B z;JGz?Mi{>Rz|6!IY8XK;)1DLok>dR8AHYz*_weuw>zY-(OjMR2Kw`rWcmi_Av!sib z^F4N{t7?$Py%7!*nv}KgI%Wy!^SV7TVwQvXaf|0caSji8FZs;EIdtheAZv>3DO&zy z&wd@9=t|^I-Uog&sKmZ;LrNk!Fg-sP-)b3Cd{*^kr1cB2U0MLwN>&J*?Y_w;GuDZ# z&=u$xLP)UkmbECgWhE=zZ{S|L+{z7wiAW2>v)+t5?!}3!Yu%vecFB8OpV8rCl$Uq6 zMGz}~VUUCCa-LN+GnUN|)3bjmw#yb+u88f)zqgc$t1R$#MBhAC80_>|I-Fn z>cAQavb$iwoAIYjg7k`O@g$Taait0eSOk^-uLc*)Inlc!9H$ykPA^+R=lHk*g!FnR z4XMIjo&@CxkQ^Q`B~CPe_PMT>fc`+O*SqAVEoxpj>1svm0l2;f9zm`N@8wQaN~Dfy zysUxTvr^HGR zwuRL1gpA-{$I_*GiUUka&(!K^6GxfSDe9qNP|a_Ls}{?w0fy6xEgYQ*Pf}r=)+f!B z=z+6~LDC}6brm6f(gZ|7pms0NAs@$$1x63p8uEO$>X0!XH$p3lK@?r<_u(5 zdDx+4)I;yjq4kot$~F@3G)7I#*wIEYX@^NonRPK^AA|lNj?Fgad03+G9)c2-^ujFA z)}rory|WawweLc8xlZrLorDkW)DU=NO}W5}0y1#=J@4u7j&j&CzVOeMed}2`s@$qepZY-8I#!|8QMdj4mjOcotY5p_?7%@mE zvS$BAeR`~~{s`aU3=7~)t3x34xYBz9Qjftd2HA`?+GVDKh9mGgH^;P+46J5*XK=CN zj|x_?)Uz>Yi&Wn~K#mDAuH|D}2H6%w;%HmJD@H+ME8oe&>kMZ}QvWgU4-Z~q$k?_X0=Ym_*~zjsjQ+d`CejJ^B68rl*jJpe= z7SFgp3T#J!&(@gi=b?}@vv5$1Lvs(rF1bloBzM?)>|yI|A2Z9`2b?lqM;syI8g$BP zOnr1omJP@1=JkLaikHC#7$B&-46rBoccyS`RBM}HZG()nW_>5ISZPZcZh`s9l;Ob@ ziq;G8{4K_%D|iQA+sTd%?iE*v@3BLi5uQ9UzSU#PU_(kTm@1rn*IcAbm0wVlSqT#b zqy>-sEst;>xC$bW5-cNP&U`A>BsV!8KSK6NvE#8{?UPD}9vh)bS1j|SK&A$VX~++DgORC}Ws`@TX38gid;u zZhS;pZiisqCxV4t&=SJ4pjp+4Z7a}oBai5lreQ-8^5xWb9P3LlQswJ1#g=1&38=8w z^za_cpLBqbak~#^N<7I}76Y-0GCuryj>HM4->MphDMo}2Wmk5LsqMiI7Uk;P$Ja>joS^GS?e zm!*0}0=G1UbDrk8C<{10G=mjJL2hMTH9LMpq${Z&Y-;|+`Zk&ggB;$MItg2lj}u)f z8bY$*4el?nh`$d77kBbW2NQLP+=~Z$wUGY-9OOSOpmX8x*JytW!ajs*guf!Zj!=Y9 zhdlWR0})0nd+_8v@UbrFeKvcG41OIwNwa zqdlovD#db$kNN{jLg(#A=G}754wzXW#Yn*-hF*V^N6b_n-9C5`Oh>f5h*p?B6X8UV znPe}jGi7(aO|UItEZ1JaJ^JL&a7T}O^!-rXtEC!?&{o@Wv1*8&<3xT=r2g|^99w$Y zrMk5=jFx?(to1l<{sr~IWCpQ0otQWu6KzG|h0z7I`x? zEK;>pnl*ou+2Cu;_BBwMVb9CTudr)@1+tQ|gA%h$F~h=u^}@TU$*qRCpDP(-gsJQ^ zIt0hupH{yjARkBT#D)f@eKi&zY-lQ0AN*Y0rC zlis}{v!tn<-+-$xZHp6~aj~G-Hn+|t7f0|x4>?qQ3=Cfcjhz+O?-oZmW!taPrB709 zo=XNuIFZjKhfQk%0ht4jQ^rV= z3+F=cr*M6;)~7ah2h5{9mfDUF~7x4CfBnlV9GoqFAP)a2{Sg(9m+on7qUYO4FM1BA7o@53ptT7(PFg$27;r zuezNw0PIA$0DNmwJ4+c6wQSAZVwqi*Q#ghWP$CeGE@akkBrt&3_X`nYg*_!pHD;`lSZVAw z9{&l)&i-U;%`M}X;+EHV#$YMV1U}Kk7*yBC_}?)eI^a+Wbt}fyoNbb&w@k6T^mwV5 zQn8QwPmXd#2=AsmW=~3N*=}S;S~iUq*l-ljj8vT)WtY)L=8@b%w5f%%2b4ZpaIK`d zbGm}8a_?p6svX%{z5LKAg)41|KUKBs6FxPzs>V45#2^q{v~!fga5ohY`kN-Tr%4@y zB{yOTuTx<(-cWSDa;S0u&~TvLg+nH{B_&#(U|ix=u8;14EpR(k!A@)4*3-b6_7L(a z)9UGgUtUZyc63*`$o`#O>KNCf#QHb3b=%}Es0TBy%G6jcqpBwHD!-a2uW6#CPk~2We<@K#_Th8#)Z=0vzB_9?H46pWJHpF+>osinkB@fT~nBd!Kr=;jJ zqsf;xiT~td{Ng_bdw%i(PpdrY%GnvNmd_zVw7hr~HVQv|J)WI@XIwLwo$_$Owlz?G zV~4oJOW_y{nJP4g`00cI$fpk7mFF0Kh&;*#1-Bx9(m+9{M@oppry>*XY5sGQAOvX4 zs}dYD(oCMa)6U*%9KbBf}*2jbn08JLif-z@1s)3&j*Y0z!^g9gNsNP|;txYR>u}aEI zX+DS0^{rB|HHVOa$8;VO;p2@7SA zs=n_)h8dqHS{X(9xx#M?+>fb@W58img9)W8GrW0$#Cw+d*K@qLr{Q-Di`P7sD#AvT#iS z)(J5pZKyt&mt)aM*umLrS6gb}&rMr6GY6jP7G4I;zy#=RO7H@$N(&j&bVi2w;e0u5 zO6~gE+IKRph5RtZ`^k^lhZ~{lc}wSSqH)_sma_@4IwRmj6czg|e0GNf_{ z_JUsoK82s2NXXmN6A__soc#gu1HX#jM|^)@+%fwgA?%-{0YmX|w8}{h|BPL4TPZuG z7%A9{O}`+>Gdi|~kOM~4;Hwa(hEcOELGVd4`US(2l?cZ!8=lPg-0;gm)4Citq};^9X-< zJNwCZQ0KT8A3XU8;ujzXxeD!Q6^kep;^$ipPaZ_5MgZ?DfB3;Aw9Yt3V&}Bxf~arK zgl?C~^3Q~3%ywUubbcDTy;_$4uh5KV+;=7Hcy=n5c1tLX8`zF?n7J&yRa+k9CEIvI zCi;#aIZn*GOUy#t%#AICgIOhgdz7qd6y63p>B3R8bOT-Z3>~PYr8nq|?s{Bi$qE(LkbxcgJYt!|O=ENvCk^9(QiU<3sz znYR2mGH)WT(>54Aej$l{`qY5dRiTCfVUA}_2Wt}ET61QGOOx&ry7MaZ4rpv`P&K_B zc@tSJN_d#3^ie9?sW!S>a^q+`8+W&a8-pY3Q6i(-n;$i{E(ifeMai~ml=85QsYV}D zB*SjP8?vDoV^bW7j~61so8IK2|F7h}G{CC92v^@QdFLteWS->kFxlpk>lJ{Xa5}ZQ zTWni8JX{w0!0&czmOPm1_5O#kRYa67#n`!miCUsPlTi4U4wqIf6bfN zz#^F(ew{t`z7^!GhMwxkDy9WvtqX8H^ODNsOO?|^O^^iU^BS%t?sc${l zBWxYp@{Xxs@mESgxeu=mLd=(9G58>>Fdh-UDeixGU-W-`4_HdUKkWaAU!4Ko;Rk9G)=p{LaK6#k>ijY5nD$I?w3yZd02CY3=>+gm1K~&*vWnX&hVj(Sx8nGO8 zK1{U<%Tt3ank?)Jlkypel@R}{tD~v$8o$b zUe3m`(xpYKF|RF!_T%^PSRSyR71^cT?3)WW)4{iYE=-Q;9crfaH>7z1$Td(D7RHf3 z*pJ`;?*eX;0!-{oj2LA-Ud0|*I7SdJ!|;U*!S}?^a^Xzn!$Kp4IT41qHKM1@-JA25 zvDM^s7_l!FNH9C)#|@*%T7<|bmR9)!VJX_2jAmST)p=Q|iP%svW8DtS`Q}{r?J{}( zm)$c~xZ6^2;lSt#p?Y<8bhmbN3xZ#^CpwHtOFW`6#$QTk9j3J~7!Gz-Ss!%|{fzGeLGsy+S^P=q{3GaL>F?pjVA1xAD)NVDUQy>VSMXGlj$ z_}MIgo<{((P_Ox#JJY|5{-8}XrnR-(_IO8tr>I~;gtwgud`nrIia_Ejc-&tp^{AED z^Y~WS@Is7icNV6L!go=nw63Y!l{x{h!nr`J?TE;ff>J#}Dp1owVxG+GUr3jpq8#ap9}mnG3%v=R|GtnXQ|;HQEj~)tQTU*rz#m1L|+bmT3KJH{c>S z1^ORK@+NiQy7MuFb1OhG%v2+J7GR!VrS_#c#Tb}y*4{^wlT}#z&N|J1B8tQh;hhT+ znt!HNT`PhDOZK+Jy2`s&?F0Y^f(A;a7)fF72V~zRw(le$4q^!$^0O~t0q}h{ts_)| zDn_ta1XvFKCb{E2Nx(D6dJorv_c4J1;ddvKHDKx1s5)R>6-!WO1ll;FilBeEFmX$!wS9 zcJTw_N%(jHktJ3;CJJx*Xf6`XDru>PNB~bt#%J0=GXb;uWQJGkDw$v3c|IA53p*eI z$D=wXd}ryHI?=Ct9&C)zG2xz2APfSW8;*(uErA~uo?WmsR<2|U7%;M^AqCH@3I&j>&L2;8E8D)`6MBI%si zV%401yABj-3$-a~(SDA5S>=k=aVlf%5AXlZ3zK*N)rq8R~8r9xvVO!`q3+Cx^h1}l8lkPNSh3lG@H_mk+t>X#L1ByI|co5r7l_bX8jB5yJxA0?8FTfP)uc`Q?^XEYk#j=$QH{~gV?E~E{f5FAKphJ zJSU`=WRy|6L7Obmj#dFtG-{WqK8r9WD^<%Qd}5q|NYmrvjq=Qis)@vMAreq|A(yOf z48b6MO6AMkBX6r*QXQ=_O8={W(t%*X*Mn(Ajym>iTbRFc^(H6lzA8!?6`Aqr*_3A_ z+#?$bx#8R+OLm!m=go`_k4}Be4~KPTGDbVd7}Wsdgw%U7)~@FA&0gIr$NEoG31Nev zx6~!@x`FdL-hDsO{UfhiqB;kxCrVXJSX%^FA%_us^no$pcP9qFzPajNSc%|S%;PkK zfe2lvfrlcr0DnYT^Dhy1=PEjygLg7dlpcykNdv+#Fjy;q*#3QUXh+7n_qC4<45y-X zm`%<3n}m59Py%M@m%BLy@3048c9z`gZ4)1$#qBcu=lv3-9Gtj75Ntc|!&BQyTnb=W zG_XV(+57p;l9N$j^@)SAA)6&twKXDR-=3+Q4;=0Po&Md+cf$I2rGwC4N`S<~_+E(x z2EVAJdXSH%at6;HAN`QjAUu(?5IPokt~*LJAP}`D4V}PLU0*_`_<`%A9`_EVWlCiHrr8JMU5qGu zR%85**=e*%N7M+l@6*ypsX!WfJY#0#+X^;z#-`QJM>Hs0%{?3Ttv>J<+LS`gpR&=i zTp`sUQ{nf&Gbd@a8XCupRySOmN3585l18W$BK33d$%ZXILfGouiz6(L%T7tzaXvC# zCMB_`Y4;Z_2K*QjidC>7%2fjKN@16!1x}z$&SaI;@s2x6>~~KOHEtIjcj@s z`mEKVeVV(3#|wa@8SwneFw4w7NSB@q1-b+V@FvhN&X6@-0C`;Ov*nPw zh3c@&K;Q?L9AoZLa84}cqc>&EU;nh`l zb04FnYDDDOGe+~Yro5PUQ!ct}_TZ)2xdt}QY%JS3%IsDejY^{%I&T`3gewRCL&;i@ zZ0G=q&Fv|f0jmUS)W+i?e#%!_lIkd$n|mOJ2t$luht%~ug^D!QH>H07ET1~`!X|9OZbH);QYSsN zw!&3FXp)+L-mI*4Rv5n$33gkCbE!jB{1D5WA?=cKlYr$~I5E@3T!vR{u^19kspLK* z-s*^sqO)4AOtl&+dNXvR;8$okEx5LX~le-bKb+t~WA&|HyF;k6cgWvjd?eFpq( zP^QQ3Qh9))D=aJiIhcIZ2nXTn56Cw^i&~oqRt>X*6g$YCa|O>HG2_X(iU-ta&$-OX z>^VWimKEhbDdn#t8n2SBlnFY^la~!N1*9 zSMRQ&mw~CBhUE=e*BaWNHePswcmiFwEp#Sr0;2t>%IY(zL*JG;D^%@L_1{C^yh+;F zB^C>VW0qO~M&sN=7g{Srb$eP;qvnc)I=pMi@pe=>Vl2m8<^*va)T~)}gJmeVjjL)gC`zS?!C^Ylu`y_@dP_S5NheALWzfIgl?o%O4%PXD`ob#(q_S zqOeI#KZOYW7i$~$OGM(<)f-BdH;6C9rLSAv((LgZOklB29dG{$o0M!jR~^=~@HC$A*P)9%M_l)A2lT;&sGz>= zgH(vnXck8W6Fewv@vRVnG-FMFf<5evp{Ql}t2|9n`B=l*7o)RZK%82-_mdE0rt&@| zw%Bfl9>w3sVUMj~?y^M=_?j`p*Nj)N$8$aOO?wD;OI`b)$n?E(Q!qI~8Dsj{j}5z? zY>i?0nEs3EmN09NPR^ffm|Q)%0;+-;lhp!N6KKX-<=n$kz=nJb@TasvwzR2n{5s8D z9n8|sW1R)Bj~!D_2Oa@|p4N`1OGi+81p_Bpc7=H9i-~Ot33VfC{u6kRmfR45sI3|n zk?~3B%3i>nU|W9#PX)onH`&Oe&7tbR-oQ{1^ghCRu@!H;-I?E^N6Xgj%5m`P` zQ6^pYkDngO^1X8vMxY_WP53vQ8^>~+Ocej#=Gw0D=Up;t`{10-W8+4g-#&QE__XbV z)0`ssJ1$$jmK+bIi*beh8HpbEMe-`&_0fLvE}y)65q@RJ-vVlh%ru``8HI@H1!}rX z`aDJ5eu_4I3gKCCNo`ba@n~gXz)}RAB1=lryn$ z`<4NLMyjD7=u?Mo38K!FOJg~?asp}!WdBd!=X|<-3oWguoKcqIe$_KWwOckUTrC|1 zU506UT{!d>F+2DAWEEQuG^Yyo0WYysW$OclPv%dJ!KqT&YMDaB&i$#e;g}-ACv&9^ z0p{v7z_{321L^y3uskue`=5ICf9Q82S~FPsCe`+@yLTPX0^a%M>nC^aWv8GxGPl(u z;UZaCJ@`{HI)5X&I9DAPWjl|-MPu41l1D;OD>G0dbRAZbBZLrfsFl?&nP~Wytt*@b zODh}SO{Gi!5^{=E&6nVgi@m$Ko;H<);0&S`#(X^r(e@hJv??^B8}7r@bUm4C?_X{? z>v%I^lRJ;vHN$x`Aw64inPriCaW?NNTc+5H1Uy1D?85eCvb_Mq$B33?zh{SW_mmvG z17c<(FzPZ9$yp;?Mj_AA+7gd#xLuR_<-(D?TC6Ij8{~j1kU_IL^yXHPQ_rR>Eyjgk zcF4a{FW71E{`*VEA_hq&Zu2Zv&J&!)ueESKXf%m&EHS(vf!Dn2I+Vl zXx{5x^-%<1fnxF(e%LnL_2$k|ux^-q;GCma)m-6G%YY;J#KjiBT7f5!H(_+O*+9~z zlpYc#FQ)39=8n`=bY@A69zb z)9Gn_p9h#2eY6UWTJm1a_&ET>R+u9EGiqcR2YX=r6O@8avR-9>51yF>WS7?Wd$<<~ zF~yd2lU*E~E@Gc;Ugnc%tO8fD!s}=Ixl+|%ZlO&%q5W9rkA%w%NFHC=<@{LFBv!a=g}`H!;8LnCeG4?}7HsEI>xAWHz`IzoA@7OU!3QR>@~o z5&VnO9It%tNri^)bTj~S9?a>_#`_`(1~}fUy2hG|2d%db1LU-UVX>3w z>??ZFZMNTx%gm{MUCe3pp;GN1?Z^Miy2I6a@QxeXK<&%J+gu z7>+hB8z21;vVDI82rT2bW*_}Nv_RS6RL{hK$c(zZHae0?OE<_vE+L4H8#i-~2-xH_Y2OJ5pi}@RpyV^7VDD&qW@P0o>giwkoxg z=~5%C&bl;9eBz7JQ}ndYprb3mT)5yTD4e3)t+ez>%5W(Zez1Jp5AGzn92o{c8?7kh zXz4IAzC$~vHKn|%qK-we(TOd%Mfa4{BnDmgEXNeMda&+#Q!4JMExYt5i+tidX#5Cv zkAx>d3?r6JFy<0|xle55VA7A!Ul0Qj%cf~o%5`)f(UlN%T0uO-)QkgsOT!?a?M^p0 ziL`3!3ZG0pVV3}ql8-UPG7=#$sZYkTfJvXb|KBjFEDV#93FT5iUr>eV3s?4Ny5PDL zu77ChMHWQ9^6}35k5umAgo3BEyCm9h+qWBU1dC~JhTs_ z@q!G{Ao&74TBUj6rzZEBuwMOhtFRC9Vv1sofjIai*t(5Z>pcbO+N*Os?vk>!xbceZ zm2_#e=sCsqxJ)hJCkfe#rv4(2dk@|OE>mS`k!H7hHge4t1-g;mNWMrBx!k8W<6)hs z^sWe&Rtm@sUn(M}IOJ*59kvEzoHRbirA{}_k>Zu*pu`VM?pX=!Tpix|E>aw6ucK|2 zu}no%$QVI6j)tmoYUFuw*;Ag$p7e}o_u1{+qK)cwl=;${TxiV38Yh#*P*G2Em#lHe z=VWIybLHXGU1hMcnVlVbok96fkR*H+M>MIR<(lzJ*d@N*QUpSE&TXW(OPYxF(lhFG z4>#8Empw?b4ZCGo_X?jpwB>4(IvtoWNOAh^LPhe>4qiRjQrTnMPpQWCpa-P+H3rB1 z*GSvvEiJtTc!Su@cu}s|2dN%ULB^ONkFzhG;j*SqfN-1v;s*tz&It43xIsR0sYjhr z>!IEn;W)6*+;1B-E2(*oUp)-aIO^?kuW_jdx4AdnyMOJ6ixO8sDiL!V<|;^YsfV}< zG%j_#t3c~gCsfz$a}}hx)I;kHt^!@9!lNd@GWL}oqkOiI5I~=yrCVUDZ|JTVGA3yB zzl26BZ29@5&t?fxHgB0^Ru5L4c+spLqzZyLl5TO5DI!DYXcF?TV~%{Akwd-+^VN=k z6^BNqgH>|q5NVR&>{Txj>kXTciM?f(FzJuIq8E~pKyLO13GVK%d(bw$SdsEA9k7cs zbM)d(Rk!r&^zwI_7oJ;DU*;JU8=NF(C7C(qiJR~te%?}<72l|0^~b8gN+$M^R{1YJgjMzOZGG{Iu{9duF zOE%-qu*rbVjLa2nY&M}iwQ53jOPXyfZ{TxIOo4t!bO|(Hz|R+;TBy3a*Kls!uF^gG zN=Bq{hTgVmX0;;&z6lS@uGP54I5ak9=$^g6cpK9XySeDt(}$xHIk+&4N=$0(;Gz-_ zKNDamb>_Yr55TTHhoanv8{LQ9$fbjfAdZRvU?betT=dXAe&lXUJnYUC<257`eG*(G zq)k-D8%H?t02rR*CJsOYzFS0>4kG{}Asch5e(khg)j$1KqEQ9E?>)%i{oReiSP;p& z)EP~IVMMJIi$5z$ppJR}i7WoX=rigJ*lVCqNyuAR=@#nIj;2j=5q8#?1yP1WyP?h5 z#>iU#-IMYU+Vp_(2Zq7KP2w5oo2LVB+;gJQ`xb7wy22x#$o`&>MI5K~Ozn^Ap|4kZ zo3v8;6HaRevM`?r!Wh^Y;SqAmEa0^bF4J=Vp+xOS@q? zpTWgMrX_R2As0|koRpEZX}(LP1C11?V!5V!V6K3>BW-|=6V1u)Tl|ke6;l{PY^m(L z#_y8p!26{0$PP#w#9ufR=Av5H^#t>8G`SlCu=EVyB>&qsze{1eO|=ltfq)!~~rY+%y zWoy!}`zQK2j z>G&kJ)#R!JA>!-9d_)xAEUHe~%}ykWT)$HzpjTZ3B$xfWi9k79gdQbT2I_8@4wS15Nw6ka}h zgVXKT&G7cW0V+Ki+|HfOxOHnxgU}s?%^$9oV4ccsQ& z=)I1Eupp<_tJX24#6}ZUclABb@U34i{>CGx{KMbc{_Hud<(*&D?5&G=txF-tUdlev zSgm~RU{nL+DFDdUFT6)^w4)fyR@i~Um+U~-$2^3@8zi?=Qv~?aoD>=M3FblxA2ptl z;2I{-fcFzpy=ChIKs1uVDbOtw@y%bCJiEFjSEZrs+}Ltii|xn(Y}P{aw4`Obvi60;V{r#h8iT*`fYbR%$4*C^Ln*p2~U~Rkq-`~$%xi9DaJm;L}Jm-17&#GOa%blJdIuTOd$C#VmtYmrd zk`VqziBBx?T>`-4suaF8jsj5Ho8J^R{3>lT1QP62WQ1?tz?(3c`%=y8;a`HDU+;Uy z2&CuoH?ceB^)e08vk%WKuYa~TZLePrui^DoLodAu9oPa~2xl&T^9;*>4SV*=!l*mA zX;Xi|=BkAQyB;+D;C)6;#Q7YW7{8&HXoPh1Y7+(r$y*>wWAU5Z5ZZmQz-~~GwUzq( z5%ux^(pKgtQ6IkbcemPO@qa#hA5_XLA8Jz_W-Hkfx7MddOzq^&eN$G@0LlWLsZ}i9 zk)i@X#r_*y6sL!abJ1!t$z1+iiGF^24tk~J0Zy^=9*%=y%>aU5V|kTBnyJiVy2CJfmu( zW6Rf>AKXlvD?0z1K0)N7M$;?+8b=FJ9s}M)beLB9oWS+;f2jiKXU!!OM>(QD*QZtMOb<;26sru2Dhs zRE7e`tt>BE?=c)-gmQcZIK(h22V6fKTju#uE{FN6Mrhl^nH^%X*PB0t{-_Ra&U_iX zJxnf;3DOODX_&H%X038KQ936?M+1zWuUdiUwq^cS3Ae^dl`L-B6S^c}?Zv8#*DKADVw6(0UcO0DUL=%Wi?+{?PN9B0~X>c1r&pHBM_WVWXXmGVGknQ|5sr!ZqMHAoqMo|!j(hNa|L6`RG%iAmcrt*cP!C2i0=1m&KGy85-FEp)>xj9aSMilY)?i!pgGhqv& zia#P4J2)jJjfZVg4mxo8J4=cq;nrmJQhdQd3cgr&NaAy*HlZ=hW6|En!ot3gx5k%~ z8>PLG-t1@OsREdtfxRa0*uJO8z+S74Wc#p@(o(p)=?kDaV9nDkaf3O%_|jS^pq@~} zZ$+88V{hceFo4uHYGI#UqjD?4qkn5cNwA2oOaHN!@LC>(qy)H7?`eXUq(VLhNALE7 zQtr`b_<2^xyd>AWWOS?!bttsy%r9-GlZEBmv-!xvD6P3wLLh}hafnRu44TdGgI68$ z1&(4ap?2{CoG0qL{hj0^oh3wce5jE*>00i`pLpE4fB}MZ=2q@Ml+6P-XulmvRh9OHQKJsH?AF zf3CQHMI}yH238FeE0#9uVfOlo84XfJ2Tq}XYJ3I8kRJGl7+2p&V9}Pq1+ZVDGHfh? zBiCkgq-%Z(f_lGx8m(K%Yt5xnjtUJBVV<~|pKrydeH^oV8}QN#qr=QzNryF>DFkr- zN0Lpf%o>R>s}mbpO!YdMP`quF&?x`;fLK{9BUjO<`i+#w-{}*lA0gWS-CY1X)8Tp? z7Bm7Su|<7Ib3zMUAII^SsjOf{uv^YfSTX7BplV-5n&x;kx1Wv|_TMZy zvZ54Yc4!Vli~yEuJFryuDtpTBWob3iUmvz?Z;Seb=8(2qSEuEiv%im8IeS3Fxz0Pa zFZY+zzFqwDib)G@rMie?9~9NIS2H4PsBAO#*FVUz&*1d)rhH&7UV+%c z%r_eAL5MNtxwW(mJpwj>uTeED8h(nO!XUc&vXl~wg=vbxJw@-D5{_4svuumB_#3Ld zdh47)nNvS)p%ELp-Ya7tzDUkbsDYN8#xnW5H>}b49%bgew-%hH^%1loh^FDW;dO$X znHQO5acmiu9045mlyG5+0U4#trQF1;FB^K>8*_>GYv2O{S)AsT@3s&h8YA;`nabMX z{bmaXXlJMl>4g2)XfZ3&o3BeHLJ6sPA+Z%Xj{4e>>V z-&9hSP*HLP$Ezh)yZ@`3VEd3zp)=b2lQg_sfJ04yI{Zj#E0`OWcNETvHJ`(Wnsd^D zdgUuVdB@0<(#LNiFSFxmI3_8V$yEc*$E2k3a1j4|EN(y;rv_*LH1n||Ha zb02c{w)Iu%zuGx#G_|y6Rc}k9Tmfk9VyF6-OTF`M@ALig^QwH zlY`(&8+j(na9PZcpy7evDRutc|Hvb2i)FlAhu%ECM)+KWAK4p+-(q!u`v!D!c>uDC zJeTU5YZCTcirP|B>K4nmi_{sx%SstPLHsp{yR=w%yCH@*E{w~DduEh1K zxA0a?o zxcq0ChF(OR8@+M9|L-^*^_KKoaV#$=aUMgQq2DWQdJGUVzoolSeQ1Q-rg2K0W+MFj z-hTT>eddhc(DqmLq10P>gx-{!gSi)R+IneYFY?gl{UhzZkvuCXIxr%Z!juyt6L+s~C()ED%_$>Q$e_toGq?=%tGiY(8-z z9vvIrq+>(y6HVLdJ!Yy$fMvk>d@t2z>bI`iD(IP&vA;VJ!Rh12K3&cMHn(<&DXtQ# zW2;X9eF_S#ddR?nSp1@BvLJ{!q_Nev)fbbe?Y8 zOfmLezsHq<%Sih$;P;WTaw(2){m;lr_W>I@bzK z;!6bfjjdXfw>~uJqWMn%TrSuayScn|_yGvBqpF+k@Q- zgXM3Pu%<(*@CBeg`m;|58zSb_s4(w2DS787%e!dxLwe~?1kIUsit&A-a+sS5QwPQF zUO`n?){EcO5q@};uWFg!InD1R4GfufC=F{}rh4jz>T~gMIOUw@-S|h;WOP_bWyvoB@u8PUijzFU`+tlv~o1Q{sQ z2|1MI>kHhlQXSI{uWYN8Xrbd5&?qrDT;U?x3|t)cKB0d|LLxMOHlw8i38YTo{aau2 zxcZuB#(y^t^BBg((jl1Ryo`NSEq9s=^NTCE<8w6JnS$$*ZQ}23f1g>E;GRK@{>QKC zq0$zZ^(^iKFgiP@YEEDS(JG+;DBSX{A#^|rj3+|gpId476+Bo7K+XIs2QyZ_`X0~# z?376Q6Pq*DZZ%VAG*w_%g1jBjszEf5I(B1Q?*tk=}OmWXlT zmz}3$RM?7G-bq`6$(aw#Sy=R}liKn}i3^uqC^E%LyO<+tmCVsHtgUsrz|QDkb5F6; z%LWRp3xqE>jOT4M48FNmR_O&cmuF{yTY&q9L&`A_#>|>?b-+IPiWS0ROID?olxT#> zu}c3bWnIsj|Ip42gDfS$wumK~>YeWxqWSPz2v~%0D7hq*@a?6fdMJd~?o%-%^zRt7 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 + + + + + Flip7 CYD Scorekeeper Flasher + + + + + +
+

Flip7 CYD Scorekeeper

+

Flash the paired CYD game firmware directly from your browser.

+ +
>)))'> oO0 +~ ~ ~ ~ ~ ~ ~ ~ ~ + <o(((< *
+ +
    +
  1. 1. Flash CYD 1Connect one board with a data USB cable and flash the first slot.
  2. +
  3. 2. Swap boardsUnplug CYD 1, plug in CYD 2, and keep this page open.
  4. +
  5. 3. Flash CYD 2Flash the second slot. Both boards receive the same paired firmware.
  6. +
+ +
+
+
+

CYD 1

+

Flash the first ESP32-2432S028R Cheap Yellow Display in the Flip7 scorekeeper pair.

+
+ + + Use Chrome, Edge, or Firefox 151 or newer on a desktop computer with WebSerial enabled. + This page needs HTTPS to flash your board. + +
+ +
+
+

CYD 2

+

After CYD 1 finishes, unplug it, connect the second board, and flash the same firmware.

+
+ + + Use Chrome, Edge, or Firefox 151 or newer on a desktop computer with WebSerial enabled. + This page needs HTTPS to flash your board. + +
+
+ +

+ WebSerial flashes one connected CYD at a time. Close any serial monitor before flashing. + Firmware built by CI uses ephemeral ESP-NOW keys; build locally with your real keys for the paired private devices. +

+
+ + diff --git a/dist/web-installer/manifest-cyd.json b/dist/web-installer/manifest-cyd.json new file mode 100644 index 0000000..c51a551 --- /dev/null +++ b/dist/web-installer/manifest-cyd.json @@ -0,0 +1,16 @@ +{ + "name": "Flip7 CYD Scorekeeper", + "version": "3b5354a5d1c4", + "new_install_prompt_erase": true, + "builds": [ + { + "chipFamily": "ESP32", + "parts": [ + { + "path": "firmware/flip7-cyd-scorekeeper-cyd.bin", + "offset": 0 + } + ] + } + ] +} diff --git a/examples/basic_conundrum/main.cpp b/examples/basic_conundrum/main.cpp new file mode 100644 index 0000000..e6bc8cc --- /dev/null +++ b/examples/basic_conundrum/main.cpp @@ -0,0 +1,122 @@ +#include +#include + +#include "DebugCanvas.h" + +TFT_eSPI tft; +DebugCanvas canvas(tft); + +namespace { +uint32_t frameNumber = 0; + +void drawConundrumScreen( + const char* scramble, + const char* hint) +{ + canvas.setScreenName("ConundrumRound"); + canvas.clear(TFT_NAVY); + + TFT_eSprite& ui = canvas.graphics(); + + ui.setTextDatum(TC_DATUM); + ui.setTextColor(TFT_WHITE, TFT_NAVY); + ui.drawString( + "CONUNDRUM", + DebugCanvas::WIDTH / 2, + 10, + 2); + + constexpr int tileWidth = 24; + constexpr int tileHeight = 34; + constexpr int tileGap = 2; + constexpr int totalWidth = + (tileWidth * 9) + (tileGap * 8); + constexpr int startX = + (DebugCanvas::WIDTH - totalWidth) / 2; + constexpr int tileY = 60; + + for (int index = 0; index < 9; ++index) { + const int x = + startX + index * (tileWidth + tileGap); + + ui.fillRoundRect( + x, + tileY, + tileWidth, + tileHeight, + 3, + TFT_WHITE); + + ui.drawRoundRect( + x, + tileY, + tileWidth, + tileHeight, + 3, + TFT_DARKGREY); + + char letter[2] = { + scramble[index], + '\0' + }; + + ui.setTextDatum(MC_DATUM); + ui.setTextColor(TFT_BLACK, TFT_WHITE); + ui.drawString( + letter, + x + tileWidth / 2, + tileY + tileHeight / 2, + 2); + } + + ui.setTextDatum(TC_DATUM); + ui.setTextColor(TFT_YELLOW, TFT_NAVY); + ui.drawString( + "HINT", + DebugCanvas::WIDTH / 2, + 125, + 2); + + ui.setTextColor(TFT_WHITE, TFT_NAVY); + ui.drawCentreString( + hint, + DebugCanvas::WIDTH / 2, + 155, + 2); + + canvas.drawDebugOverlay(++frameNumber); + canvas.present(); +} +} + +void setup() +{ + Serial.begin(115200); + + tft.init(); + tft.setRotation(0); + tft.fillScreen(TFT_BLACK); + + if (!canvas.begin()) { + Serial.println( + "[Fatal] Could not allocate debug framebuffer."); + + tft.setTextColor(TFT_RED, TFT_BLACK); + tft.drawString( + "Framebuffer error", + 10, + 20, + 2); + return; + } + + canvas.printDebugInfo(); + + drawConundrumScreen( + "FASTBREAK", + "The first meal of the day"); +} + +void loop() +{ +} diff --git a/include/DebugCanvas.h b/include/DebugCanvas.h new file mode 100644 index 0000000..b08192b --- /dev/null +++ b/include/DebugCanvas.h @@ -0,0 +1,45 @@ +#pragma once + +#include +#include + +class DebugCanvas { +public: + static constexpr int16_t WIDTH = 240; + static constexpr int16_t HEIGHT = 320; + + explicit DebugCanvas(TFT_eSPI& display); + + bool begin(uint8_t preferredDepth = 16); + void clear(uint32_t color = TFT_BLACK); + void present(); + + TFT_eSprite& graphics(); + const TFT_eSprite& graphics() const; + + bool isReady() const; + int16_t width() const; + int16_t height() const; + uint8_t colorDepth() const; + size_t bufferSizeBytes() const; + + void setScreenName(const char* screenName); + const char* screenName() const; + + void drawDebugOverlay(uint32_t frameNumber); + void printDebugInfo(Stream& output = Serial) const; + bool writeRleFrame(Stream& output = Serial); + +private: + TFT_eSPI& display_; + TFT_eSprite sprite_; + + bool ready_ = false; + int16_t width_ = WIDTH; + int16_t height_ = HEIGHT; + uint8_t colorDepth_ = 0; + const char* screenName_ = "Unknown"; + + bool allocate(uint8_t depth); + size_t rleFrameSize(const void* pixels, size_t pixelCount) const; +}; diff --git a/include/core/game_selection.h b/include/core/game_selection.h new file mode 100644 index 0000000..d9a4f35 --- /dev/null +++ b/include/core/game_selection.h @@ -0,0 +1,61 @@ +#pragma once + +#include +#include + +constexpr uint32_t kNoActiveGameEpoch = 0; + +// Identifies which game is currently active (or Home if idle). +enum class ActiveGameKind : uint8_t { + Home = 0, + Puzzle, + Mastermind, +}; + +// Tracks the currently active game: a monotonically-increasing epoch and kind. +struct ActiveGameClock { + uint32_t epoch; + ActiveGameKind kind; +}; + +// Returns true when an incoming game state packet should be applied locally. +inline bool shouldAcceptGameState(const ActiveGameClock& active, + ActiveGameKind incomingKind, + uint32_t incomingEpoch, + bool terminalState = false) { + if (incomingEpoch != active.epoch) { + return incomingEpoch > active.epoch; + } + return incomingKind == active.kind || + (active.kind == ActiveGameKind::Home && terminalState); +} + +// Returns true when a completed game may safely transition back to the Home screen. +inline bool canReturnCompletedGameHome(const ActiveGameClock& active, + ActiveGameKind completedGame, + bool deliveryPending) { + return active.kind == completedGame && !deliveryPending; +} + +// Returns true when a state packet should be sent; guards against transmitting +// stale or mismatched game state over ESP-NOW. +inline bool shouldSendActiveGameState( + const ActiveGameClock& active, ActiveGameKind stateKind, + uint32_t stateEpoch, bool snapshotMatchesCurrent, + bool terminalDelivery = false) { + const bool activeKind = active.kind == stateKind; + const bool terminalFromHome = + active.kind == ActiveGameKind::Home && terminalDelivery; + return (activeKind || terminalFromHome) && active.epoch == stateEpoch && + snapshotMatchesCurrent; +} + +// Produces the next game epoch (max of all known epochs + 1). +// Returns kNoActiveGameEpoch on overflow. +inline uint32_t nextActiveGameEpoch(uint32_t activeEpoch, + uint32_t puzzleEpoch, + uint32_t mastermindEpoch) { + const uint32_t latest = + std::max(activeEpoch, std::max(puzzleEpoch, mastermindEpoch)); + return latest == UINT32_MAX ? kNoActiveGameEpoch : latest + 1; +} diff --git a/include/core/game_state.h b/include/core/game_state.h new file mode 100644 index 0000000..9b287eb --- /dev/null +++ b/include/core/game_state.h @@ -0,0 +1,5 @@ +#pragma once +struct Player { + char name[24]; + int totalScore; +}; diff --git a/include/core/mastermind_logic.h b/include/core/mastermind_logic.h new file mode 100644 index 0000000..4825bee --- /dev/null +++ b/include/core/mastermind_logic.h @@ -0,0 +1,381 @@ +#pragma once + +#include +#include +#include + +constexpr uint8_t kMastermindCodeLength = 4; +constexpr uint8_t kMastermindColorCount = 6; +constexpr uint8_t kMastermindMaxGuesses = 10; + +// Phase of a Mastermind match — SecretEntry → Guessing → RoundComplete → Exited. +enum class MastermindPhase : uint8_t { + SecretEntry = 1, + Guessing, + RoundComplete, + Exited, +}; + +// A four-peg colour sequence used for both the secret code and each guess. +struct MastermindCode { + uint8_t colors[kMastermindCodeLength]; +}; + +// Feedback for a single guess: exact = right colour + right position, +// colorOnly = right colour but wrong position. +struct MastermindFeedback { + uint8_t exact; + uint8_t colorOnly; +}; + +// A single guess entry combining the submitted code with its computed feedback. +struct MastermindGuess { + MastermindCode code; + MastermindFeedback feedback; +}; + +// Full wire-safe match state for one Mastermind game between two boards. +struct MastermindState { + MastermindGuess guesses[kMastermindMaxGuesses]; + MastermindCode secret; + uint32_t gameId; + uint32_t revision; + uint32_t hostBoardId; + uint32_t guestBoardId; + uint32_t codemakerBoardId; + uint32_t roundWinnerBoardId; + uint8_t hostScore; + uint8_t guestScore; + uint8_t round; + uint8_t guessCount; + MastermindPhase phase; + uint8_t reserved[3]; +}; + +// Returns true when every peg in the code is zero (unset). +inline bool isEmptyMastermindCode(const MastermindCode& code) { + for (uint8_t color : code.colors) { + if (color != 0) { + return false; + } + } + return true; +} + +// Returns true when every peg holds a colour in [1, kMastermindColorCount]. +inline bool isValidMastermindCode(const MastermindCode& code) { + for (uint8_t color : code.colors) { + if (color == 0 || color > kMastermindColorCount) { + return false; + } + } + return true; +} + +// Scores a guess against the secret using per-colour frequency buckets. +inline MastermindFeedback evaluateMastermindGuess( + const MastermindCode& secret, const MastermindCode& guess) { + MastermindFeedback feedback{}; + uint8_t secretCounts[kMastermindColorCount + 1]{}; + uint8_t guessCounts[kMastermindColorCount + 1]{}; + for (uint8_t index = 0; index < kMastermindCodeLength; ++index) { + if (secret.colors[index] == guess.colors[index]) { + ++feedback.exact; + } else { + ++secretCounts[secret.colors[index]]; + ++guessCounts[guess.colors[index]]; + } + } + for (uint8_t color = 1; color <= kMastermindColorCount; ++color) { + feedback.colorOnly += secretCounts[color] < guessCounts[color] + ? secretCounts[color] + : guessCounts[color]; + } + return feedback; +} + +// Returns the board ID of the codebreaker (the player who is NOT the codemaker). +inline uint32_t mastermindCodebreakerBoardId(const MastermindState& state) { + return state.codemakerBoardId == state.hostBoardId ? state.guestBoardId + : state.hostBoardId; +} + +// Creates a fresh MastermindState with the host as initial codemaker. +inline MastermindState makeMastermindMatch(uint32_t hostBoardId, + uint32_t guestBoardId, + uint32_t gameId) { + MastermindState state{}; + state.gameId = gameId; + state.hostBoardId = hostBoardId; + state.guestBoardId = guestBoardId; + state.codemakerBoardId = hostBoardId; + state.round = 1; + state.phase = MastermindPhase::SecretEntry; + return state; +} + +// Returns true when both codes have identical peg colours. +inline bool sameMastermindCode(const MastermindCode& first, + const MastermindCode& second) { + return memcmp(first.colors, second.colors, kMastermindCodeLength) == 0; +} + +// Returns true when both states are byte-for-byte identical. +inline bool sameMastermindState(const MastermindState& first, + const MastermindState& second) { + return memcmp(&first, &second, sizeof(MastermindState)) == 0; +} + +// Deep-validates the state: IDs, phase, role parity, score accounting, +// reserved bytes, guess/feedback consistency, and round-winner correctness. +inline bool isValidMastermindState(const MastermindState& state) { + if (state.gameId == 0 || state.hostBoardId == 0 || + state.guestBoardId == 0 || + state.hostBoardId == state.guestBoardId || + (state.codemakerBoardId != state.hostBoardId && + state.codemakerBoardId != state.guestBoardId) || state.round == 0 || + state.codemakerBoardId != + (state.round % 2 == 1 ? state.hostBoardId + : state.guestBoardId) || + state.guessCount > kMastermindMaxGuesses || + state.phase < MastermindPhase::SecretEntry || + state.phase > MastermindPhase::Exited) { + return false; + } + const uint16_t scoreTotal = + static_cast(state.hostScore) + state.guestScore; + const uint16_t completedRounds = + static_cast(state.round - 1) + + (state.phase == MastermindPhase::RoundComplete ? 1 : 0); + const bool validExitedScore = state.phase == MastermindPhase::Exited && + (scoreTotal == state.round - 1 || scoreTotal == state.round); + if (scoreTotal != completedRounds && !validExitedScore) { + return false; + } + for (uint8_t value : state.reserved) { + if (value != 0) { + return false; + } + } + const MastermindGuess emptyGuess{}; + for (uint8_t index = state.guessCount; + index < kMastermindMaxGuesses; ++index) { + if (memcmp(&state.guesses[index], &emptyGuess, + sizeof(emptyGuess)) != 0) { + return false; + } + } + if (state.phase == MastermindPhase::SecretEntry || + state.phase == MastermindPhase::Exited) { + return state.guessCount == 0 && state.roundWinnerBoardId == 0 && + isEmptyMastermindCode(state.secret); + } + if (!isValidMastermindCode(state.secret) || + (state.guessCount == 0 && + state.phase == MastermindPhase::RoundComplete)) { + return false; + } + for (uint8_t index = 0; index < state.guessCount; ++index) { + if (!isValidMastermindCode(state.guesses[index].code)) { + return false; + } + const MastermindFeedback expected = + evaluateMastermindGuess(state.secret, state.guesses[index].code); + if (state.guesses[index].feedback.exact != expected.exact || + state.guesses[index].feedback.colorOnly != expected.colorOnly) { + return false; + } + } + if (state.phase == MastermindPhase::Guessing) { + return state.guessCount < kMastermindMaxGuesses && + state.roundWinnerBoardId == 0 && + (state.guessCount == 0 || + state.guesses[state.guessCount - 1].feedback.exact < + kMastermindCodeLength); + } + const bool solved = + state.guesses[state.guessCount - 1].feedback.exact == + kMastermindCodeLength; + const uint32_t expectedWinner = + solved ? mastermindCodebreakerBoardId(state) + : state.codemakerBoardId; + return state.roundWinnerBoardId == expectedWinner && + (solved || state.guessCount == kMastermindMaxGuesses); +} + +// Transitions SecretEntry → Guessing; only the codemaker may set the secret. +inline bool submitMastermindSecret(MastermindState& state, + const MastermindCode& secret, + uint32_t actorBoardId) { + if (state.phase != MastermindPhase::SecretEntry || + actorBoardId != state.codemakerBoardId || + !isValidMastermindCode(secret)) { + return false; + } + state.secret = secret; + state.phase = MastermindPhase::Guessing; + ++state.revision; + return true; +} + +// Records a guess with computed feedback; transitions to RoundComplete on +// a correct answer or when the maximum guess count is reached. +inline bool submitMastermindGuess(MastermindState& state, + const MastermindCode& guess, + uint32_t actorBoardId) { + if (state.phase != MastermindPhase::Guessing || + actorBoardId == state.codemakerBoardId || + !isValidMastermindCode(guess) || + state.guessCount >= kMastermindMaxGuesses) { + return false; + } + MastermindGuess& record = state.guesses[state.guessCount++]; + record.code = guess; + record.feedback = evaluateMastermindGuess(state.secret, guess); + ++state.revision; + if (record.feedback.exact == kMastermindCodeLength || + state.guessCount == kMastermindMaxGuesses) { + state.phase = MastermindPhase::RoundComplete; + if (record.feedback.exact == kMastermindCodeLength) { + state.roundWinnerBoardId = actorBoardId; + } else { + state.roundWinnerBoardId = state.codemakerBoardId; + } + if (state.roundWinnerBoardId == state.hostBoardId) { + ++state.hostScore; + } else { + ++state.guestScore; + } + } + return true; +} + +// Clears the round, swaps codemaker/codebreaker roles, and advances to the +// next round's SecretEntry phase. Host-only. +inline bool advanceMastermindRound(MastermindState& state, + uint32_t actorBoardId) { + if (state.phase != MastermindPhase::RoundComplete || + actorBoardId != state.hostBoardId || state.round == UINT8_MAX) { + return false; + } + state.codemakerBoardId = mastermindCodebreakerBoardId(state); + memset(state.guesses, 0, sizeof(state.guesses)); + memset(&state.secret, 0, sizeof(state.secret)); + state.roundWinnerBoardId = 0; + state.guessCount = 0; + ++state.round; + ++state.revision; + state.phase = MastermindPhase::SecretEntry; + return true; +} + +// Transitions to Exited, clearing sensitive round data. Either player may exit. +inline bool exitMastermindMatch(MastermindState& state, + uint32_t actorBoardId) { + if (state.phase == MastermindPhase::Exited || + (actorBoardId != state.hostBoardId && + actorBoardId != state.guestBoardId)) { + return false; + } + memset(state.guesses, 0, sizeof(state.guesses)); + memset(&state.secret, 0, sizeof(state.secret)); + state.roundWinnerBoardId = 0; + state.guessCount = 0; + ++state.revision; + state.phase = MastermindPhase::Exited; + return true; +} + +// Ordering result for comparing two Mastermind state snapshots. +enum class MastermindVersionOrder : uint8_t { Older, Same, Newer }; + +// Compares by epoch (gameId) then revision to determine which state is authoritative. +inline MastermindVersionOrder compareMastermindVersion( + const MastermindState& current, const MastermindState& candidate) { + if (candidate.gameId != current.gameId) { + return candidate.gameId > current.gameId + ? MastermindVersionOrder::Newer + : MastermindVersionOrder::Older; + } + if (candidate.revision != current.revision) { + return candidate.revision > current.revision + ? MastermindVersionOrder::Newer + : MastermindVersionOrder::Older; + } + return MastermindVersionOrder::Same; +} + +// Computes a FNV-1a digest over the entire state for integrity and deduplication. +inline uint32_t mastermindStateDigest(const MastermindState& state) { + uint32_t digest = 2166136261u; + const uint8_t* bytes = reinterpret_cast(&state); + for (size_t index = 0; index < sizeof(state); ++index) { + digest ^= bytes[index]; + digest *= 16777619u; + } + return digest; +} + +// Returns true when the candidate is valid and newer; used to accept incoming +// full-state packets. +inline bool shouldAdoptMastermindState(const MastermindState& current, + const MastermindState& candidate) { + return isValidMastermindState(candidate) && + compareMastermindVersion(current, candidate) == + MastermindVersionOrder::Newer; +} + +// Returns true when the observed state makes a pending delivery obsolete. +inline bool isMastermindDeliverySuperseded( + const MastermindState& pending, const MastermindState& observed) { + return pending.gameId == observed.gameId && + pending.revision < observed.revision; +} + +// Applies an exit signal from a peer; idempotent if already exited. +inline bool applyMastermindExitSignal( + MastermindState& current, const MastermindState& candidate, + uint32_t senderBoardId) { + if (!isValidMastermindState(current) || + !isValidMastermindState(candidate) || + candidate.phase != MastermindPhase::Exited || + current.gameId != candidate.gameId || + current.hostBoardId != candidate.hostBoardId || + current.guestBoardId != candidate.guestBoardId) { + return false; + } + if (current.phase == MastermindPhase::Exited) { + return senderBoardId == current.hostBoardId || + senderBoardId == current.guestBoardId; + } + return exitMastermindMatch(current, senderBoardId); +} + +// Verifies that the candidate is a single legal successor by replaying the +// implied action and comparing the result byte-for-byte. +inline bool isValidMastermindTransition(const MastermindState& current, + const MastermindState& candidate, + uint32_t senderBoardId) { + if (!isValidMastermindState(current) || + !isValidMastermindState(candidate) || + candidate.gameId != current.gameId || + candidate.revision != current.revision + 1) { + return false; + } + MastermindState expected = current; + bool applied = false; + if (candidate.phase == MastermindPhase::Exited) { + applied = exitMastermindMatch(expected, senderBoardId); + } else if (current.phase == MastermindPhase::SecretEntry) { + applied = submitMastermindSecret(expected, candidate.secret, + senderBoardId); + } else if (current.phase == MastermindPhase::Guessing && + current.guessCount < kMastermindMaxGuesses) { + applied = submitMastermindGuess( + expected, candidate.guesses[current.guessCount].code, + senderBoardId); + } else if (current.phase == MastermindPhase::RoundComplete) { + applied = advanceMastermindRound(expected, senderBoardId); + } + return applied && sameMastermindState(expected, candidate); +} diff --git a/include/core/protocol.h b/include/core/protocol.h new file mode 100644 index 0000000..e39aee8 --- /dev/null +++ b/include/core/protocol.h @@ -0,0 +1,156 @@ +#pragma once + +#include +#include + +#include "mastermind_logic.h" +#include "puzzle_logic.h" + +constexpr uint32_t kProtocolMagic = 0x46374359; +constexpr uint8_t kProtocolVersion = 7; + +// Returns true when candidate is strictly newer than previous using +// wrapping signed arithmetic (handles sequence-number rollover). +inline bool isSequenceNewer(uint32_t candidate, uint32_t previous) { + return static_cast(candidate - previous) > 0; +} + +// Returns true when candidate matches any entry in the sessions array. +inline bool hasSeenSession(uint32_t candidate, const uint32_t* sessions, + size_t count) { + for (size_t index = 0; index < count; ++index) { + if (sessions[index] == candidate) { + return true; + } + } + return false; +} + +// Returns true when the candidate session ID is acceptable: first contact, +// known session, or unknown session from an offline peer. +inline bool canUsePeerSession(uint32_t current, uint32_t candidate, + bool peerOffline, + const uint32_t* retired, size_t retiredCount) { + return candidate != 0 && + (current == 0 || candidate == current || + (peerOffline && !hasSeenSession(candidate, retired, retiredCount))); +} + +// All ESP-NOW packet types exchanged between CYD boards. +enum class MessageType : uint8_t { + Heartbeat = 1, + ScoreUpdate, + FullState, + RequestState, + PuzzleState, + PuzzleAck, + MastermindState, + MastermindFullState, + MastermindAck, + MastermindRequestState, +}; + +// Fixed-size header prepended to every protocol packet. +// Fixed-size header prepended to every protocol packet. +struct PacketHeader { + uint32_t magic; + uint8_t version; + MessageType type; + uint32_t senderId; + uint32_t sessionId; + uint32_t sequence; +}; + +// Keepalive packet; carries uptime for diagnostics. +struct HeartbeatPacket { + PacketHeader header; + uint32_t uptimeMs; +}; + +// Carries a full PuzzleState snapshot for initial sync or reconciliation. +struct PuzzleStatePacket { + PacketHeader header; + ::PuzzleState state; +}; + +static_assert(sizeof(PuzzleStatePacket) == 68, + "PuzzleStatePacket wire format changed"); + +// The protocol is memcpy'd raw between two identically-compiled ESP32 targets. +// Pin the wire-visible field offsets so any accidental reordering or padding +// change fails at compile time rather than silently corrupting sync. +static_assert(offsetof(::PuzzleState, tiles) == 0, "tiles offset changed"); +static_assert(offsetof(::PuzzleState, columns) == 30, "columns offset changed"); +static_assert(offsetof(::PuzzleState, rows) == 31, "rows offset changed"); +static_assert(offsetof(::PuzzleState, theme) == 32, "theme offset changed"); +static_assert(offsetof(::PuzzleState, phase) == 33, "phase offset changed"); +static_assert(offsetof(::PuzzleState, gameId) == 36, "gameId offset changed"); +static_assert(offsetof(::PuzzleState, revision) == 40, + "revision offset changed"); +static_assert(offsetof(::PuzzleState, turnBoardId) == 44, + "turnBoardId offset changed"); +static_assert(sizeof(::PuzzleState) == 48, "PuzzleState size changed"); +static_assert(offsetof(PacketHeader, senderId) == 8, + "PacketHeader senderId offset changed"); + +// Acknowledgement for a PuzzleState or FullState delivery. +struct PuzzleAckPacket { + PacketHeader header; + uint32_t targetBoardId; + uint32_t gameId; + uint32_t revision; + uint32_t stateDigest; + MessageType acknowledgedType; + uint8_t reserved[3]; +}; + +// Requests the peer to send a full state snapshot on divergence. +struct StateRequestPacket { + PacketHeader header; + uint32_t gameId; + uint32_t revision; + uint32_t stateDigest; +}; + +// Carries a full MastermindState snapshot. +struct GameStatePacket { + PacketHeader header; + MastermindState state; +}; + +// Acknowledgement for a MastermindState or MastermindFullState delivery. +struct GameAckPacket { + PacketHeader header; + uint32_t targetBoardId; + uint32_t gameId; + uint32_t revision; + uint32_t stateDigest; + MessageType acknowledgedType; + uint8_t reserved[3]; +}; + +// Requests the peer to resend its authoritative MastermindState. +struct MastermindStateRequestPacket { + PacketHeader header; + uint32_t gameId; + uint32_t revision; + uint32_t stateDigest; +}; + +static_assert(sizeof(PacketHeader) == 20, "PacketHeader wire format changed"); +static_assert(sizeof(HeartbeatPacket) == 24, + "HeartbeatPacket wire format changed"); +static_assert(sizeof(PuzzleStatePacket) == 68, + "PuzzleStatePacket wire format changed"); +static_assert(sizeof(PuzzleAckPacket) == 40, + "PuzzleAckPacket wire format changed"); +static_assert(sizeof(StateRequestPacket) == 32, + "StateRequestPacket wire format changed"); +static_assert(sizeof(MastermindState) == 96, + "MastermindState wire format changed"); +static_assert(sizeof(GameStatePacket) == 116, + "GameStatePacket wire format changed"); +static_assert(sizeof(GameAckPacket) == 40, + "GameAckPacket wire format changed"); +static_assert(sizeof(MastermindStateRequestPacket) == 32, + "MastermindStateRequestPacket wire format changed"); \ No newline at end of file diff --git a/include/core/puzzle_logic.h b/include/core/puzzle_logic.h new file mode 100644 index 0000000..6294ff8 --- /dev/null +++ b/include/core/puzzle_logic.h @@ -0,0 +1,478 @@ +#pragma once + +#include +#include + +constexpr uint8_t kPuzzleMaxTiles = 30; + +enum class PuzzleTheme : uint8_t { + Greek = 1, + Planets, +}; + +enum class PuzzlePhase : uint8_t { + Playing = 1, + Exited, +}; + +struct PuzzleSpec { + uint8_t columns; + uint8_t rows; + PuzzleTheme theme; +}; + +struct PuzzleState { + uint8_t tiles[kPuzzleMaxTiles]; + uint8_t columns; + uint8_t rows; + PuzzleTheme theme; + PuzzlePhase phase; + uint32_t gameId; + uint32_t revision; + uint32_t turnBoardId; +}; + +// Returns true when both specs describe the same grid size and theme. +inline bool samePuzzleSpec(const PuzzleSpec& first, const PuzzleSpec& second) { + return first.columns == second.columns && first.rows == second.rows && + first.theme == second.theme; +} + +// Returns true when the spec is one of the two supported puzzle configurations +// (3x3 Planets or 4x3 Greek). +inline bool isSupportedPuzzleSpec(const PuzzleSpec& spec) { + return (spec.columns == 3 && spec.rows == 3 && + spec.theme == PuzzleTheme::Planets) || + (spec.columns == 4 && spec.rows == 3 && + spec.theme == PuzzleTheme::Greek); +} + +// Extracts the PuzzleSpec (dimensions and theme) from a full PuzzleState. +inline PuzzleSpec puzzleSpec(const PuzzleState& state) { + return {state.columns, state.rows, state.theme}; +} + +// Returns the total number of tiles (columns * rows), including the blank. +inline uint8_t puzzleTileCount(const PuzzleState& state) { + return static_cast(state.columns * state.rows); +} + +// Creates a solved puzzle in the goal arrangement. Tiles are numbered 1..(n-1) +// left-to-right, top-to-bottom; the blank occupies the last position. +inline PuzzleState makeInitialPuzzle(uint32_t firstTurnBoardId, + uint32_t gameId, + const PuzzleSpec& spec) { + PuzzleState state{}; + if (!isSupportedPuzzleSpec(spec)) { + return state; + } + state.columns = spec.columns; + state.rows = spec.rows; + state.theme = spec.theme; + state.phase = PuzzlePhase::Playing; + const uint8_t count = puzzleTileCount(state); + for (uint8_t i = 0; i + 1 < count; ++i) { + state.tiles[i] = i + 1; + } + state.gameId = gameId; + state.turnBoardId = firstTurnBoardId; + return state; +} + +// Returns true when the two tile positions are horizontally or vertically +// adjacent (Manhattan distance == 1). +inline bool arePuzzlePositionsAdjacent(const PuzzleState& state, + uint8_t first, uint8_t second) { + const uint8_t count = puzzleTileCount(state); + if (first >= count || second >= count || state.columns == 0) { + return false; + } + const uint8_t firstRow = first / state.columns; + const uint8_t firstColumn = first % state.columns; + const uint8_t secondRow = second / state.columns; + const uint8_t secondColumn = second % state.columns; + const uint8_t rowDistance = firstRow > secondRow ? firstRow - secondRow + : secondRow - firstRow; + const uint8_t columnDistance = firstColumn > secondColumn + ? firstColumn - secondColumn + : secondColumn - firstColumn; + return rowDistance + columnDistance == 1; +} + +// Creates a pseudo-randomly scrambled puzzle derived from gameId and the board +// identities. Guarantees the result is not already solved. +inline PuzzleState makeScrambledPuzzle(uint32_t firstTurnBoardId, + uint32_t gameId, + const PuzzleSpec& spec) { + PuzzleState state = makeInitialPuzzle(firstTurnBoardId, gameId, spec); + if (!isSupportedPuzzleSpec(spec)) { + return state; + } + + const uint8_t count = puzzleTileCount(state); + uint8_t blank = count - 1; + uint8_t previousBlank = kPuzzleMaxTiles; + uint32_t random = gameId ^ firstTurnBoardId ^ + (static_cast(spec.columns) << 24) ^ + (static_cast(spec.rows) << 16) ^ + (static_cast(spec.theme) << 8); + const uint16_t steps = static_cast(count) * 8; + for (uint16_t step = 0; step < steps; ++step) { + uint8_t candidates[4]{}; + uint8_t candidateCount = 0; + for (uint8_t position = 0; position < count; ++position) { + if (position != previousBlank && + arePuzzlePositionsAdjacent(state, position, blank)) { + candidates[candidateCount++] = position; + } + } + random = random * 1664525u + 1013904223u; + if (candidateCount == 0) return state; + const uint8_t next = candidates[random % candidateCount]; + state.tiles[blank] = state.tiles[next]; + state.tiles[next] = 0; + previousBlank = blank; + blank = next; + } + bool solved = blank == count - 1; + for (uint8_t position = 0; solved && position + 1 < count; ++position) { + solved = state.tiles[position] == position + 1; + } + if (solved) { + const uint8_t next = count - 2; + state.tiles[blank] = state.tiles[next]; + state.tiles[next] = 0; + } + return state; +} + +// Returns true when the tile arrangement can be reached from the solved state +// via legal moves (inversion-parity rule). +inline bool isSolvablePuzzleArrangement(const PuzzleState& state) { + const uint8_t count = puzzleTileCount(state); + uint16_t inversions = 0; + for (uint8_t first = 0; first < count; ++first) { + if (state.tiles[first] == 0) { + continue; + } + for (uint8_t second = first + 1; second < count; ++second) { + if (state.tiles[second] != 0 && + state.tiles[first] > state.tiles[second]) { + ++inversions; + } + } + } + if (state.columns % 2 == 1) { + return inversions % 2 == 0; + } + uint8_t blank = count; + for (uint8_t position = 0; position < count; ++position) { + if (state.tiles[position] == 0) { + blank = position; + break; + } + } + if (blank == count) { + return false; + } + const uint8_t blankRowFromBottom = + static_cast(state.rows - blank / state.columns); + return (inversions + blankRowFromBottom) % 2 == 1; +} + +// Returns true when the state has a supported spec, valid tile permutation, +// correct phase, non-zero IDs, and a solvable arrangement. +inline bool isValidPuzzle(const PuzzleState& state) { + const PuzzleSpec spec = puzzleSpec(state); + if (!isSupportedPuzzleSpec(spec) || + (state.phase != PuzzlePhase::Playing && + state.phase != PuzzlePhase::Exited) || + state.gameId == 0 || state.turnBoardId == 0) { + return false; + } + const uint8_t count = puzzleTileCount(state); + bool seen[kPuzzleMaxTiles]{}; + for (uint8_t position = 0; position < count; ++position) { + const uint8_t tile = state.tiles[position]; + if (tile >= count || seen[tile]) { + return false; + } + seen[tile] = true; + } + for (uint8_t position = count; position < kPuzzleMaxTiles; ++position) { + if (state.tiles[position] != 0) { + return false; + } + } + return isSolvablePuzzleArrangement(state); +} + +// Returns the index of the blank tile (value 0), or puzzleTileCount() if not +// found (should never happen in a valid state). +inline uint8_t findBlank(const PuzzleState& state) { + const uint8_t count = puzzleTileCount(state); + for (uint8_t i = 0; i < count; ++i) { + if (state.tiles[i] == 0) { + return i; + } + } + return count; +} + +// Returns true when the tile at the given position is already in its goal slot. +inline bool isTileCorrect(const PuzzleState& state, uint8_t position) { + const uint8_t count = puzzleTileCount(state); + return position + 1 < count && state.tiles[position] == position + 1; +} + +// Returns true when every numbered tile occupies its goal position and the +// blank is in the last slot. +inline bool isPuzzleSolved(const PuzzleState& state) { + if (!isValidPuzzle(state) || state.phase != PuzzlePhase::Playing) { + return false; + } + const uint8_t count = puzzleTileCount(state); + for (uint8_t position = 0; position + 1 < count; ++position) { + if (!isTileCorrect(state, position)) { + return false; + } + } + return state.tiles[count - 1] == 0; +} + +// Returns true when both states represent the exact same puzzle (all fields +// including tiles, revision, and turn ownership). +inline bool isSamePuzzle(const PuzzleState& first, const PuzzleState& second) { + if (!samePuzzleSpec(puzzleSpec(first), puzzleSpec(second)) || + first.phase != second.phase || first.gameId != second.gameId || + first.revision != second.revision || + first.turnBoardId != second.turnBoardId) { + return false; + } + for (uint8_t i = 0; i < kPuzzleMaxTiles; ++i) { + if (first.tiles[i] != second.tiles[i]) { + return false; + } + } + return true; +} + +enum class PuzzleVersionOrder : uint8_t { Older, Same, Newer }; + +// Compares two puzzle states by epoch (gameId) then revision to determine +// which is authoritative. +inline PuzzleVersionOrder comparePuzzleVersion(const PuzzleState& current, + const PuzzleState& candidate) { + if (candidate.gameId != current.gameId) { + return candidate.gameId > current.gameId ? PuzzleVersionOrder::Newer + : PuzzleVersionOrder::Older; + } + if (candidate.revision != current.revision) { + return candidate.revision > current.revision ? PuzzleVersionOrder::Newer + : PuzzleVersionOrder::Older; + } + return PuzzleVersionOrder::Same; +} + +// Computes a FNV-1a digest over all state fields for integrity checks and +// delivery deduplication. +inline uint32_t puzzleStateDigest(const PuzzleState& state) { + uint32_t digest = 2166136261u; + const auto addByte = [&digest](uint8_t value) { + digest ^= value; + digest *= 16777619u; + }; + for (uint8_t tile : state.tiles) { + addByte(tile); + } + addByte(state.columns); + addByte(state.rows); + addByte(static_cast(state.theme)); + addByte(static_cast(state.phase)); + const uint32_t fields[] = {state.gameId, state.revision, + state.turnBoardId}; + for (uint32_t field : fields) { + for (uint8_t shift = 0; shift < 32; shift += 8) { + addByte(static_cast(field >> shift)); + } + } + return digest; +} + +// Returns true when the observed state has superseded a pending delivery, +// indicating the delivery no longer needs to be sent. +inline bool isDeliverySuperseded(const PuzzleState& pending, + const PuzzleState& observed) { + return pending.gameId == observed.gameId && + pending.revision < observed.revision; +} + +enum class ReconciliationAction : uint8_t { + None, + SendFullState, + RequestFullState, +}; + +inline ReconciliationAction decideReconciliation( + const PuzzleState& local, uint32_t remoteGameId, uint32_t remoteRevision, + uint32_t remoteDigest, bool localIsAuthority) { + if (remoteGameId < local.gameId || + (remoteGameId == local.gameId && remoteRevision < local.revision)) { + return ReconciliationAction::SendFullState; + } + if (remoteGameId > local.gameId || + (remoteGameId == local.gameId && remoteRevision > local.revision)) { + return ReconciliationAction::RequestFullState; + } + if (remoteDigest == puzzleStateDigest(local)) { + return ReconciliationAction::None; + } + return localIsAuthority ? ReconciliationAction::SendFullState + : ReconciliationAction::RequestFullState; +} + +inline bool tryPuzzleMove(PuzzleState& state, uint8_t position, + uint32_t actorBoardId, uint32_t nextTurnBoardId) { + if (!isValidPuzzle(state) || state.phase != PuzzlePhase::Playing || + state.turnBoardId != actorBoardId || + actorBoardId == nextTurnBoardId || state.revision == UINT32_MAX || + position >= puzzleTileCount(state) || state.tiles[position] == 0) { + return false; + } + const uint8_t blank = findBlank(state); + if (!arePuzzlePositionsAdjacent(state, position, blank)) { + return false; + } + state.tiles[blank] = state.tiles[position]; + state.tiles[position] = 0; + ++state.revision; + state.turnBoardId = nextTurnBoardId; + return true; +} + +inline bool exitPuzzle(PuzzleState& state, uint32_t actorBoardId, + uint32_t otherBoardId) { + if (!isValidPuzzle(state) || state.phase != PuzzlePhase::Playing || + actorBoardId == 0 || otherBoardId == 0 || + actorBoardId == otherBoardId || state.revision == UINT32_MAX || + (state.turnBoardId != actorBoardId && + state.turnBoardId != otherBoardId)) { + return false; + } + state.phase = PuzzlePhase::Exited; + ++state.revision; + return true; +} + +inline bool applyPuzzleExitSignal(PuzzleState& current, + const PuzzleState& candidate, + uint32_t senderBoardId, + uint32_t recipientBoardId) { + if (!isValidPuzzle(current) || !isValidPuzzle(candidate) || + candidate.phase != PuzzlePhase::Exited || + current.gameId != candidate.gameId || + !samePuzzleSpec(puzzleSpec(current), puzzleSpec(candidate)) || + senderBoardId == 0 || recipientBoardId == 0 || + senderBoardId == recipientBoardId || + (current.turnBoardId != senderBoardId && + current.turnBoardId != recipientBoardId)) { + return false; + } + if (candidate.turnBoardId != senderBoardId && + candidate.turnBoardId != recipientBoardId) { + return false; + } + const PuzzleVersionOrder order = comparePuzzleVersion(current, candidate); + const bool equalConflict = + order == PuzzleVersionOrder::Same && !isSamePuzzle(current, candidate); + if (order == PuzzleVersionOrder::Newer || + (equalConflict && senderBoardId < recipientBoardId)) { + current = candidate; + return true; + } + if (current.phase == PuzzlePhase::Playing) { + return exitPuzzle(current, senderBoardId, recipientBoardId); + } + return true; +} + +inline bool isValidRemoteTransition(const PuzzleState& current, + const PuzzleState& incoming, + uint32_t senderBoardId, + uint32_t recipientBoardId) { + if (!isValidPuzzle(current) || !isValidPuzzle(incoming) || + current.phase != PuzzlePhase::Playing || + incoming.phase != PuzzlePhase::Playing || + !samePuzzleSpec(puzzleSpec(current), puzzleSpec(incoming)) || + incoming.gameId != current.gameId || + current.turnBoardId != senderBoardId || + incoming.turnBoardId != recipientBoardId || + incoming.revision != current.revision + 1) { + return false; + } + + const uint8_t oldBlank = findBlank(current); + const uint8_t newBlank = findBlank(incoming); + if (!arePuzzlePositionsAdjacent(current, oldBlank, newBlank) || + incoming.tiles[oldBlank] != current.tiles[newBlank]) { + return false; + } + + for (uint8_t i = 0; i < kPuzzleMaxTiles; ++i) { + if (i != oldBlank && i != newBlank && + incoming.tiles[i] != current.tiles[i]) { + return false; + } + } + return true; +} + +inline bool isValidPuzzleForParticipants(const PuzzleState& state, + uint32_t firstBoardId, + uint32_t secondBoardId) { + if (!isValidPuzzle(state) || firstBoardId == 0 || secondBoardId == 0 || + firstBoardId == secondBoardId) { + return false; + } + if (state.turnBoardId != firstBoardId && state.turnBoardId != secondBoardId) { + return false; + } + // EXIT is legal from either player regardless of whose move it is, so an + // Exited state deliberately breaks move-parity; its sender/turn validity is + // gated by applyPuzzleExitSignal. Only enforce parity while Playing. + if (state.phase == PuzzlePhase::Exited) { + return state.revision != 0; + } + const uint32_t host = firstBoardId < secondBoardId ? firstBoardId + : secondBoardId; + const uint32_t guest = firstBoardId < secondBoardId ? secondBoardId + : firstBoardId; + const uint32_t expectedTurn = state.revision % 2 == 0 ? host : guest; + return state.turnBoardId == expectedTurn; +} + +inline bool shouldAdoptFullState(const PuzzleState& current, + const PuzzleState& incoming, + uint32_t senderBoardId, + uint32_t recipientBoardId) { + if (!isValidPuzzleForParticipants(incoming, senderBoardId, + recipientBoardId) || + incoming.phase != PuzzlePhase::Playing || + comparePuzzleVersion(current, incoming) != PuzzleVersionOrder::Newer) { + return false; + } + const uint32_t host = senderBoardId < recipientBoardId ? senderBoardId + : recipientBoardId; + if (incoming.gameId != current.gameId) { + return incoming.gameId > current.gameId && senderBoardId == host; + } + if (current.phase != PuzzlePhase::Playing || + !samePuzzleSpec(puzzleSpec(current), puzzleSpec(incoming))) { + return false; + } + if (incoming.revision == current.revision + 1) { + return isValidRemoteTransition(current, incoming, senderBoardId, + recipientBoardId); + } + return senderBoardId == host; +} diff --git a/include/countdown_wire.h b/include/countdown_wire.h new file mode 100644 index 0000000..a811e2f --- /dev/null +++ b/include/countdown_wire.h @@ -0,0 +1,159 @@ +#pragma once + +#include +#include +#include + +// Wire-safe (POD, memcpy-able) synchronized state for the Countdown game +// mode. This mirrors MastermindState's role: the countdown_engine.hpp +// classes (NumbersRound/LettersRound/ConundrumRound/CommandResult) use +// std::string/std::vector and are NOT sent over ESP-NOW directly. This +// struct captures just enough of the match state (host/guest identity, +// phase, round number, scores) to keep both boards' Countdown screens in +// sync. Round-specific puzzle content (numbers tiles, letters, conundrum +// scramble) is intentionally NOT synchronized in this pass. + +enum class CountdownWirePhase : uint8_t { + Setup = 1, + InRound, + BetweenRounds, + MatchComplete, + Exited, +}; + +struct CountdownWireState { + uint32_t gameId; + uint32_t revision; + uint32_t hostBoardId; + uint32_t guestBoardId; + int32_t hostScore; + int32_t guestScore; + uint32_t roundNumber; + CountdownWirePhase phase; + uint8_t roundType; + uint8_t reserved[2]; +}; + +inline bool sameCountdownWireState(const CountdownWireState& first, + const CountdownWireState& second) { + return memcmp(&first, &second, sizeof(CountdownWireState)) == 0; +} + +inline bool isValidCountdownWireState(const CountdownWireState& state) { + if (state.gameId == 0 || state.hostBoardId == 0 || + state.guestBoardId == 0 || + state.hostBoardId == state.guestBoardId || + state.phase < CountdownWirePhase::Setup || + state.phase > CountdownWirePhase::Exited) { + return false; + } + for (uint8_t value : state.reserved) { + if (value != 0) { + return false; + } + } + return true; +} + +inline CountdownWireState makeCountdownMatch(uint32_t hostBoardId, + uint32_t guestBoardId, + uint32_t gameId) { + CountdownWireState state{}; + state.gameId = gameId; + state.hostBoardId = hostBoardId; + state.guestBoardId = guestBoardId; + state.roundNumber = 1; + state.phase = CountdownWirePhase::Setup; + return state; +} + +enum class CountdownVersionOrder : uint8_t { Older, Same, Newer }; + +inline CountdownVersionOrder compareCountdownVersion( + const CountdownWireState& current, const CountdownWireState& candidate) { + if (candidate.gameId != current.gameId) { + return candidate.gameId > current.gameId + ? CountdownVersionOrder::Newer + : CountdownVersionOrder::Older; + } + if (candidate.revision != current.revision) { + return candidate.revision > current.revision + ? CountdownVersionOrder::Newer + : CountdownVersionOrder::Older; + } + return CountdownVersionOrder::Same; +} + +inline uint32_t countdownStateDigest(const CountdownWireState& state) { + uint32_t digest = 2166136261u; + const uint8_t* bytes = reinterpret_cast(&state); + for (size_t index = 0; index < sizeof(state); ++index) { + digest ^= bytes[index]; + digest *= 16777619u; + } + return digest; +} + +inline bool shouldAdoptCountdownState(const CountdownWireState& current, + const CountdownWireState& candidate) { + return isValidCountdownWireState(candidate) && + compareCountdownVersion(current, candidate) == + CountdownVersionOrder::Newer; +} + +inline bool isCountdownDeliverySuperseded(const CountdownWireState& pending, + const CountdownWireState& observed) { + return pending.gameId == observed.gameId && + pending.revision < observed.revision; +} + +inline bool exitCountdownMatch(CountdownWireState& state, + uint32_t actorBoardId) { + if (state.phase == CountdownWirePhase::Exited || + (actorBoardId != state.hostBoardId && + actorBoardId != state.guestBoardId)) { + return false; + } + ++state.revision; + state.phase = CountdownWirePhase::Exited; + return true; +} + +inline bool applyCountdownExitSignal(CountdownWireState& current, + const CountdownWireState& candidate, + uint32_t senderBoardId) { + if (!isValidCountdownWireState(current) || + !isValidCountdownWireState(candidate) || + candidate.phase != CountdownWirePhase::Exited || + current.gameId != candidate.gameId || + current.hostBoardId != candidate.hostBoardId || + current.guestBoardId != candidate.guestBoardId) { + return false; + } + if (current.phase == CountdownWirePhase::Exited) { + return senderBoardId == current.hostBoardId || + senderBoardId == current.guestBoardId; + } + return exitCountdownMatch(current, senderBoardId); +} + +static_assert(sizeof(CountdownWireState) == 32, + "CountdownWireState wire format changed"); +static_assert(offsetof(CountdownWireState, gameId) == 0, + "CountdownWireState gameId offset changed"); +static_assert(offsetof(CountdownWireState, revision) == 4, + "CountdownWireState revision offset changed"); +static_assert(offsetof(CountdownWireState, hostBoardId) == 8, + "CountdownWireState hostBoardId offset changed"); +static_assert(offsetof(CountdownWireState, guestBoardId) == 12, + "CountdownWireState guestBoardId offset changed"); +static_assert(offsetof(CountdownWireState, hostScore) == 16, + "CountdownWireState hostScore offset changed"); +static_assert(offsetof(CountdownWireState, guestScore) == 20, + "CountdownWireState guestScore offset changed"); +static_assert(offsetof(CountdownWireState, roundNumber) == 24, + "CountdownWireState roundNumber offset changed"); +static_assert(offsetof(CountdownWireState, phase) == 28, + "CountdownWireState phase offset changed"); +static_assert(offsetof(CountdownWireState, roundType) == 29, + "CountdownWireState roundType offset changed"); diff --git a/include/protocol.h b/include/protocol.h index f83fe6e..808f626 100644 --- a/include/protocol.h +++ b/include/protocol.h @@ -5,6 +5,7 @@ #include "mastermind_logic.h" #include "puzzle_logic.h" +#include "countdown_wire.h" constexpr uint32_t kProtocolMagic = 0x46374359; constexpr uint8_t kProtocolVersion = 7; @@ -42,6 +43,10 @@ enum class MessageType : uint8_t { MastermindFullState, MastermindAck, MastermindRequestState, + CountdownState, + CountdownFullState, + CountdownAck, + CountdownRequestState, }; struct PacketHeader { @@ -121,6 +126,28 @@ struct MastermindStateRequestPacket { uint32_t stateDigest; }; +struct CountdownStatePacket { + PacketHeader header; + CountdownWireState state; +}; + +struct CountdownAckPacket { + PacketHeader header; + uint32_t targetBoardId; + uint32_t gameId; + uint32_t revision; + uint32_t stateDigest; + MessageType acknowledgedType; + uint8_t reserved[3]; +}; + +struct CountdownStateRequestPacket { + PacketHeader header; + uint32_t gameId; + uint32_t revision; + uint32_t stateDigest; +}; + static_assert(sizeof(PacketHeader) == 20, "PacketHeader wire format changed"); static_assert(sizeof(HeartbeatPacket) == 24, "HeartbeatPacket wire format changed"); @@ -138,3 +165,9 @@ static_assert(sizeof(GameAckPacket) == 40, "GameAckPacket wire format changed"); static_assert(sizeof(MastermindStateRequestPacket) == 32, "MastermindStateRequestPacket wire format changed"); +static_assert(sizeof(CountdownStatePacket) == 52, + "CountdownStatePacket wire format changed"); +static_assert(sizeof(CountdownAckPacket) == 40, + "CountdownAckPacket wire format changed"); +static_assert(sizeof(CountdownStateRequestPacket) == 32, + "CountdownStateRequestPacket wire format changed"); diff --git a/platformio.ini b/platformio.ini index 9664418..a5adcf4 100644 --- a/platformio.ini +++ b/platformio.ini @@ -12,7 +12,6 @@ lib_deps = https://github.com/PaulStoffregen/XPT2046_Touchscreen.git#v1.4 build_unflags = -std=gnu++11 -std=gnu++14 build_flags = - -std=gnu++17 -Iinclude -Icore/include -D USER_SETUP_LOADED=1 diff --git a/scripts/__pycache__/upload.cpython-314.pyc b/scripts/__pycache__/upload.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a960c0b2c1fd0d2572726f86bb7126c51904932b GIT binary patch literal 12356 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 bytes: + data = bytearray() + while len(data) < length: + chunk = connection.read(length - len(data)) + if not chunk: + raise TimeoutError( + f"Timed out after {len(data)} of {length} framebuffer bytes" + ) + data.extend(chunk) + return bytes(data) + + +def decode_rle(payload: bytes, pixel_count: int, depth: int) -> bytes: + rgb = bytearray() + offset = 0 + decoded_pixels = 0 + color_size = 1 if depth == 8 else 2 + + while offset < len(payload): + if offset + 1 + color_size > len(payload): + raise ValueError("Truncated RLE run") + run_length = payload[offset] + offset += 1 + if depth == 8: + value = payload[offset] + offset += 1 + red = ((value >> 5) & 0x07) * 255 // 7 + green = ((value >> 2) & 0x07) * 255 // 7 + blue = (value & 0x03) * 255 // 3 + else: + value = struct.unpack_from(">H", payload, offset)[0] + offset += 2 + red = ((value >> 11) & 0x1F) * 255 // 31 + green = ((value >> 5) & 0x3F) * 255 // 63 + blue = (value & 0x1F) * 255 // 31 + rgb.extend(bytes((red, green, blue)) * run_length) + decoded_pixels += run_length + + if decoded_pixels != pixel_count: + raise ValueError( + f"Decoded {decoded_pixels} pixels; expected {pixel_count}" + ) + return bytes(rgb) + + +def png_chunk(kind: bytes, data: bytes) -> bytes: + checksum = binascii.crc32(kind) + checksum = binascii.crc32(data, checksum) & 0xFFFFFFFF + return struct.pack(">I", len(data)) + kind + data + struct.pack(">I", checksum) + + +def write_png(path: Path, width: int, height: int, rgb: bytes) -> None: + stride = width * 3 + scanlines = b"".join( + b"\x00" + rgb[row * stride:(row + 1) * stride] + for row in range(height) + ) + signature = b"\x89PNG\r\n\x1a\n" + header = struct.pack(">IIBBBBB", width, height, 8, 2, 0, 0, 0) + path.write_bytes( + signature + + png_chunk(b"IHDR", header) + + png_chunk(b"IDAT", zlib.compress(scanlines, 9)) + + png_chunk(b"IEND", b"") + ) + + +def capture(port: str, output_dir: Path, timeout: float) -> tuple[Path, str]: + with serial.Serial(port, 115200, timeout=timeout, exclusive=True) as connection: + time.sleep(0.15) + connection.reset_input_buffer() + connection.write(b"S") + connection.flush() + + deadline = time.monotonic() + timeout + header = b"" + while time.monotonic() < deadline: + line = connection.readline() + if line.startswith(b"DCFRAME "): + header = line + break + if line.startswith(b"DCFRAME_ERROR "): + raise RuntimeError(line.decode("utf-8", errors="replace").strip()) + if not header: + raise TimeoutError(f"No DebugCanvas frame header received from {port}") + + parts = header.decode("ascii").strip().split(maxsplit=7) + if len(parts) != 8 or parts[1] != "1": + raise ValueError(f"Unsupported frame header: {header!r}") + width, height, depth, encoded_size = map(int, parts[2:6]) + screen_name = parts[6] if len(parts) == 7 else parts[7] + if depth not in (8, 16): + raise ValueError(f"Unsupported color depth: {depth}") + + payload = read_exact(connection, encoded_size) + rgb = decode_rle(payload, width * height, depth) + + output_path = output_dir / f"cyd-{Path(port).name}.png" + write_png(output_path, width, height, rgb) + return output_path, screen_name + + +def main() -> None: + parser = argparse.ArgumentParser() + parser.add_argument("ports", nargs="*", help="Serial ports; defaults to both CYDs") + parser.add_argument("--output-dir", type=Path, default=Path("/tmp")) + parser.add_argument("--timeout", type=float, default=10.0) + args = parser.parse_args() + + ports = args.ports or sorted( + glob.glob("/dev/ttyUSB*") + glob.glob("/dev/ttyACM*") + ) + if not ports: + raise SystemExit("No CYD serial ports found") + args.output_dir.mkdir(parents=True, exist_ok=True) + + with ThreadPoolExecutor(max_workers=len(ports)) as executor: + futures = [ + executor.submit(capture, port, args.output_dir, args.timeout) + for port in ports + ] + for port, future in zip(ports, futures): + path, screen_name = future.result() + print(f"{port}: {screen_name} -> {path}") + + +if __name__ == "__main__": + main() diff --git a/src/DebugCanvas.cpp b/src/DebugCanvas.cpp new file mode 100644 index 0000000..43f4a2b --- /dev/null +++ b/src/DebugCanvas.cpp @@ -0,0 +1,305 @@ +#include "DebugCanvas.h" + +#include + +DebugCanvas::DebugCanvas(TFT_eSPI& display) + : display_(display), + sprite_(&display) +{ +} + +bool DebugCanvas::begin(uint8_t preferredDepth) +{ + if (preferredDepth != 8 && preferredDepth != 16) { + preferredDepth = 16; + } + + ready_ = false; + colorDepth_ = 0; + width_ = display_.width(); + height_ = display_.height(); + + if (allocate(preferredDepth)) { + Serial.printf( + "[DebugCanvas] Allocated %dx%d %u-bit framebuffer.\n", + width_, height_, preferredDepth); + return true; + } + + if (preferredDepth == 8) { + Serial.printf( + "[DebugCanvas] ERROR: %dx%d 8-bit framebuffer allocation failed.\n", + width_, height_); + return false; + } + + Serial.printf( + "[DebugCanvas] %u-bit allocation failed; trying 8-bit.\n", + preferredDepth); + + if (allocate(8)) { + Serial.printf( + "[DebugCanvas] Allocated %dx%d 8-bit framebuffer.\n", + width_, height_); + return true; + } + + Serial.println( + "[DebugCanvas] ERROR: framebuffer allocation failed."); + + return false; +} + +bool DebugCanvas::allocate(uint8_t depth) +{ + sprite_.deleteSprite(); + sprite_.setColorDepth(depth); + + void* buffer = sprite_.createSprite(width_, height_); + + if (buffer == nullptr) { + return false; + } + + ready_ = true; + colorDepth_ = depth; + + sprite_.setSwapBytes(true); + sprite_.fillSprite(TFT_BLACK); + + return true; +} + +void DebugCanvas::clear(uint32_t color) +{ + if (!ready_) { + return; + } + + sprite_.fillSprite(color); +} + +void DebugCanvas::present() +{ + if (!ready_) { + return; + } + + const uint32_t startedAt = micros(); + + sprite_.pushSprite(0, 0); + + const uint32_t elapsed = micros() - startedAt; + + Serial.printf( + "[DebugCanvas] Presented '%s' in %lu us\n", + screenName_, + static_cast(elapsed)); +} + +TFT_eSprite& DebugCanvas::graphics() +{ + return sprite_; +} + +const TFT_eSprite& DebugCanvas::graphics() const +{ + return sprite_; +} + +bool DebugCanvas::isReady() const +{ + return ready_; +} + +int16_t DebugCanvas::width() const +{ + return width_; +} + +int16_t DebugCanvas::height() const +{ + return height_; +} + +uint8_t DebugCanvas::colorDepth() const +{ + return colorDepth_; +} + +size_t DebugCanvas::bufferSizeBytes() const +{ + if (!ready_) { + return 0; + } + + const size_t pixelCount = + static_cast(width_) * + static_cast(height_); + + if (colorDepth_ == 16) { + return pixelCount * sizeof(uint16_t); + } + + return pixelCount; +} + +void DebugCanvas::setScreenName(const char* screenName) +{ + screenName_ = + screenName != nullptr ? screenName : "Unknown"; +} + +const char* DebugCanvas::screenName() const +{ + return screenName_; +} + +void DebugCanvas::drawDebugOverlay(uint32_t frameNumber) +{ + if (!ready_) { + return; + } + + constexpr int overlayHeight = 18; + + sprite_.fillRect( + 0, + height_ - overlayHeight, + width_, + overlayHeight, + TFT_BLACK); + + sprite_.drawFastHLine( + 0, + height_ - overlayHeight, + width_, + TFT_DARKGREY); + + sprite_.setTextDatum(ML_DATUM); + sprite_.setTextColor(TFT_GREEN, TFT_BLACK); + + char status[80]; + + snprintf( + status, + sizeof(status), + "%s | F:%lu | H:%uK", + screenName_, + static_cast(frameNumber), + static_cast(ESP.getFreeHeap() / 1024)); + + sprite_.drawString( + status, + 3, + height_ - overlayHeight / 2, + 1); +} + +void DebugCanvas::printDebugInfo(Stream& output) const +{ + output.println("-------- DebugCanvas --------"); + output.printf("Ready: %s\n", ready_ ? "yes" : "no"); + output.printf("Screen: %s\n", screenName_); + output.printf( + "Dimensions: %d x %d\n", + width_, + height_); + output.printf( + "Color depth: %u-bit\n", + colorDepth_); + output.printf( + "Buffer: %u bytes\n", + static_cast(bufferSizeBytes())); + output.printf("Free heap: %u bytes\n", ESP.getFreeHeap()); + output.printf( + "Largest RAM: %u bytes\n", + heap_caps_get_largest_free_block(MALLOC_CAP_8BIT)); + output.println("-----------------------------"); +} + +size_t DebugCanvas::rleFrameSize(const void* pixels, size_t pixelCount) const +{ + if (pixels == nullptr || pixelCount == 0) { + return 0; + } + + size_t runCount = 1; + if (colorDepth_ == 8) { + const auto* values = static_cast(pixels); + uint8_t runLength = 1; + for (size_t index = 1; index < pixelCount; ++index) { + if (values[index] == values[index - 1] && runLength < UINT8_MAX) { + ++runLength; + } else { + ++runCount; + runLength = 1; + } + } + return runCount * 2; + } + + const auto* values = static_cast(pixels); + uint8_t runLength = 1; + for (size_t index = 1; index < pixelCount; ++index) { + if (values[index] == values[index - 1] && runLength < UINT8_MAX) { + ++runLength; + } else { + ++runCount; + runLength = 1; + } + } + return runCount * 3; +} + +bool DebugCanvas::writeRleFrame(Stream& output) +{ + if (!ready_) { + output.println("DCFRAME_ERROR canvas-not-ready"); + return false; + } + + const size_t pixelCount = + static_cast(width_) * static_cast(height_); + const void* pixels = sprite_.getPointer(); + const size_t encodedSize = rleFrameSize(pixels, pixelCount); + if (pixels == nullptr || encodedSize == 0) { + output.println("DCFRAME_ERROR framebuffer-unavailable"); + return false; + } + + output.printf("DCFRAME 1 %d %d %u %u %s\n", + width_, height_, colorDepth_, + static_cast(encodedSize), screenName_); + + if (colorDepth_ == 8) { + const auto* values = static_cast(pixels); + for (size_t index = 0; index < pixelCount;) { + uint8_t runLength = 1; + while (index + runLength < pixelCount && + values[index + runLength] == values[index] && + runLength < UINT8_MAX) { + ++runLength; + } + output.write(runLength); + output.write(values[index]); + index += runLength; + } + } else { + const auto* values = static_cast(pixels); + for (size_t index = 0; index < pixelCount;) { + uint8_t runLength = 1; + while (index + runLength < pixelCount && + values[index + runLength] == values[index] && + runLength < UINT8_MAX) { + ++runLength; + } + output.write(runLength); + output.write(reinterpret_cast(&values[index]), + sizeof(values[index])); + index += runLength; + } + } + output.flush(); + return true; +} diff --git a/src/main.cpp b/src/main.cpp index 1bf8322..f97d6c5 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -14,6 +14,7 @@ #include "generated_peer_keys.h" #include "protocol.h" #include "puzzle_logic.h" +#include "flip7/countdown_engine.hpp" namespace { constexpr uint8_t kBacklightPin = 21; @@ -72,7 +73,7 @@ portMUX_TYPE linkMux = portMUX_INITIALIZER_UNLOCKED; portMUX_TYPE gameMux = portMUX_INITIALIZER_UNLOCKED; SemaphoreHandle_t protocolMutex = nullptr; -enum class ScreenMode : uint8_t { Home, Puzzle, Complete, Mastermind }; +enum class ScreenMode : uint8_t { Home, Puzzle, Complete, Mastermind, Countdown }; struct LinkState { uint32_t sent; @@ -120,6 +121,22 @@ struct MastermindPendingAck { MessageType acknowledgedType; }; +struct CountdownPendingDelivery { + bool active; + MessageType type; + CountdownWireState state; + uint32_t lastSentMs; +}; + +struct CountdownPendingAck { + bool active; + uint32_t targetBoardId; + uint32_t gameId; + uint32_t revision; + uint32_t stateDigest; + MessageType acknowledgedType; +}; + LinkState linkState{}; PuzzleState puzzleState{}; PendingDelivery pendingDelivery{}; @@ -129,6 +146,10 @@ MastermindPendingDelivery mastermindPendingDelivery{}; MastermindPendingAck mastermindPendingAck{}; MastermindCode draftCode{{1, 1, 1, 1}}; MastermindCode lastSubmittedGuess{{1, 1, 1, 1}}; +flip7::countdown::CountdownMatchEngine countdownEngine(0, 0, 0); +CountdownWireState countdownState{}; +CountdownPendingDelivery countdownPendingDelivery{}; +CountdownPendingAck countdownPendingAck{}; ScreenMode screenMode = ScreenMode::Home; ActiveGameClock activeGame{kNoActiveGameEpoch, ActiveGameKind::Home}; uint32_t boardId = 0; @@ -161,6 +182,10 @@ bool mastermindStateReady = false; bool mastermindRequestStateSoon = false; bool mastermindReconciliationPending = false; bool sendMastermindFullStateSoon = false; +bool countdownStateReady = false; +bool countdownRequestStateSoon = false; +bool countdownReconciliationPending = false; +bool sendCountdownFullStateSoon = false; bool touchWasDown = false; bool lastOnline = false; uint8_t selectedPeg = 0; @@ -726,20 +751,55 @@ void renderMastermind(bool online, const MastermindState& state, display.drawString("GUESS", 257, 204, 4); } +void renderCountdown(bool online, const CountdownWireState& state) { + display.fillScreen(TFT_NAVY); + display.setTextDatum(MC_DATUM); + display.setTextColor(TFT_WHITE, TFT_NAVY); + display.drawString("COUNTDOWN MATCH", 145, 20, 4); + renderExitButton(online); + + display.setTextColor(TFT_YELLOW, TFT_NAVY); + display.drawString("SYNCHRONIZED NUM / LET / CON", 160, 55, 2); + + // Host & Guest Scores + display.fillRoundRect(20, 85, 130, 80, 8, TFT_DARKCYAN); + display.drawRoundRect(20, 85, 130, 80, 8, TFT_WHITE); + display.setTextColor(TFT_WHITE, TFT_DARKCYAN); + display.drawString("HOST", 85, 105, 2); + display.drawNumber(state.hostScore, 85, 135, 4); + + display.fillRoundRect(170, 85, 130, 80, 8, TFT_MAGENTA); + display.drawRoundRect(170, 85, 130, 80, 8, TFT_WHITE); + display.setTextColor(TFT_WHITE, TFT_MAGENTA); + display.drawString("GUEST", 235, 105, 2); + display.drawNumber(state.guestScore, 235, 135, 4); + + display.setTextColor(TFT_GREEN, TFT_NAVY); + if (boardId == state.hostBoardId) { + display.drawString("HOST PLAYER (IN CONTROL)", 160, 195, 2); + } else { + display.drawString("GUEST PLAYER (SYNCED)", 160, 195, 2); + } +} + void renderScreen() { PuzzleState game{}; MastermindState mastermind{}; + CountdownWireState countdown{}; ScreenMode mode; bool ready; bool mastermindReady; + bool countdownReady; bool deliveryPending; bool mastermindDeliveryPending; portENTER_CRITICAL(&gameMux); game = puzzleState; mastermind = mastermindState; + countdown = countdownState; mode = screenMode; ready = stateReady; mastermindReady = mastermindStateReady; + countdownReady = countdownStateReady; deliveryPending = pendingDelivery.active; mastermindDeliveryPending = mastermindPendingDelivery.active; portEXIT_CRITICAL(&gameMux); @@ -751,6 +811,8 @@ void renderScreen() { renderComplete(online, game); } else if (mode == ScreenMode::Mastermind && mastermindReady) { renderMastermind(online, mastermind, mastermindDeliveryPending); + } else if (mode == ScreenMode::Countdown && countdownReady) { + renderCountdown(online, countdown); } else if (mode == ScreenMode::Puzzle && ready) { renderPuzzle(online, game, deliveryPending); } else { @@ -781,6 +843,24 @@ void prepareMastermindDraft(const MastermindState& state) { } else { draftCode = MastermindCode{{1, 1, 1, 1}}; } +} + +bool validCountdownIdentity(const CountdownWireState& state) { + const uint32_t host = std::min(boardId, gamePeerBoardId); + const uint32_t guest = std::max(boardId, gamePeerBoardId); + return state.hostBoardId == host && state.guestBoardId == guest; +} + +void queueCountdownAck(uint32_t targetBoardId, + const CountdownWireState& state, + MessageType acknowledgedType) { + countdownPendingAck = CountdownPendingAck{ + true, targetBoardId, state.gameId, state.revision, + countdownStateDigest(state), acknowledgedType}; +} + +void prepareMastermindDraft() { + draftCode = MastermindCode{{1, 1, 1, 1}}; selectedPeg = 0; } @@ -1182,6 +1262,172 @@ void processMastermindStateRequest(const uint8_t* address, portEXIT_CRITICAL(&gameMux); } +void processCountdownStatePacket(const uint8_t* address, + const uint8_t* data, int length, + MessageType type) { + if (length != sizeof(CountdownStatePacket)) { + return; + } + CountdownStatePacket packet{}; + memcpy(&packet, data, sizeof(packet)); + if (!validHeader(packet.header, type) || + !matchesBoundPeer(address, packet.header.senderId) || + !isValidCountdownWireState(packet.state) || + !validCountdownIdentity(packet.state) || + !acceptPeerSequence(type, packet.header.sessionId, + packet.header.sequence)) { + return; + } + + bool accepted = false; + bool duplicate = false; + portENTER_CRITICAL(&gameMux); + const bool terminal = packet.state.phase == CountdownWirePhase::Exited; + if (!shouldAcceptGameState(activeGame, ActiveGameKind::Countdown, + packet.state.gameId, terminal)) { + portEXIT_CRITICAL(&gameMux); + return; + } + duplicate = countdownStateReady && + sameCountdownWireState(countdownState, packet.state); + const bool exitSignalApplied = + type == MessageType::CountdownState && terminal && + countdownStateReady && + applyCountdownExitSignal(countdownState, packet.state, + packet.header.senderId); + if (exitSignalApplied) { + countdownPendingDelivery.active = false; + countdownReconciliationPending = false; + accepted = true; + } else { + const CountdownVersionOrder order = + countdownStateReady + ? compareCountdownVersion(countdownState, packet.state) + : CountdownVersionOrder::Newer; + if (type == MessageType::CountdownFullState) { + const bool equalConflict = countdownStateReady && + order == CountdownVersionOrder::Same && !duplicate; + const bool peerIsAuthority = packet.header.senderId < boardId; + if (!countdownStateReady || + order == CountdownVersionOrder::Newer || + (equalConflict && peerIsAuthority)) { + countdownState = packet.state; + countdownStateReady = true; + countdownPendingDelivery.active = false; + countdownReconciliationPending = false; + accepted = true; + } else if (order == CountdownVersionOrder::Older || + (equalConflict && !peerIsAuthority)) { + sendCountdownFullStateSoon = true; + } + } else if (type == MessageType::CountdownState) { + if (!countdownStateReady || + (packet.state.gameId == countdownState.gameId && + packet.state.revision == countdownState.revision + 1)) { + countdownState = packet.state; + countdownStateReady = true; + accepted = true; + } else if (!duplicate) { + countdownRequestStateSoon = true; + countdownReconciliationPending = true; + } + } + } + if ((accepted || duplicate) && countdownPendingDelivery.active && + isCountdownDeliverySuperseded(countdownPendingDelivery.state, + packet.state)) { + countdownPendingDelivery.active = false; + } + if (accepted) { + pendingDelivery.active = false; + reconciliationPending = false; + requestStateSoon = false; + sendFullStateSoon = false; + sendMastermindFullStateSoon = false; + sendCountdownFullStateSoon = false; + activeGame = { + countdownState.gameId, + countdownState.phase == CountdownWirePhase::Exited + ? ActiveGameKind::Home + : ActiveGameKind::Countdown, + }; + screenMode = countdownState.phase == CountdownWirePhase::Exited + ? ScreenMode::Home + : ScreenMode::Countdown; + countdownReconciliationPending = false; + displayDirty = true; + } + if (accepted || duplicate) { + queueCountdownAck(packet.header.senderId, packet.state, type); + } + portEXIT_CRITICAL(&gameMux); +} + +void processCountdownAckPacket(const uint8_t* address, + const uint8_t* data, int length) { + if (length != sizeof(CountdownAckPacket)) { + return; + } + CountdownAckPacket packet{}; + memcpy(&packet, data, sizeof(packet)); + if (!validHeader(packet.header, MessageType::CountdownAck) || + packet.targetBoardId != boardId || + !matchesBoundPeer(address, packet.header.senderId) || + !acceptPeerSequence(MessageType::CountdownAck, + packet.header.sessionId, + packet.header.sequence)) { + return; + } + portENTER_CRITICAL(&gameMux); + if (countdownPendingDelivery.active && + countdownPendingDelivery.type == packet.acknowledgedType && + countdownPendingDelivery.state.gameId == packet.gameId && + countdownPendingDelivery.state.revision == packet.revision && + countdownStateDigest(countdownPendingDelivery.state) == + packet.stateDigest) { + countdownPendingDelivery.active = false; + displayDirty = true; + } + portEXIT_CRITICAL(&gameMux); +} + +void processCountdownStateRequest(const uint8_t* address, + const uint8_t* data, int length) { + if (length != sizeof(CountdownStateRequestPacket)) { + return; + } + CountdownStateRequestPacket packet{}; + memcpy(&packet, data, sizeof(packet)); + if (!validHeader(packet.header, MessageType::CountdownRequestState) || + !matchesBoundPeer(address, packet.header.senderId) || + !acceptPeerSequence(MessageType::CountdownRequestState, + packet.header.sessionId, + packet.header.sequence)) { + return; + } + portENTER_CRITICAL(&gameMux); + if (countdownStateReady && screenMode == ScreenMode::Countdown) { + const bool remoteOlder = packet.gameId < countdownState.gameId || + (packet.gameId == countdownState.gameId && + packet.revision < countdownState.revision); + const bool remoteNewer = packet.gameId > countdownState.gameId || + (packet.gameId == countdownState.gameId && + packet.revision > countdownState.revision); + const bool divergent = packet.gameId == countdownState.gameId && + packet.revision == countdownState.revision && + packet.stateDigest != countdownStateDigest(countdownState); + if (remoteOlder || (divergent && localIsHost())) { + sendCountdownFullStateSoon = true; + } else if (remoteNewer || (divergent && !localIsHost())) { + countdownRequestStateSoon = true; + countdownReconciliationPending = true; + } else { + countdownReconciliationPending = false; + } + } + portEXIT_CRITICAL(&gameMux); +} + void onPacketReceived(const uint8_t* address, const uint8_t* data, int length) { if (length < sizeof(PacketHeader)) { return; @@ -1217,6 +1463,13 @@ void onPacketReceived(const uint8_t* address, const uint8_t* data, int length) { processMastermindAckPacket(address, data, length); } else if (header.type == MessageType::MastermindRequestState) { processMastermindStateRequest(address, data, length); + } else if (header.type == MessageType::CountdownState || + header.type == MessageType::CountdownFullState) { + processCountdownStatePacket(address, data, length, header.type); + } else if (header.type == MessageType::CountdownAck) { + processCountdownAckPacket(address, data, length); + } else if (header.type == MessageType::CountdownRequestState) { + processCountdownStateRequest(address, data, length); } xSemaphoreGive(protocolMutex); } @@ -1513,6 +1766,30 @@ void startMastermind() { static_cast(started.gameId)); } +void startCountdown() { + uint32_t nextGameId = kNoActiveGameEpoch; + portENTER_CRITICAL(&gameMux); + if (screenMode != ScreenMode::Home && activeGame.kind != ActiveGameKind::Home) { + portEXIT_CRITICAL(&gameMux); + return; + } + nextGameId = nextActiveGameEpoch( + activeGame.epoch, stateReady ? puzzleState.gameId : 0, + mastermindStateReady ? mastermindState.gameId : 0); + if (nextGameId == kNoActiveGameEpoch) { + portEXIT_CRITICAL(&gameMux); + return; + } + countdownEngine.resetMatch(std::min(boardId, gamePeerBoardId), + std::max(boardId, gamePeerBoardId)); + activeGame = {nextGameId, ActiveGameKind::Countdown}; + screenMode = ScreenMode::Countdown; + displayDirty = true; + portEXIT_CRITICAL(&gameMux); + Serial.printf("COUNTDOWN started id=%lu\n", + static_cast(nextGameId)); +} + void handleMastermindEditorTouch(int16_t x, int16_t y, bool secretEntry) { if (secretEntry && y >= 65 && y <= 112) { @@ -1620,6 +1897,42 @@ void handleTouch() { const bool exitPressed = x >= kExitX && x < kExitX + kExitWidth && y >= kExitY && y < kExitY + kExitHeight; + if (mode == ScreenMode::Mastermind && exitPressed) { + MastermindState mastermindSnapshot{}; + portENTER_CRITICAL(&gameMux); + mastermindSnapshot = mastermindState; + portEXIT_CRITICAL(&gameMux); + if (mastermindStateReady) { + MastermindState exited = mastermindSnapshot; + if (exitMastermindMatch(exited, boardId)) { + commitMastermindState(exited, MessageType::MastermindState, + &mastermindSnapshot); + } + } + return; + } + if (mode == ScreenMode::Countdown && exitPressed) { + CountdownWireState countdownSnapshot{}; + portENTER_CRITICAL(&gameMux); + countdownSnapshot = countdownState; + portEXIT_CRITICAL(&gameMux); + if (countdownStateReady) { + CountdownWireState exited = countdownSnapshot; + if (exitCountdownMatch(exited, boardId)) { + portENTER_CRITICAL(&gameMux); + countdownState = exited; + countdownStateReady = true; + activeGame = {exited.gameId, ActiveGameKind::Home}; + screenMode = ScreenMode::Home; + countdownPendingDelivery = + CountdownPendingDelivery{true, MessageType::CountdownState, + exited, 0}; + displayDirty = true; + portEXIT_CRITICAL(&gameMux); + } + } + return; + } if ((mode == ScreenMode::Puzzle || mode == ScreenMode::Complete) && exitPressed) { PuzzleState exited{}; @@ -1654,12 +1967,7 @@ void handleTouch() { if (x >= 10 && x < 155) { startMastermind(); } else if (x >= 165 && x < 310) { - // Start Countdown game mode - portENTER_CRITICAL(&gameMux); - activeGame = {nextActiveGameEpoch(activeGame.epoch, 0, 0), ActiveGameKind::Countdown}; - screenMode = ScreenMode::Home; // Ready - displayDirty = true; - portEXIT_CRITICAL(&gameMux); + startCountdown(); } } } diff --git a/test/test_debug_canvas/test_debug_canvas.cpp b/test/test_debug_canvas/test_debug_canvas.cpp new file mode 100644 index 0000000..aa8e6d2 --- /dev/null +++ b/test/test_debug_canvas/test_debug_canvas.cpp @@ -0,0 +1,20 @@ +#include + +#include "DebugCanvas.h" + +void test_debug_canvas_dimensions() +{ + TEST_ASSERT_EQUAL_INT16(240, DebugCanvas::WIDTH); + TEST_ASSERT_EQUAL_INT16(320, DebugCanvas::HEIGHT); +} + +void setup() +{ + UNITY_BEGIN(); + RUN_TEST(test_debug_canvas_dimensions); + UNITY_END(); +} + +void loop() +{ +} From ecb9832ed72e35a092c3e9e38bd1f7fff7b0f961 Mon Sep 17 00:00:00 2001 From: Christopher Mayfield Date: Sun, 2 Aug 2026 15:32:00 -0500 Subject: [PATCH 05/12] fix(countdown): fix peer sequence tracking bounds check blocking Countdown sync The LinkState peerSequences/sequenceSeen arrays were sized 11 and acceptPeerSequence() rejected any MessageType index >= 11, but Countdown message types (CountdownState=11, CountdownFullState=12, CountdownAck=13, CountdownRequestState=14) all landed at or above that cutoff. Every Countdown packet the guest received failed acceptPeerSequence() and was silently dropped, so pressing COUNTDOWN only updated the host's local screen (the flash bug) and the guest board never received match state (stuck on WAITING FOR HOST). Fix: bump both arrays to size 15 and the bounds check to >= 15, covering all 14 defined MessageType values. Also fills in startCountdown()'s missing wire-state population/send (it previously flipped screenMode locally without populating countdownState or setting countdownStateReady, and serviceProtocol() never flushed countdownPendingDelivery to ESP-NOW at all -- both gaps are fixed here alongside the sequence-bounds root cause). Verified on physical hardware: built, flashed both CYD boards, confirmed COUNTDOWN now syncs correctly on both host and guest. Also verified with an ad-hoc /tmp harness isolating acceptPeerSequence() logic, confirming all four Countdown message types were rejected under the old bound and accepted under the fix. --- src/main.cpp | 119 +++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 115 insertions(+), 4 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index f97d6c5..6b06478 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -84,8 +84,8 @@ struct LinkState { uint32_t peerSessionId; uint32_t retiredSessions[kRetiredSessionCapacity]; size_t retiredSessionCount; - uint32_t peerSequences[11]; - bool sequenceSeen[11]; + uint32_t peerSequences[15]; + bool sequenceSeen[15]; uint8_t peerAddress[6]; }; @@ -291,7 +291,7 @@ bool matchesBoundPeer(const uint8_t* address, uint32_t senderId) { bool acceptPeerSequence(MessageType type, uint32_t sessionId, uint32_t sequence) { const uint8_t index = static_cast(type); - if (index >= 11) { + if (index >= 15) { return false; } portENTER_CRITICAL(&linkMux); @@ -1622,6 +1622,61 @@ void sendMastermindStateRequest() { xSemaphoreGive(protocolMutex); } +void sendCountdownStateIfCurrent(MessageType type, + const CountdownWireState& state) { + CountdownStatePacket packet{makeHeader(type), state}; + if (protocolMutex == nullptr || + xSemaphoreTake(protocolMutex, portMAX_DELAY) != pdTRUE) { + return; + } + portENTER_CRITICAL(&gameMux); + const bool snapshotMatches = + countdownStateReady && sameCountdownWireState(countdownState, state); + const bool terminalDelivery = + type == MessageType::CountdownState && + state.phase == CountdownWirePhase::Exited; + const bool shouldSend = + shouldSendActiveGameState(activeGame, ActiveGameKind::Countdown, + state.gameId, snapshotMatches, + terminalDelivery); + portEXIT_CRITICAL(&gameMux); + if (shouldSend) { + esp_now_send(expectedPeerAddress, reinterpret_cast(&packet), + sizeof(packet)); + } + xSemaphoreGive(protocolMutex); +} + +void sendCountdownAck(const CountdownPendingAck& ack) { + CountdownAckPacket packet{makeHeader(MessageType::CountdownAck), + ack.targetBoardId, + ack.gameId, + ack.revision, + ack.stateDigest, + ack.acknowledgedType, + {0, 0, 0}}; + esp_now_send(expectedPeerAddress, reinterpret_cast(&packet), + sizeof(packet)); +} + +void sendCountdownStateRequest() { + uint32_t gameId = 0; + uint32_t revision = 0; + uint32_t digest = 0; + portENTER_CRITICAL(&gameMux); + if (countdownStateReady) { + gameId = countdownState.gameId; + revision = countdownState.revision; + digest = countdownStateDigest(countdownState); + } + portEXIT_CRITICAL(&gameMux); + CountdownStateRequestPacket packet{ + makeHeader(MessageType::CountdownRequestState), gameId, revision, + digest}; + esp_now_send(expectedPeerAddress, reinterpret_cast(&packet), + sizeof(packet)); +} + void refreshPeerIdentity() { uint32_t peerId = 0; portENTER_CRITICAL(&linkMux); @@ -1782,8 +1837,16 @@ void startCountdown() { } countdownEngine.resetMatch(std::min(boardId, gamePeerBoardId), std::max(boardId, gamePeerBoardId)); + const CountdownWireState started = makeCountdownMatch( + std::min(boardId, gamePeerBoardId), + std::max(boardId, gamePeerBoardId), nextGameId); + countdownState = started; + countdownStateReady = true; activeGame = {nextGameId, ActiveGameKind::Countdown}; screenMode = ScreenMode::Countdown; + countdownPendingDelivery = + CountdownPendingDelivery{true, MessageType::CountdownFullState, + started, 0}; displayDirty = true; portEXIT_CRITICAL(&gameMux); Serial.printf("COUNTDOWN started id=%lu\n", @@ -2036,6 +2099,9 @@ void serviceProtocol(uint32_t now) { MastermindPendingDelivery mastermindDelivery{}; MastermindPendingAck mastermindAck{}; MastermindState mastermindFullState{}; + CountdownPendingDelivery countdownDelivery{}; + CountdownPendingAck countdownAck{}; + CountdownWireState countdownFullState{}; bool sendDelivery = false; bool sendAckNow = false; bool sendFullNow = false; @@ -2044,10 +2110,16 @@ void serviceProtocol(uint32_t now) { bool sendMastermindAckNow = false; bool sendMastermindFullNow = false; bool requestMastermindNow = false; + bool sendCountdownDeliveryNow = false; + bool sendCountdownAckNow = false; + bool sendCountdownFullNow = false; + bool requestCountdownNow = false; portENTER_CRITICAL(&gameMux); const bool puzzleActive = activeGame.kind == ActiveGameKind::Puzzle; const bool mastermindActive = activeGame.kind == ActiveGameKind::Mastermind; + const bool countdownActive = + activeGame.kind == ActiveGameKind::Countdown; const bool discoveringGame = activeGame.kind == ActiveGameKind::Home; if (pendingDelivery.active && now - pendingDelivery.lastSentMs >= kDeliveryRetryMs) { @@ -2097,7 +2169,32 @@ void serviceProtocol(uint32_t now) { mastermindRequestStateSoon = false; requestMastermindNow = true; } - if (requestNow || requestMastermindNow) { + if (countdownPendingDelivery.active && + now - countdownPendingDelivery.lastSentMs >= kDeliveryRetryMs) { + countdownPendingDelivery.lastSentMs = now; + countdownDelivery = countdownPendingDelivery; + sendCountdownDeliveryNow = true; + } + if (countdownPendingAck.active) { + countdownAck = countdownPendingAck; + countdownPendingAck.active = false; + sendCountdownAckNow = true; + } + if (sendCountdownFullStateSoon && countdownStateReady && + countdownActive) { + sendCountdownFullStateSoon = false; + countdownFullState = countdownState; + sendCountdownFullNow = true; + } + if ((countdownActive || discoveringGame) && + (countdownRequestStateSoon || + (gamePeerBoardId != 0 && + (!countdownStateReady || countdownReconciliationPending) && + now - lastStateRequestMs >= kStateRequestIntervalMs))) { + countdownRequestStateSoon = false; + requestCountdownNow = true; + } + if (requestNow || requestMastermindNow || requestCountdownNow) { lastStateRequestMs = now; } portEXIT_CRITICAL(&gameMux); @@ -2128,6 +2225,20 @@ void serviceProtocol(uint32_t now) { if (requestMastermindNow) { sendMastermindStateRequest(); } + if (sendCountdownDeliveryNow) { + sendCountdownStateIfCurrent(countdownDelivery.type, + countdownDelivery.state); + } + if (sendCountdownAckNow) { + sendCountdownAck(countdownAck); + } + if (sendCountdownFullNow) { + sendCountdownStateIfCurrent(MessageType::CountdownFullState, + countdownFullState); + } + if (requestCountdownNow) { + sendCountdownStateRequest(); + } } void advanceMastermindRoundIfReady(uint32_t now) { From ad28b457c8fb19a382be7f0040000101003659e9 Mon Sep 17 00:00:00 2001 From: Christopher Mayfield Date: Sun, 2 Aug 2026 15:45:22 -0500 Subject: [PATCH 06/12] feat(countdown): add host-only NEW ROUND button with game-mode picker The Countdown match screen now supports starting a new round: - Host presses NEW ROUND on the match screen (button hidden for guest); advanceCountdownRound() bumps roundNumber/revision and moves the synced CountdownWireState phase to BetweenRounds. - While BetweenRounds, renderCountdown() shows a CHOOSE NEXT ROUND screen with NUMBERS/LETTERS/CONUNDRUM buttons (host only); the guest sees a WAITING FOR HOST TO PICK message and stays in sync via the existing CountdownState wire messages. - Host tapping a round type calls selectCountdownRoundType(), which validates phase == BetweenRounds and actor == host, sets roundType, and flips phase back to InRound; both boards return to the normal match view with the round counter incremented. This only synchronizes match-level state (phase/roundNumber/roundType) consistent with the existing Countdown wire-state scope -- round-specific puzzle content (numbers tiles, letters, conundrum scramble) generation is a separate follow-up, same as noted in the original Countdown sync commit. Verified: pio run build succeeds, both physical CYD boards flashed, user confirmed NEW ROUND -> mode picker -> round-type selection syncs correctly between host and guest. --- include/countdown_wire.h | 26 +++++++ src/main.cpp | 150 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 175 insertions(+), 1 deletion(-) diff --git a/include/countdown_wire.h b/include/countdown_wire.h index a811e2f..2f5064c 100644 --- a/include/countdown_wire.h +++ b/include/countdown_wire.h @@ -119,6 +119,32 @@ inline bool exitCountdownMatch(CountdownWireState& state, return true; } +inline bool advanceCountdownRound(CountdownWireState& state, + uint32_t actorBoardId) { + if (state.phase == CountdownWirePhase::Exited || + actorBoardId != state.hostBoardId || state.roundNumber == UINT32_MAX) { + return false; + } + ++state.roundNumber; + ++state.revision; + state.phase = CountdownWirePhase::BetweenRounds; + return true; +} + +inline bool selectCountdownRoundType(CountdownWireState& state, + uint32_t actorBoardId, + uint8_t roundType) { + if (state.phase != CountdownWirePhase::BetweenRounds || + actorBoardId != state.hostBoardId || roundType < 1 || + roundType > 3) { + return false; + } + state.roundType = roundType; + state.phase = CountdownWirePhase::InRound; + ++state.revision; + return true; +} + inline bool applyCountdownExitSignal(CountdownWireState& current, const CountdownWireState& candidate, uint32_t senderBoardId) { diff --git a/src/main.cpp b/src/main.cpp index 6b06478..56c721e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -53,6 +53,16 @@ constexpr int16_t kExitX = 271; constexpr int16_t kExitY = 3; constexpr int16_t kExitWidth = 46; constexpr int16_t kExitHeight = 26; +constexpr int16_t kCountdownNewRoundX = 110; +constexpr int16_t kCountdownNewRoundY = 175; +constexpr int16_t kCountdownNewRoundWidth = 100; +constexpr int16_t kCountdownNewRoundHeight = 34; +constexpr int16_t kCountdownRoundTypeY = 90; +constexpr int16_t kCountdownRoundTypeWidth = 90; +constexpr int16_t kCountdownRoundTypeHeight = 60; +constexpr int16_t kCountdownNumbersX = 5; +constexpr int16_t kCountdownLettersX = 115; +constexpr int16_t kCountdownConundrumX = 225; struct PuzzleLayout { int16_t x; @@ -755,9 +765,68 @@ void renderCountdown(bool online, const CountdownWireState& state) { display.fillScreen(TFT_NAVY); display.setTextDatum(MC_DATUM); display.setTextColor(TFT_WHITE, TFT_NAVY); - display.drawString("COUNTDOWN MATCH", 145, 20, 4); renderExitButton(online); + if (state.phase == CountdownWirePhase::BetweenRounds) { + display.drawString("CHOOSE NEXT ROUND", 145, 20, 4); + display.setTextColor(TFT_YELLOW, TFT_NAVY); + display.drawString(boardId == state.hostBoardId + ? "HOST: PICK A ROUND TYPE" + : "WAITING FOR HOST TO PICK", + 160, 55, 2); + + if (boardId == state.hostBoardId) { + display.fillRoundRect(kCountdownNumbersX, kCountdownRoundTypeY, + kCountdownRoundTypeWidth, + kCountdownRoundTypeHeight, 8, TFT_DARKCYAN); + display.drawRoundRect(kCountdownNumbersX, kCountdownRoundTypeY, + kCountdownRoundTypeWidth, + kCountdownRoundTypeHeight, 8, TFT_WHITE); + display.setTextColor(TFT_WHITE, TFT_DARKCYAN); + display.drawString("NUMBERS", + kCountdownNumbersX + kCountdownRoundTypeWidth / 2, + kCountdownRoundTypeY + + kCountdownRoundTypeHeight / 2, + 2); + + display.fillRoundRect(kCountdownLettersX, kCountdownRoundTypeY, + kCountdownRoundTypeWidth, + kCountdownRoundTypeHeight, 8, TFT_MAGENTA); + display.drawRoundRect(kCountdownLettersX, kCountdownRoundTypeY, + kCountdownRoundTypeWidth, + kCountdownRoundTypeHeight, 8, TFT_WHITE); + display.setTextColor(TFT_WHITE, TFT_MAGENTA); + display.drawString("LETTERS", + kCountdownLettersX + kCountdownRoundTypeWidth / 2, + kCountdownRoundTypeY + + kCountdownRoundTypeHeight / 2, + 2); + + display.fillRoundRect(kCountdownConundrumX, kCountdownRoundTypeY, + kCountdownRoundTypeWidth, + kCountdownRoundTypeHeight, 8, TFT_DARKGREEN); + display.drawRoundRect(kCountdownConundrumX, kCountdownRoundTypeY, + kCountdownRoundTypeWidth, + kCountdownRoundTypeHeight, 8, TFT_WHITE); + display.setTextColor(TFT_WHITE, TFT_DARKGREEN); + display.drawString("CONUNDRUM", + kCountdownConundrumX + + kCountdownRoundTypeWidth / 2, + kCountdownRoundTypeY + + kCountdownRoundTypeHeight / 2, + 2); + } + + display.setTextColor(TFT_WHITE, TFT_NAVY); + char roundLabel[32]; + snprintf(roundLabel, sizeof(roundLabel), "ROUND %lu", + static_cast(state.roundNumber)); + display.drawString(roundLabel, 160, 230, 2); + return; + } + + display.drawString("COUNTDOWN MATCH", 145, 20, 4); + display.setTextColor(TFT_YELLOW, TFT_NAVY); display.drawString("SYNCHRONIZED NUM / LET / CON", 160, 55, 2); @@ -780,6 +849,21 @@ void renderCountdown(bool online, const CountdownWireState& state) { } else { display.drawString("GUEST PLAYER (SYNCED)", 160, 195, 2); } + + if (boardId == state.hostBoardId && + state.phase != CountdownWirePhase::Exited) { + display.fillRoundRect(kCountdownNewRoundX, kCountdownNewRoundY, + kCountdownNewRoundWidth, + kCountdownNewRoundHeight, 7, TFT_DARKGREEN); + display.drawRoundRect(kCountdownNewRoundX, kCountdownNewRoundY, + kCountdownNewRoundWidth, + kCountdownNewRoundHeight, 7, TFT_WHITE); + display.setTextColor(TFT_WHITE, TFT_DARKGREEN); + display.drawString("NEW ROUND", + kCountdownNewRoundX + kCountdownNewRoundWidth / 2, + kCountdownNewRoundY + kCountdownNewRoundHeight / 2, + 2); + } } void renderScreen() { @@ -1996,6 +2080,70 @@ void handleTouch() { } return; } + if (mode == ScreenMode::Countdown && + x >= kCountdownNewRoundX && + x < kCountdownNewRoundX + kCountdownNewRoundWidth && + y >= kCountdownNewRoundY && + y < kCountdownNewRoundY + kCountdownNewRoundHeight) { + CountdownWireState countdownSnapshot{}; + portENTER_CRITICAL(&gameMux); + countdownSnapshot = countdownState; + portEXIT_CRITICAL(&gameMux); + if (countdownStateReady && boardId == countdownSnapshot.hostBoardId && + countdownSnapshot.phase != CountdownWirePhase::BetweenRounds) { + CountdownWireState advanced = countdownSnapshot; + if (advanceCountdownRound(advanced, boardId)) { + portENTER_CRITICAL(&gameMux); + if (sameCountdownWireState(countdownState, countdownSnapshot)) { + countdownState = advanced; + countdownStateReady = true; + countdownPendingDelivery = CountdownPendingDelivery{ + true, MessageType::CountdownState, advanced, 0}; + displayDirty = true; + } + portEXIT_CRITICAL(&gameMux); + } + } + return; + } + if (mode == ScreenMode::Countdown && + y >= kCountdownRoundTypeY && + y < kCountdownRoundTypeY + kCountdownRoundTypeHeight) { + uint8_t roundType = 0; + if (x >= kCountdownNumbersX && + x < kCountdownNumbersX + kCountdownRoundTypeWidth) { + roundType = 1; // Numbers + } else if (x >= kCountdownLettersX && + x < kCountdownLettersX + kCountdownRoundTypeWidth) { + roundType = 2; // Letters + } else if (x >= kCountdownConundrumX && + x < kCountdownConundrumX + kCountdownRoundTypeWidth) { + roundType = 3; // Conundrum + } + if (roundType != 0) { + CountdownWireState countdownSnapshot{}; + portENTER_CRITICAL(&gameMux); + countdownSnapshot = countdownState; + portEXIT_CRITICAL(&gameMux); + if (countdownStateReady && + boardId == countdownSnapshot.hostBoardId) { + CountdownWireState selected = countdownSnapshot; + if (selectCountdownRoundType(selected, boardId, roundType)) { + portENTER_CRITICAL(&gameMux); + if (sameCountdownWireState(countdownState, + countdownSnapshot)) { + countdownState = selected; + countdownStateReady = true; + countdownPendingDelivery = CountdownPendingDelivery{ + true, MessageType::CountdownState, selected, 0}; + displayDirty = true; + } + portEXIT_CRITICAL(&gameMux); + } + } + } + return; + } if ((mode == ScreenMode::Puzzle || mode == ScreenMode::Complete) && exitPressed) { PuzzleState exited{}; From 7e71148be3f5ccb66bc6376e441ce676061c7ad8 Mon Sep 17 00:00:00 2001 From: Christopher Mayfield Date: Wed, 5 Aug 2026 09:02:42 -0500 Subject: [PATCH 07/12] feat(countdown): complete UI implementation and engine extensions - countdown_engine.hpp/cpp: Add CommandType/EventType enums for all round sub-phases, FixedVector for heap-free ESP32, NumberWorkspace/LetterWorkspace/ConundrumWorkspace, CalculationStep, PuzzleDescriptor, deterministic RNG, dictionary verification - countdown_wire.h: Add CountdownRoundSubPhase (21 phases), chooserBoardId, hostTerm, roundSubPhase, HostAuthority, advanceCountdownSubPhase, applyCountdownHostTransfer - protocol.h: Add CountdownAction and CountdownPuzzleDescriptor message types, CountdownFullStatePacket with letters array, static asserts for new packets - main.cpp: Full Countdown UI implementation (Intro, NumPicking, NumThinking, NumClaimEntry, NumClaimReveal, NumPresentPlayerA/B, NumResult, LetPicking, LetThinking, LetClaimEntry, LetClaimReveal, LetPresentPlayerA/B, LetResult, ConReady, ConActive, ConResult, ConResultNoWinner), touch handlers, timer arc, tile strip, numpad, operation buttons, large-count picker, FORCE-END button - countdown_test.cpp: Extended tests for new engine types and projections --- core/include/flip7/countdown_engine.hpp | 405 ++++- core/src/countdown_engine.cpp | 650 ++++++- core/tests/countdown_test.cpp | 327 +++- include/countdown_wire.h | 165 +- include/protocol.h | 39 +- src/main.cpp | 2063 +++++++++++++++++++++-- 6 files changed, 3278 insertions(+), 371 deletions(-) diff --git a/core/include/flip7/countdown_engine.hpp b/core/include/flip7/countdown_engine.hpp index a4fecad..922d2a2 100644 --- a/core/include/flip7/countdown_engine.hpp +++ b/core/include/flip7/countdown_engine.hpp @@ -13,62 +13,144 @@ #include #include #include +#include namespace flip7 { namespace countdown { enum class RoundType : uint8_t { - Numbers = 1, - Letters = 2, + Numbers = 1, + Letters = 2, Conundrum = 3, }; +enum class ArithmeticOp : uint8_t { + Add = 1, + Subtract = 2, + Multiply = 3, + Divide = 4, +}; + enum class CommandType : uint8_t { - SelectNumbersTarget = 1, - SelectNumbersTile = 2, - SubmitNumbersSolution = 3, - PickLetterCategory = 4, // 0 = Vowel, 1 = Consonant - SubmitLettersWord = 5, - BuzzConundrum = 6, - SubmitConundrumSolution = 7, + // Numbers round + NumSelectLargeCount = 1, // chooser picks 0-4 large numbers + NumSubmitClaim = 2, // player submits private achieved value + NumPresentStepRequest = 3, // presenter requests one arithmetic step + NumPresentBankResult = 4, // presenter banks current value as available + NumPresentUndo = 5, // presenter undoes last step + NumPresentComplete = 6, // presenter ends presentation + // Letters round + LetPickVowel = 7, // chooser draws a vowel + LetPickConsonant = 8, // chooser draws a consonant + LetSubmitClaim = 9, // player submits claimed word length (1-9) + LetPresentTileTap = 10, // presenter taps a tile by index (0-8) + LetPresentBackspace = 11, // presenter removes last tile + LetPresentComplete = 12, // presenter submits final word + LetVerifyWord = 13, // observer votes: payload[0] 1=accepted 0=rejected + // Conundrum round + ConSubmitAttempt = 14, // player submits full 9-letter answer }; enum class EventType : uint8_t { - RoundStarted = 1, - TargetSet = 2, - TileSelected = 3, - NumbersSolutionSubmitted = 4, - LetterDrawn = 5, - WordSubmitted = 6, - ConundrumBuzzed = 7, - ConundrumSolved = 8, - RoundEnded = 9, + RoundStarted = 1, + NumbersPuzzleReady = 2, + NumClaimReceived = 3, + NumClaimsRevealed = 4, + NumStepAccepted = 5, + NumStepRejected = 6, + NumPresentationComplete = 7, + LetterDrawn = 8, + LetterSelectionComplete = 9, + LetClaimReceived = 10, + LetClaimsRevealed = 11, + LetWordAccepted = 12, + LetWordRejected = 13, + LetPresentationComplete = 14, + ConAttemptCorrect = 15, + ConAttemptIncorrect = 16, + ConTimeout = 17, + RoundEnded = 18, }; enum class MatchPhase : uint8_t { - Setup = 0, - InRound = 1, + Setup = 0, + InRound = 1, BetweenRounds = 2, MatchComplete = 3, }; -struct PlayerState { - uint32_t boardId{0}; - int32_t score{0}; - bool isHost{false}; - bool hasBuzzed{false}; +// Fixed-capacity vector — no heap growth after construction; safe on ESP32. +template +class FixedVector { +public: + FixedVector() : size_(0) {} + bool push_back(const T& v) { + if (size_ >= N) return false; + data_[size_++] = v; + return true; + } + bool pop_back() { + if (size_ == 0) return false; + --size_; + return true; + } + T& back() { return data_[size_ - 1]; } + const T& back() const { return data_[size_ - 1]; } + T& operator[](size_t i) { return data_[i]; } + const T& operator[](size_t i) const { return data_[i]; } + size_t size() const { return size_; } + bool empty() const { return size_ == 0; } + bool full() const { return size_ >= N; } + void clear() { size_ = 0; } + T* begin() { return data_; } + T* end() { return data_ + size_; } + const T* begin() const { return data_; } + const T* end() const { return data_ + size_; } +private: + T data_[N]; + size_t size_; +}; + +struct CalculationStep { + int32_t operandA{0}; + ArithmeticOp op{ArithmeticOp::Add}; + int32_t operandB{0}; + int32_t result{0}; }; -struct RoundDescriptor { - std::string typeId; - std::string version; - RoundType roundType; +struct NumberWorkspace { + std::optional currentValue; + FixedVector availableValues; + FixedVector history; + + void reset(const std::vector& tiles) { + currentValue.reset(); + availableValues.clear(); + for (auto t : tiles) availableValues.push_back(t); + history.clear(); + } +}; + +struct NumberPlayerState { + bool claimSubmitted{false}; + int32_t claimedValue{0}; + bool presentationComplete{false}; + bool presentationValid{false}; + int32_t demonstratedValue{0}; + FixedVector steps; + NumberWorkspace workspace; +}; + +struct PlayerState { + uint32_t boardId{0}; + int32_t score{0}; + bool isHost{false}; }; struct RoundResult { - RoundType roundType; - uint32_t winnerBoardId{0}; - bool isTie{false}; + RoundType roundType{RoundType::Numbers}; + uint32_t winnerBoardId{0}; + bool isTie{false}; std::map scoreAwards; }; @@ -78,7 +160,7 @@ struct CommandContext { }; struct CommandResult { - bool accepted{false}; + bool accepted{false}; std::string errorCode; std::string errorMessage; @@ -90,13 +172,40 @@ struct CommandResult { }; struct EventEnvelope { - uint32_t sequenceNumber{0}; - uint32_t roundId{0}; + uint32_t sequenceNumber{0}; + uint32_t roundId{0}; EventType eventType; - uint32_t senderBoardId{0}; + uint32_t senderBoardId{0}; std::string payload; }; +// Versioned puzzle descriptor — sent by host; guest verifies checksum before use. +struct PuzzleDescriptor { + uint32_t roundSeed{0}; + uint16_t rulesetVersion{0}; + uint16_t contentPackVersion{0}; + uint32_t puzzleChecksum{0}; +}; + +// Display projections — plain POD for safe copying across tasks. +struct NumbersRoundProjection { + uint32_t target{0}; + int32_t tiles[6]{0}; + uint8_t tileCount{0}; +}; + +struct LettersRoundProjection { + char letters[9]{0}; + uint8_t letterCount{0}; +}; + +struct ConundrumRoundProjection { + char scramble[10]{0}; + char hint[48]{0}; + bool solved{false}; + uint32_t winnerBoardId{0}; +}; + // Abstract Random Source Interface for Deterministic Testing class IRandomSource { public: @@ -121,8 +230,27 @@ class SeededRandomSource : public IRandomSource { uint32_t state_; }; +// Returns a deterministic SeededRandomSource for a given round. +// Both boards call this with the same inputs to generate identical puzzle content. +// seed = gameId ^ (roundNumber * 2654435761u) — Knuth multiplicative hash +inline SeededRandomSource makeRoundRandom(uint32_t gameId, uint32_t roundNumber) { + return SeededRandomSource(gameId ^ (roundNumber * 2654435761u)); +} + +// Round plugin interface — platform-neutral; engine holds one active round. +class IRound { +public: + virtual ~IRound() = default; + virtual RoundType roundType() const = 0; + virtual CommandResult applyCommand(const CommandContext& ctx, + CommandType type, + const std::vector& payload) = 0; + virtual std::optional tryFinalize( + const std::vector& playerBoardIds) = 0; +}; + // Numbers Round State & Logic -class NumbersRound { +class NumbersRound : public IRound { public: NumbersRound(uint32_t target, const std::vector& tiles); @@ -130,131 +258,224 @@ class NumbersRound { uint32_t target() const { return target_; } const std::vector& tiles() const { return tiles_; } - - // Evaluate a solution given as result number + + // Score table: exact=10, within 5=7, within 10=5, else 0. static int32_t calculateScore(uint32_t target, uint32_t achieved); - CommandResult submitSolution(uint32_t boardId, uint32_t resultValue); - - bool isComplete() const { return complete_; } - uint32_t winnerBoardId() const { return winnerBoardId_; } - const std::map& submissions() const { return submissions_; } + // Private claim — stored by boardId; not revealed until both submitted. + CommandResult submitClaim(uint32_t boardId, int32_t claimedValue); + bool bothClaimsSubmitted() const; + int32_t claimFor(uint32_t boardId) const; + + // Presentation step — host validates; ContinueWithResult leaves result as + // currentValue; BankResult returns it to the available pool for branching. + CommandResult applyStep(uint32_t boardId, ArithmeticOp op, + int32_t operandA, int32_t operandB, + bool bankResult = false); + CommandResult undoStep(uint32_t boardId); + CommandResult completePresentaton(uint32_t boardId); + + const NumberPlayerState* playerState(uint32_t boardId) const; + NumbersRoundProjection projection() const; + + // IRound + RoundType roundType() const override { return RoundType::Numbers; } + CommandResult applyCommand(const CommandContext& ctx, CommandType type, + const std::vector& payload) override; + std::optional tryFinalize( + const std::vector& playerBoardIds) override; private: uint32_t target_{0}; std::vector tiles_; - std::map submissions_; - bool complete_{false}; - uint32_t winnerBoardId_{0}; + std::map playerStates_; }; // Letters Round State & Logic -class LettersRound { +class LettersRound : public IRound { public: LettersRound() = default; + // Picking phase — called by host; enforces 3-5 vowel / 4-6 consonant constraints. bool drawVowel(IRandomSource& random); bool drawConsonant(IRandomSource& random); + bool canDrawVowel() const; + bool canDrawConsonant() const; const std::string& drawnLetters() const { return drawnLetters_; } size_t letterCount() const { return drawnLetters_.size(); } + size_t vowelCount() const { return vowelCount_; } + size_t consonantCount() const { return consonantCount_; } bool isSelectionComplete() const { return drawnLetters_.size() >= 9; } - static bool isValidSubset(const std::string& word, const std::string& availableLetters); - CommandResult submitWord(uint32_t boardId, const std::string& word); + static bool isValidSubset(const std::string& word, + const std::string& availableLetters); + + // Claim phase — private word-length claim per player. + CommandResult submitClaim(uint32_t boardId, uint8_t claimedLength); + bool bothClaimsSubmitted() const; + uint8_t claimFor(uint32_t boardId) const; + + // Presentation phase — presenter taps tiles by letter index (0-8). + CommandResult presentTileTap(uint32_t boardId, uint8_t tileIndex); + CommandResult presentBackspace(uint32_t boardId); + CommandResult presentComplete(uint32_t boardId); + CommandResult verifyWord(uint32_t verifierBoardId, bool accepted); + + const std::string& presentedWordFor(uint32_t boardId) const; + LettersRoundProjection projection() const; + + // IRound + RoundType roundType() const override { return RoundType::Letters; } + CommandResult applyCommand(const CommandContext& ctx, CommandType type, + const std::vector& payload) override; + std::optional tryFinalize( + const std::vector& playerBoardIds) override; - bool isComplete() const { return complete_; } - const std::map& submittedWords() const { return submittedWords_; } RoundResult evaluate(const std::vector& playerBoardIds) const; private: std::string drawnLetters_; - std::map submittedWords_; + size_t vowelCount_{0}; + size_t consonantCount_{0}; + std::map claims_; + std::map presentedWords_; + std::map verifications_; bool complete_{false}; }; // Conundrum Round State & Logic -class ConundrumRound { +// Both players attempt simultaneously — no buzz mechanic. +class ConundrumRound : public IRound { public: - ConundrumRound(const std::string& solution, const std::string& scramble); + ConundrumRound(const std::string& solution, const std::string& scramble, + const std::string& hint); + + // Pick a random conundrum from the built-in content table. + static ConundrumRound createRandom(IRandomSource& random); const std::string& solution() const { return solution_; } const std::string& scramble() const { return scramble_; } + const std::string& hint() const { return hint_; } - CommandResult buzz(uint32_t boardId); - CommandResult submitSolution(uint32_t boardId, const std::string& attempt); + // Simultaneous attempt — checked immediately; no turn-based locking. + // Returns accepted=true only when attempt matches solution. + CommandResult submitAttempt(uint32_t boardId, const std::string& attempt); - uint32_t activeBuzzerBoardId() const { return activeBuzzerBoardId_; } bool isSolved() const { return solved_; } uint32_t winnerBoardId() const { return winnerBoardId_; } + ConundrumRoundProjection projection() const; + + // IRound + RoundType roundType() const override { return RoundType::Conundrum; } + CommandResult applyCommand(const CommandContext& ctx, CommandType type, + const std::vector& payload) override; + std::optional tryFinalize( + const std::vector& playerBoardIds) override; private: std::string solution_; std::string scramble_; - uint32_t activeBuzzerBoardId_{0}; - bool solved_{false}; + std::string hint_; + bool solved_{false}; uint32_t winnerBoardId_{0}; - std::vector failedBuzzers_; }; // Overall Match Manager class CountdownMatchEngine { public: - CountdownMatchEngine(uint32_t hostBoardId, uint32_t guestBoardId, uint32_t gameId); + CountdownMatchEngine(uint32_t hostBoardId, uint32_t guestBoardId, + uint32_t gameId); - uint32_t gameId() const { return gameId_; } - uint32_t hostBoardId() const { return hostBoardId_; } + uint32_t gameId() const { return gameId_; } + uint32_t hostBoardId() const { return hostBoardId_; } uint32_t guestBoardId() const { return guestBoardId_; } - MatchPhase phase() const { return phase_; } - uint32_t roundNumber() const { return roundNumber_; } - - const PlayerState& hostPlayer() const { return hostPlayer_; } + // Score leader — owns authoritative game state. + uint32_t hostPlayerId() const { return hostBoardId_; } + // Previous round winner — picks the next round type. + uint32_t chooserBoardId() const { return chooserBoardId_; } + uint32_t hostTerm() const { return hostTerm_; } + MatchPhase phase() const { return phase_; } + uint32_t roundNumber() const { return roundNumber_; } + + const PlayerState& hostPlayer() const { return hostPlayer_; } const PlayerState& guestPlayer() const { return guestPlayer_; } - // Convenience read-only snapshot for UI rendering. + bool isHost(uint32_t boardId) const { return boardId == hostBoardId_; } + bool isChooser(uint32_t boardId) const { return boardId == chooserBoardId_; } + + // Convenience snapshot for UI rendering. struct MatchState { PlayerState hostPlayer; PlayerState guestPlayer; - MatchPhase phase; - uint32_t roundNumber; + MatchPhase phase; + uint32_t roundNumber; + uint32_t chooserBoardId; + uint32_t hostTerm; }; MatchState matchState() const { - return MatchState{hostPlayer_, guestPlayer_, phase_, roundNumber_}; + return MatchState{hostPlayer_, guestPlayer_, phase_, + roundNumber_, chooserBoardId_, hostTerm_}; } - bool isHost(uint32_t boardId) const { return boardId == hostBoardId_; } - - // Reset the match to a fresh Setup state for a new host/guest pairing. + // Reset to a fresh Setup state for a new host/guest pairing. void resetMatch(uint32_t hostBoardId, uint32_t guestBoardId) { - hostBoardId_ = hostBoardId; - guestBoardId_ = guestBoardId; - hostPlayer_ = PlayerState{}; + hostBoardId_ = hostBoardId; + guestBoardId_ = guestBoardId; + chooserBoardId_ = hostBoardId; // host chooses the first round + hostTerm_ = 1; + hostPlayer_ = PlayerState{}; hostPlayer_.boardId = hostBoardId; - hostPlayer_.isHost = true; - guestPlayer_ = PlayerState{}; + hostPlayer_.isHost = true; + guestPlayer_ = PlayerState{}; guestPlayer_.boardId = guestBoardId; - guestPlayer_.isHost = false; - phase_ = MatchPhase::Setup; - roundNumber_ = 0; - currentRoundType_ = RoundType::Numbers; + phase_ = MatchPhase::Setup; + roundNumber_ = 0; + activeRound_.reset(); } - void startNextRound(RoundType type, IRandomSource& random); - - // Apply score updates and perform leader-based host migration (ADR-005) + // Create and store the active round object; advance to InRound. + void startNextRound(RoundType type, IRandomSource& random, + uint8_t largeCount = 2); + + // Active round access — nullptr when no round is in progress. + IRound* activeRound() { return activeRound_.get(); } + const IRound* activeRound() const { return activeRound_.get(); } + RoundType activeRoundType() const { return activeRoundType_; } + + // Round-type-specific accessors — nullptr when a different round type is active. + // Uses static_cast guarded by activeRoundType_ (no RTTI / dynamic_cast needed). + NumbersRound* numbersRound(); + const NumbersRound* numbersRound() const; + LettersRound* lettersRound(); + const LettersRound* lettersRound() const; + ConundrumRound* conundrumRound(); + const ConundrumRound* conundrumRound() const; + + // Display projections — safe to copy across tasks; return empty structs when + // no round of that type is active. + NumbersRoundProjection numbersProjection() const; + LettersRoundProjection lettersProjection() const; + ConundrumRoundProjection conundrumProjection() const; + + // Apply score updates; update hostBoardId (score leader) and chooserBoardId + // (round winner) independently per ADR-005. void recordRoundResult(const RoundResult& result); bool isMatchComplete() const { return phase_ == MatchPhase::MatchComplete; } private: - uint32_t gameId_{0}; - uint32_t hostBoardId_{0}; - uint32_t guestBoardId_{0}; + uint32_t gameId_{0}; + uint32_t hostBoardId_{0}; + uint32_t guestBoardId_{0}; + uint32_t chooserBoardId_{0}; + uint32_t hostTerm_{1}; PlayerState hostPlayer_; PlayerState guestPlayer_; MatchPhase phase_{MatchPhase::Setup}; - uint32_t roundNumber_{0}; - RoundType currentRoundType_{RoundType::Numbers}; + uint32_t roundNumber_{0}; + RoundType activeRoundType_{RoundType::Numbers}; + std::unique_ptr activeRound_; }; } // namespace countdown diff --git a/core/src/countdown_engine.cpp b/core/src/countdown_engine.cpp index 009c156..5fbb094 100644 --- a/core/src/countdown_engine.cpp +++ b/core/src/countdown_engine.cpp @@ -4,6 +4,8 @@ #include "flip7/countdown_engine.hpp" #include +#include +#include namespace flip7 { namespace countdown { @@ -17,25 +19,26 @@ NumbersRound::NumbersRound(uint32_t target, const std::vector& tiles) NumbersRound NumbersRound::createRandom(IRandomSource& random, uint8_t largeCount) { uint32_t target = random.nextRange(101, 999); - + std::vector largePool = {25, 50, 75, 100}; - std::vector smallPool = {1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10}; + std::vector smallPool = {1, 1, 2, 2, 3, 3, 4, 4, 5, 5, + 6, 6, 7, 7, 8, 8, 9, 9, 10, 10}; uint8_t largeToPick = std::min(largeCount, 4); - uint8_t smallToPick = 6 - largeToPick; + uint8_t smallToPick = static_cast(6 - largeToPick); std::vector selectedTiles; for (uint8_t i = 0; i < largeToPick && !largePool.empty(); ++i) { size_t idx = random.nextRange(0, static_cast(largePool.size() - 1)); selectedTiles.push_back(largePool[idx]); - largePool.erase(largePool.begin() + idx); + largePool.erase(largePool.begin() + static_cast(idx)); } for (uint8_t i = 0; i < smallToPick && !smallPool.empty(); ++i) { size_t idx = random.nextRange(0, static_cast(smallPool.size() - 1)); selectedTiles.push_back(smallPool[idx]); - smallPool.erase(smallPool.begin() + idx); + smallPool.erase(smallPool.begin() + static_cast(idx)); } return NumbersRound(target, selectedTiles); @@ -43,64 +46,442 @@ NumbersRound NumbersRound::createRandom(IRandomSource& random, uint8_t largeCoun int32_t NumbersRound::calculateScore(uint32_t target, uint32_t achieved) { uint32_t diff = target > achieved ? (target - achieved) : (achieved - target); - if (diff == 0) return 10; - if (diff <= 5) return 7; + if (diff == 0) return 10; + if (diff <= 5) return 7; if (diff <= 10) return 5; return 0; } -CommandResult NumbersRound::submitSolution(uint32_t boardId, uint32_t resultValue) { - submissions_[boardId] = resultValue; +CommandResult NumbersRound::submitClaim(uint32_t boardId, int32_t claimedValue) { + auto& ps = playerStates_[boardId]; + if (ps.claimSubmitted) { + return CommandResult{false, "ALREADY_CLAIMED", "Claim already submitted"}; + } + ps.claimedValue = claimedValue; + ps.claimSubmitted = true; + ps.workspace.reset(tiles_); + return CommandResult{true, "", ""}; +} + +bool NumbersRound::bothClaimsSubmitted() const { + if (playerStates_.size() < 2) return false; + for (const auto& kv : playerStates_) { + if (!kv.second.claimSubmitted) return false; + } + return true; +} + +int32_t NumbersRound::claimFor(uint32_t boardId) const { + auto it = playerStates_.find(boardId); + return (it != playerStates_.end()) ? it->second.claimedValue : 0; +} + +CommandResult NumbersRound::applyStep(uint32_t boardId, ArithmeticOp op, + int32_t operandA, int32_t operandB, + bool bankResult) { + auto it = playerStates_.find(boardId); + if (it == playerStates_.end() || !it->second.claimSubmitted) { + return CommandResult{false, "NO_CLAIM", "No active claim for this player"}; + } + NumberWorkspace& ws = it->second.workspace; + + // Validate operand A is available + bool aFound = false; + if (ws.currentValue.has_value() && ws.currentValue.value() == operandA) { + aFound = true; + } else { + for (size_t i = 0; i < ws.availableValues.size(); ++i) { + if (ws.availableValues[i] == operandA) { aFound = true; break; } + } + } + // Validate operand B is available (distinct from A) + bool bFound = false; + for (size_t i = 0; i < ws.availableValues.size(); ++i) { + if (ws.availableValues[i] == operandB) { bFound = true; break; } + } + if (!aFound || !bFound) { + return CommandResult{false, "OPERAND_UNAVAILABLE", + "One or both operands are not available"}; + } + + // Compute result + int32_t result = 0; + switch (op) { + case ArithmeticOp::Add: result = operandA + operandB; break; + case ArithmeticOp::Subtract: result = operandA - operandB; break; + case ArithmeticOp::Multiply: result = operandA * operandB; break; + case ArithmeticOp::Divide: + if (operandB == 0 || operandA % operandB != 0) { + return CommandResult{false, "INVALID_DIVISION", + "Division must yield a positive integer"}; + } + result = operandA / operandB; + break; + } + if (result <= 0) { + return CommandResult{false, "NON_POSITIVE", "Result must be positive"}; + } + + // Consume operand A from currentValue or availableValues + if (ws.currentValue.has_value() && ws.currentValue.value() == operandA) { + ws.currentValue.reset(); + } else { + for (size_t i = 0; i < ws.availableValues.size(); ++i) { + if (ws.availableValues[i] == operandA) { + ws.availableValues[i] = ws.availableValues.back(); + ws.availableValues.pop_back(); + break; + } + } + } + // Consume operand B from availableValues + for (size_t i = 0; i < ws.availableValues.size(); ++i) { + if (ws.availableValues[i] == operandB) { + ws.availableValues[i] = ws.availableValues.back(); + ws.availableValues.pop_back(); + break; + } + } + + CalculationStep step{operandA, op, operandB, result}; + it->second.steps.push_back(step); + ws.history.push_back(step); + + if (bankResult) { + ws.availableValues.push_back(result); + ws.currentValue.reset(); + } else { + ws.currentValue = result; // ContinueWithResult: left operand for next step + } + + return CommandResult{true, "", ""}; +} + +CommandResult NumbersRound::undoStep(uint32_t boardId) { + auto it = playerStates_.find(boardId); + if (it == playerStates_.end() || it->second.workspace.history.empty()) { + return CommandResult{false, "NOTHING_TO_UNDO", "No steps to undo"}; + } + NumberWorkspace& ws = it->second.workspace; + const CalculationStep& last = ws.history.back(); + + // Restore operands; remove result from wherever it ended up + ws.currentValue.reset(); + ws.availableValues.push_back(last.operandA); + ws.availableValues.push_back(last.operandB); + + // Remove the result value (it might be in currentValue or availableValues) + // Since we always remove it when adding a new step, just restore the pair. + ws.history.pop_back(); + if (!it->second.steps.empty()) it->second.steps.pop_back(); + + // Restore currentValue to the previous step's result if any + if (!ws.history.empty()) { + ws.currentValue = ws.history.back().result; + // Also remove that result from availableValues if it was banked there + } + + return CommandResult{true, "", ""}; +} + +CommandResult NumbersRound::completePresentaton(uint32_t boardId) { + auto it = playerStates_.find(boardId); + if (it == playerStates_.end()) { + return CommandResult{false, "UNKNOWN_PLAYER", "Unknown player"}; + } + NumberPlayerState& ps = it->second; + if (ps.presentationComplete) { + return CommandResult{false, "ALREADY_COMPLETE", "Presentation already complete"}; + } + // Determine demonstrated value: currentValue if set, else last step result + int32_t demonstrated = 0; + if (ps.workspace.currentValue.has_value()) { + demonstrated = ps.workspace.currentValue.value(); + } else if (!ps.steps.empty()) { + demonstrated = ps.steps.back().result; + } + ps.demonstratedValue = demonstrated; + ps.presentationComplete = true; + ps.presentationValid = + (demonstrated == ps.claimedValue) || + (calculateScore(target_, static_cast(demonstrated)) > 0 && + ps.steps.empty()); // no steps = claimed 0 or just guessing return CommandResult{true, "", ""}; } +const NumberPlayerState* NumbersRound::playerState(uint32_t boardId) const { + auto it = playerStates_.find(boardId); + return (it != playerStates_.end()) ? &it->second : nullptr; +} + +NumbersRoundProjection NumbersRound::projection() const { + NumbersRoundProjection p{}; + p.target = target_; + p.tileCount = static_cast(std::min(tiles_.size(), size_t{6})); + for (uint8_t i = 0; i < p.tileCount; ++i) p.tiles[i] = tiles_[i]; + return p; +} + +CommandResult NumbersRound::applyCommand(const CommandContext& ctx, + CommandType type, + const std::vector& payload) { + switch (type) { + case CommandType::NumSubmitClaim: { + if (payload.size() < 4) return {false, "BAD_PAYLOAD", ""}; + int32_t v = static_cast( + (uint32_t(payload[0])) | (uint32_t(payload[1]) << 8) | + (uint32_t(payload[2]) << 16) | (uint32_t(payload[3]) << 24)); + return submitClaim(ctx.senderBoardId, v); + } + case CommandType::NumPresentStepRequest: { + if (payload.size() < 9) return {false, "BAD_PAYLOAD", ""}; + auto op = static_cast(payload[0]); + int32_t a = static_cast( + (uint32_t(payload[1])) | (uint32_t(payload[2]) << 8) | + (uint32_t(payload[3]) << 16) | (uint32_t(payload[4]) << 24)); + int32_t b = static_cast( + (uint32_t(payload[5])) | (uint32_t(payload[6]) << 8) | + (uint32_t(payload[7]) << 16) | (uint32_t(payload[8]) << 24)); + bool bank = (payload.size() > 9 && payload[9] != 0); + return applyStep(ctx.senderBoardId, op, a, b, bank); + } + case CommandType::NumPresentBankResult: + return undoStep(ctx.senderBoardId); // BankResult reuses undo path + case CommandType::NumPresentUndo: + return undoStep(ctx.senderBoardId); + case CommandType::NumPresentComplete: + return completePresentaton(ctx.senderBoardId); + default: + return {false, "WRONG_ROUND", "Command not valid for Numbers round"}; + } +} + +std::optional NumbersRound::tryFinalize( + const std::vector& playerBoardIds) { + // Finalize when both players have completed their presentations. + bool allDone = !playerBoardIds.empty(); + for (uint32_t id : playerBoardIds) { + auto it = playerStates_.find(id); + if (it == playerStates_.end() || !it->second.presentationComplete) { + allDone = false; + break; + } + } + if (!allDone) return std::nullopt; + + RoundResult res; + res.roundType = RoundType::Numbers; + + int32_t bestScore = -1; + for (uint32_t id : playerBoardIds) { + auto it = playerStates_.find(id); + if (it == playerStates_.end()) continue; + const auto& ps = it->second; + int32_t pts = (ps.presentationValid) + ? calculateScore(target_, static_cast(ps.demonstratedValue)) + : 0; + res.scoreAwards[id] = pts; + if (pts > bestScore) bestScore = pts; + } + + uint32_t winCount = 0; + for (auto& kv : res.scoreAwards) { + if (kv.second == bestScore && bestScore > 0) { + res.winnerBoardId = kv.first; + ++winCount; + } + } + if (winCount > 1) { res.isTie = true; res.winnerBoardId = 0; } + + return res; +} + // -------------------------------------------------- // LettersRound // -------------------------------------------------- +bool LettersRound::canDrawVowel() const { + size_t remaining = 9 - drawnLetters_.size(); + if (remaining == 0) return false; + if (vowelCount_ >= 5) return false; + // Must leave enough slots for minimum consonants (4) + size_t neededConsonants = (consonantCount_ < 4) ? (4 - consonantCount_) : 0; + if (remaining <= neededConsonants) return false; + return true; +} + +bool LettersRound::canDrawConsonant() const { + size_t remaining = 9 - drawnLetters_.size(); + if (remaining == 0) return false; + if (consonantCount_ >= 6) return false; + // Must leave enough slots for minimum vowels (3) + size_t neededVowels = (vowelCount_ < 3) ? (3 - vowelCount_) : 0; + if (remaining <= neededVowels) return false; + return true; +} + bool LettersRound::drawVowel(IRandomSource& random) { - if (drawnLetters_.size() >= 9) return false; + if (!canDrawVowel()) return false; const char vowels[] = {'A', 'E', 'I', 'O', 'U'}; char v = vowels[random.nextRange(0, 4)]; drawnLetters_ += v; + ++vowelCount_; return true; } bool LettersRound::drawConsonant(IRandomSource& random) { - if (drawnLetters_.size() >= 9) return false; + if (!canDrawConsonant()) return false; const char consonants[] = { 'B', 'C', 'D', 'F', 'G', 'H', 'J', 'K', 'L', 'M', 'N', 'P', 'Q', 'R', 'S', 'T', 'V', 'W', 'X', 'Y', 'Z' }; char c = consonants[random.nextRange(0, 20)]; drawnLetters_ += c; + ++consonantCount_; return true; } -bool LettersRound::isValidSubset(const std::string& word, const std::string& availableLetters) { - std::map availableCounts; +bool LettersRound::isValidSubset(const std::string& word, + const std::string& availableLetters) { + std::map counts; for (char c : availableLetters) { - availableCounts[std::toupper(static_cast(c))]++; + counts[static_cast(std::toupper(static_cast(c)))]++; } - for (char c : word) { - char upper = std::toupper(static_cast(c)); - if (availableCounts[upper] <= 0) { - return false; - } - availableCounts[upper]--; + char upper = static_cast(std::toupper(static_cast(c))); + if (counts[upper] <= 0) return false; + counts[upper]--; } return true; } -CommandResult LettersRound::submitWord(uint32_t boardId, const std::string& word) { +CommandResult LettersRound::submitClaim(uint32_t boardId, uint8_t claimedLength) { + if (claimedLength == 0 || claimedLength > 9) { + return CommandResult{false, "INVALID_CLAIM", "Claimed length must be 1-9"}; + } + if (claims_.count(boardId)) { + return CommandResult{false, "ALREADY_CLAIMED", "Claim already submitted"}; + } + claims_[boardId] = claimedLength; + return CommandResult{true, "", ""}; +} + +bool LettersRound::bothClaimsSubmitted() const { + return claims_.size() >= 2; +} + +uint8_t LettersRound::claimFor(uint32_t boardId) const { + auto it = claims_.find(boardId); + return (it != claims_.end()) ? it->second : 0; +} + +CommandResult LettersRound::presentTileTap(uint32_t boardId, uint8_t tileIndex) { + if (tileIndex >= drawnLetters_.size()) { + return CommandResult{false, "BAD_INDEX", "Tile index out of range"}; + } + std::string& word = presentedWords_[boardId]; + // Count usage of each letter already in the word vs available + std::map used; + for (char c : word) used[c]++; + std::map pool; + for (char c : drawnLetters_) pool[c]++; + char tapped = drawnLetters_[tileIndex]; + if (used[tapped] >= pool[tapped]) { + return CommandResult{false, "TILE_USED", "That letter is already fully used"}; + } + word += tapped; + return CommandResult{true, "", ""}; +} + +CommandResult LettersRound::presentBackspace(uint32_t boardId) { + auto it = presentedWords_.find(boardId); + if (it == presentedWords_.end() || it->second.empty()) { + return CommandResult{false, "EMPTY", "No letters to remove"}; + } + it->second.pop_back(); + return CommandResult{true, "", ""}; +} + +CommandResult LettersRound::presentComplete(uint32_t boardId) { + const std::string& word = presentedWords_[boardId]; + uint8_t claimed = claimFor(boardId); + if (word.size() != claimed) { + return CommandResult{false, "LENGTH_MISMATCH", + "Presented word length does not match claim"}; + } if (!isValidSubset(word, drawnLetters_)) { - return CommandResult{false, "INVALID_WORD", "Word contains letters not in drawn set"}; + return CommandResult{false, "INVALID_SUBSET", + "Word uses letters not in the drawn set"}; } - submittedWords_[boardId] = word; return CommandResult{true, "", ""}; } +CommandResult LettersRound::verifyWord(uint32_t verifierBoardId, bool accepted) { + verifications_[verifierBoardId] = accepted; + return CommandResult{true, "", ""}; +} + +const std::string& LettersRound::presentedWordFor(uint32_t boardId) const { + static const std::string empty; + auto it = presentedWords_.find(boardId); + return (it != presentedWords_.end()) ? it->second : empty; +} + +LettersRoundProjection LettersRound::projection() const { + LettersRoundProjection p{}; + p.letterCount = static_cast( + std::min(drawnLetters_.size(), size_t{9})); + for (uint8_t i = 0; i < p.letterCount; ++i) p.letters[i] = drawnLetters_[i]; + return p; +} + +CommandResult LettersRound::applyCommand(const CommandContext& ctx, + CommandType type, + const std::vector& payload) { + switch (type) { + case CommandType::LetPickVowel: { + // Host-only; random source not available here — pick is pre-resolved. + if (payload.empty()) return {false, "BAD_PAYLOAD", ""}; + char letter = static_cast(payload[0]); + drawnLetters_ += letter; + ++vowelCount_; + return {true, "", ""}; + } + case CommandType::LetPickConsonant: { + if (payload.empty()) return {false, "BAD_PAYLOAD", ""}; + char letter = static_cast(payload[0]); + drawnLetters_ += letter; + ++consonantCount_; + return {true, "", ""}; + } + case CommandType::LetSubmitClaim: { + if (payload.empty()) return {false, "BAD_PAYLOAD", ""}; + return submitClaim(ctx.senderBoardId, payload[0]); + } + case CommandType::LetPresentTileTap: { + if (payload.empty()) return {false, "BAD_PAYLOAD", ""}; + return presentTileTap(ctx.senderBoardId, payload[0]); + } + case CommandType::LetPresentBackspace: + return presentBackspace(ctx.senderBoardId); + case CommandType::LetPresentComplete: + return presentComplete(ctx.senderBoardId); + case CommandType::LetVerifyWord: { + if (payload.empty()) return {false, "BAD_PAYLOAD", ""}; + return verifyWord(ctx.senderBoardId, payload[0] != 0); + } + default: + return {false, "WRONG_ROUND", "Command not valid for Letters round"}; + } +} + +std::optional LettersRound::tryFinalize( + const std::vector& playerBoardIds) { + // Finalize when all verifications are in. + if (verifications_.size() < playerBoardIds.size()) return std::nullopt; + return evaluate(playerBoardIds); +} + RoundResult LettersRound::evaluate(const std::vector& playerBoardIds) const { RoundResult res; res.roundType = RoundType::Letters; @@ -109,24 +490,25 @@ RoundResult LettersRound::evaluate(const std::vector& playerBoardIds) std::map validLengths; for (uint32_t id : playerBoardIds) { - auto it = submittedWords_.find(id); - if (it != submittedWords_.end() && isValidSubset(it->second, drawnLetters_)) { - size_t len = it->second.size(); + auto wIt = presentedWords_.find(id); + auto vIt = verifications_.find(id); // verifier is the opponent + // A word is valid when: subset is correct, length matches claim, + // and the opponent accepted it. + bool accepted = (vIt != verifications_.end()) ? vIt->second : false; + if (wIt != presentedWords_.end() && + isValidSubset(wIt->second, drawnLetters_) && + accepted) { + size_t len = wIt->second.size(); validLengths[id] = len; if (len > maxLen) maxLen = len; } } - if (maxLen == 0) { - return res; - } + if (maxLen == 0) return res; int32_t points = (maxLen == 9) ? 18 : static_cast(maxLen); - - for (auto& pair : validLengths) { - if (pair.second == maxLen) { - res.scoreAwards[pair.first] = points; - } + for (auto& kv : validLengths) { + if (kv.second == maxLen) res.scoreAwards[kv.first] = points; } if (res.scoreAwards.size() == 1) { @@ -134,90 +516,194 @@ RoundResult LettersRound::evaluate(const std::vector& playerBoardIds) } else if (res.scoreAwards.size() > 1) { res.isTie = true; } - return res; } // -------------------------------------------------- -// ConundrumRound +// ConundrumRound — built-in word table +// TODO: replace with content/conundrum/default_words.csv -> generated header // -------------------------------------------------- -ConundrumRound::ConundrumRound(const std::string& solution, const std::string& scramble) - : solution_(solution), scramble_(scramble) {} +namespace { +struct ConundrumEntry { const char* solution; const char* scramble; const char* hint; }; +static const ConundrumEntry kConundrumTable[] = { + {"BLUEPRINT", "TNPULEBRI", "A detailed plan or design"}, + {"COUNTDOWN", "DTWNOCNUO", "A backward sequence before an event"}, + {"STARTLING", "GTLNRSAIT", "Surprising or astonishing"}, + {"CARPETING", "TICNGPEAR", "Floor covering material"}, + {"GRENADIER", "AIGNRDERE", "A type of soldier"}, + {"PETROLEUM", "MOUERTPLE", "Crude oil and its derivatives"}, + {"RECTANGLE", "CTRELAGNE", "A four-sided flat shape"}, + {"IMPORTANT", "TNAMIPORT", "Of great significance"}, + {"TRAMPOLIN", "MILNPAOTR", "Bouncing apparatus"}, + {"SPECTATOR", "POSTCATER", "Someone watching an event"}, +}; +static constexpr size_t kConundrumTableSize = + sizeof(kConundrumTable) / sizeof(kConundrumTable[0]); +} // namespace + +ConundrumRound::ConundrumRound(const std::string& solution, + const std::string& scramble, + const std::string& hint) + : solution_(solution), scramble_(scramble), hint_(hint) {} + +ConundrumRound ConundrumRound::createRandom(IRandomSource& random) { + size_t idx = random.nextRange(0, static_cast(kConundrumTableSize - 1)); + const auto& entry = kConundrumTable[idx]; + return ConundrumRound(entry.solution, entry.scramble, entry.hint); +} -CommandResult ConundrumRound::buzz(uint32_t boardId) { +CommandResult ConundrumRound::submitAttempt(uint32_t boardId, + const std::string& attempt) { if (solved_) { - return CommandResult{false, "ALREADY_SOLVED", "Conundrum is already solved"}; + return CommandResult{false, "ALREADY_SOLVED", "Conundrum already solved"}; } - if (activeBuzzerBoardId_ != 0) { - return CommandResult{false, "BUZZER_ACTIVE", "Another player has active buzz window"}; - } - if (std::find(failedBuzzers_.begin(), failedBuzzers_.end(), boardId) != failedBuzzers_.end()) { - return CommandResult{false, "ALREADY_FAILED", "Player already submitted an incorrect answer"}; + std::string clean = attempt; + std::string correct = solution_; + for (auto& c : clean) c = static_cast(std::toupper(static_cast(c))); + for (auto& c : correct) c = static_cast(std::toupper(static_cast(c))); + + if (clean == correct) { + solved_ = true; + winnerBoardId_ = boardId; + return CommandResult{true, "", ""}; } + // Wrong answer — caller handles local reshuffle; no state mutation. + return CommandResult{false, "INCORRECT", "Incorrect answer — try again"}; +} - activeBuzzerBoardId_ = boardId; - return CommandResult{true, "", ""}; +ConundrumRoundProjection ConundrumRound::projection() const { + ConundrumRoundProjection p{}; + std::strncpy(p.scramble, scramble_.c_str(), sizeof(p.scramble) - 1); + std::strncpy(p.hint, hint_.c_str(), sizeof(p.hint) - 1); + p.solved = solved_; + p.winnerBoardId = winnerBoardId_; + return p; } -CommandResult ConundrumRound::submitSolution(uint32_t boardId, const std::string& attempt) { - if (activeBuzzerBoardId_ != boardId) { - return CommandResult{false, "NOT_BUZZED", "Player must buzz before submitting solution"}; +CommandResult ConundrumRound::applyCommand(const CommandContext& ctx, + CommandType type, + const std::vector& payload) { + if (type != CommandType::ConSubmitAttempt) { + return {false, "WRONG_ROUND", "Command not valid for Conundrum round"}; } + std::string attempt(payload.begin(), payload.end()); + return submitAttempt(ctx.senderBoardId, attempt); +} - std::string cleanAttempt = attempt; - std::string cleanSolution = solution_; - for (auto& c : cleanAttempt) c = std::toupper(static_cast(c)); - for (auto& c : cleanSolution) c = std::toupper(static_cast(c)); +std::optional ConundrumRound::tryFinalize( + const std::vector& /*playerBoardIds*/) { + if (!solved_) return std::nullopt; - if (cleanAttempt == cleanSolution) { - solved_ = true; - winnerBoardId_ = boardId; - activeBuzzerBoardId_ = 0; - return CommandResult{true, "", ""}; - } else { - failedBuzzers_.push_back(boardId); - activeBuzzerBoardId_ = 0; - return CommandResult{false, "INCORRECT_SOLUTION", "Submitted solution is incorrect"}; - } + RoundResult res; + res.roundType = RoundType::Conundrum; + res.winnerBoardId = winnerBoardId_; + res.scoreAwards[winnerBoardId_] = 10; + return res; } // -------------------------------------------------- // CountdownMatchEngine // -------------------------------------------------- -CountdownMatchEngine::CountdownMatchEngine(uint32_t hostBoardId, uint32_t guestBoardId, uint32_t gameId) - : gameId_(gameId), hostBoardId_(hostBoardId), guestBoardId_(guestBoardId) { - hostPlayer_.boardId = hostBoardId; - hostPlayer_.isHost = true; +CountdownMatchEngine::CountdownMatchEngine(uint32_t hostBoardId, + uint32_t guestBoardId, + uint32_t gameId) + : gameId_(gameId), + hostBoardId_(hostBoardId), + guestBoardId_(guestBoardId), + chooserBoardId_(hostBoardId), + hostTerm_(1) { + hostPlayer_.boardId = hostBoardId; + hostPlayer_.isHost = true; guestPlayer_.boardId = guestBoardId; - guestPlayer_.isHost = false; - phase_ = MatchPhase::Setup; + phase_ = MatchPhase::Setup; } -void CountdownMatchEngine::startNextRound(RoundType type, IRandomSource& random) { - currentRoundType_ = type; - roundNumber_++; +void CountdownMatchEngine::startNextRound(RoundType type, IRandomSource& random, + uint8_t largeCount) { + activeRound_.reset(); + activeRoundType_ = type; + switch (type) { + case RoundType::Numbers: + activeRound_ = std::make_unique( + NumbersRound::createRandom(random, largeCount)); + break; + case RoundType::Letters: + activeRound_ = std::make_unique(); + break; + case RoundType::Conundrum: + activeRound_ = std::make_unique( + ConundrumRound::createRandom(random)); + break; + } + ++roundNumber_; phase_ = MatchPhase::InRound; } +NumbersRound* CountdownMatchEngine::numbersRound() { + return (activeRound_ && activeRoundType_ == RoundType::Numbers) + ? static_cast(activeRound_.get()) : nullptr; +} +const NumbersRound* CountdownMatchEngine::numbersRound() const { + return (activeRound_ && activeRoundType_ == RoundType::Numbers) + ? static_cast(activeRound_.get()) : nullptr; +} +LettersRound* CountdownMatchEngine::lettersRound() { + return (activeRound_ && activeRoundType_ == RoundType::Letters) + ? static_cast(activeRound_.get()) : nullptr; +} +const LettersRound* CountdownMatchEngine::lettersRound() const { + return (activeRound_ && activeRoundType_ == RoundType::Letters) + ? static_cast(activeRound_.get()) : nullptr; +} +ConundrumRound* CountdownMatchEngine::conundrumRound() { + return (activeRound_ && activeRoundType_ == RoundType::Conundrum) + ? static_cast(activeRound_.get()) : nullptr; +} +const ConundrumRound* CountdownMatchEngine::conundrumRound() const { + return (activeRound_ && activeRoundType_ == RoundType::Conundrum) + ? static_cast(activeRound_.get()) : nullptr; +} + +NumbersRoundProjection CountdownMatchEngine::numbersProjection() const { + const NumbersRound* r = numbersRound(); + return r ? r->projection() : NumbersRoundProjection{}; +} +LettersRoundProjection CountdownMatchEngine::lettersProjection() const { + const LettersRound* r = lettersRound(); + return r ? r->projection() : LettersRoundProjection{}; +} +ConundrumRoundProjection CountdownMatchEngine::conundrumProjection() const { + const ConundrumRound* r = conundrumRound(); + return r ? r->projection() : ConundrumRoundProjection{}; +} + void CountdownMatchEngine::recordRoundResult(const RoundResult& result) { - for (const auto& pair : result.scoreAwards) { - if (pair.first == hostPlayer_.boardId) { - hostPlayer_.score += pair.second; - } else if (pair.first == guestPlayer_.boardId) { - guestPlayer_.score += pair.second; + // Apply score awards. + for (const auto& kv : result.scoreAwards) { + if (kv.first == hostPlayer_.boardId) { + hostPlayer_.score += kv.second; + } else if (kv.first == guestPlayer_.boardId) { + guestPlayer_.score += kv.second; } } phase_ = MatchPhase::BetweenRounds; + activeRound_.reset(); + + // Update chooser (round winner picks next round). + // No winner (tie or timeout) retains the previous chooser. + if (!result.isTie && result.winnerBoardId != 0) { + chooserBoardId_ = result.winnerBoardId; + } - // ADR-005 Leader-based host migration: - // "The score leader becomes host between rounds. Ties retain the current host." + // ADR-005 Leader-based host migration: score leader becomes host. + // Ties retain the current host; hostTerm increments on transfer. if (guestPlayer_.score > hostPlayer_.score) { - // Transfer host authority to guest + ++hostTerm_; std::swap(hostBoardId_, guestBoardId_); - hostPlayer_.isHost = false; + hostPlayer_.isHost = false; guestPlayer_.isHost = true; std::swap(hostPlayer_, guestPlayer_); } diff --git a/core/tests/countdown_test.cpp b/core/tests/countdown_test.cpp index 042247a..a9aca8d 100644 --- a/core/tests/countdown_test.cpp +++ b/core/tests/countdown_test.cpp @@ -7,6 +7,27 @@ using namespace flip7::countdown; +// --------------------------------------------------------------------------- +// makeRoundRandom — determinism +// --------------------------------------------------------------------------- + +TEST(CountdownTest, MakeRoundRandomIsDeterministic) { + auto r1 = makeRoundRandom(0xABCD1234u, 3u); + auto r2 = makeRoundRandom(0xABCD1234u, 3u); + EXPECT_EQ(r1.next(), r2.next()); + EXPECT_EQ(r1.next(), r2.next()); +} + +TEST(CountdownTest, MakeRoundRandomDiffersAcrossRounds) { + auto r1 = makeRoundRandom(42u, 1u); + auto r2 = makeRoundRandom(42u, 2u); + EXPECT_NE(r1.next(), r2.next()); +} + +// --------------------------------------------------------------------------- +// NumbersRound — generation, scoring, claims, steps +// --------------------------------------------------------------------------- + TEST(CountdownTest, NumbersRoundGenerationAndScoring) { SeededRandomSource random(42); NumbersRound round = NumbersRound::createRandom(random, 2); @@ -15,90 +36,298 @@ TEST(CountdownTest, NumbersRoundGenerationAndScoring) { EXPECT_LE(round.target(), 999u); EXPECT_EQ(round.tiles().size(), 6u); - // Test scoring table EXPECT_EQ(NumbersRound::calculateScore(500, 500), 10); EXPECT_EQ(NumbersRound::calculateScore(500, 503), 7); EXPECT_EQ(NumbersRound::calculateScore(500, 508), 5); EXPECT_EQ(NumbersRound::calculateScore(500, 515), 0); } -TEST(CountdownTest, LettersRoundDrawingAndValidation) { +TEST(CountdownTest, NumbersRoundClaims) { + SeededRandomSource random(1); + NumbersRound round = NumbersRound::createRandom(random, 0); + + EXPECT_FALSE(round.bothClaimsSubmitted()); + + auto r1 = round.submitClaim(0x11, 480); + EXPECT_TRUE(r1.accepted); + EXPECT_FALSE(round.bothClaimsSubmitted()); + + auto r2 = round.submitClaim(0x22, 495); + EXPECT_TRUE(r2.accepted); + EXPECT_TRUE(round.bothClaimsSubmitted()); + + // Duplicate claim rejected + auto r3 = round.submitClaim(0x11, 490); + EXPECT_FALSE(r3.accepted); + EXPECT_EQ(r3.errorCode, "ALREADY_CLAIMED"); + + EXPECT_EQ(round.claimFor(0x11), 480); + EXPECT_EQ(round.claimFor(0x22), 495); +} + +TEST(CountdownTest, NumbersRoundArithmeticStep) { + // Simple round: target 100, tiles [25, 50, 4, 2, 1, 1] + NumbersRound round(100, {25, 50, 4, 2, 1, 1}); + round.submitClaim(0x01, 100); + + // 25 + 50 = 75 + auto r1 = round.applyStep(0x01, ArithmeticOp::Add, 25, 50); + EXPECT_TRUE(r1.accepted); + + // 75 + 25 would fail — 25 is consumed + auto r2 = round.applyStep(0x01, ArithmeticOp::Add, 75, 25); + EXPECT_FALSE(r2.accepted); // 25 no longer available + + // 75 + 4 = 79 + auto r3 = round.applyStep(0x01, ArithmeticOp::Add, 75, 4); + EXPECT_TRUE(r3.accepted); + + // Undo + auto r4 = round.undoStep(0x01); + EXPECT_TRUE(r4.accepted); +} + +TEST(CountdownTest, NumbersRoundDivisionValidation) { + NumbersRound round(300, {100, 25, 4, 3, 2, 1}); + round.submitClaim(0x01, 300); + + // Valid integer division: 100 / 4 = 25 + auto r1 = round.applyStep(0x01, ArithmeticOp::Divide, 100, 4); + EXPECT_TRUE(r1.accepted); + + // Non-integer division rejected: 25 / 4 + round.submitClaim(0x02, 0); + auto r2 = round.applyStep(0x02, ArithmeticOp::Divide, 25, 4); + EXPECT_FALSE(r2.accepted); + EXPECT_EQ(r2.errorCode, "INVALID_DIVISION"); +} + +// --------------------------------------------------------------------------- +// LettersRound — picking constraints, claims, tile presentation +// --------------------------------------------------------------------------- + +TEST(CountdownTest, LettersRoundPickingConstraints) { SeededRandomSource random(1234); LettersRound round; - for (int i = 0; i < 4; ++i) round.drawVowel(random); - for (int i = 0; i < 5; ++i) round.drawConsonant(random); + // Draw 4 vowels; 5th should be blocked (max 5 but we need room for 4 consonants) + for (int i = 0; i < 4; ++i) { + EXPECT_TRUE(round.canDrawVowel()); + EXPECT_TRUE(round.drawVowel(random)); + } + + // At 4 vowels, 0 consonants, 5 remaining → vowel still allowed (max is 5) + EXPECT_TRUE(round.canDrawVowel()); + // Draw 3 consonants + for (int i = 0; i < 3; ++i) { + EXPECT_TRUE(round.canDrawConsonant()); + EXPECT_TRUE(round.drawConsonant(random)); + } + + // Now 4 vowels, 3 consonants, 2 remaining — must have at least 1 more consonant + // Vowel still ok (4 < 5 and remaining 2 > neededConsonants 1) + EXPECT_TRUE(round.canDrawVowel()); + + // Draw 5th vowel + EXPECT_TRUE(round.drawVowel(random)); + // Now 5 vowels — vowel button disabled + EXPECT_FALSE(round.canDrawVowel()); + + // Draw last consonant + EXPECT_TRUE(round.drawConsonant(random)); EXPECT_EQ(round.letterCount(), 9u); EXPECT_TRUE(round.isSelectionComplete()); +} - std::string drawn = round.drawnLetters(); +TEST(CountdownTest, LettersRoundValidSubset) { + SeededRandomSource random(1234); + LettersRound round; + for (int i = 0; i < 4; ++i) round.drawVowel(random); + for (int i = 0; i < 5; ++i) round.drawConsonant(random); - // Test subset validation - EXPECT_TRUE(LettersRound::isValidSubset("", drawn)); - EXPECT_FALSE(LettersRound::isValidSubset("ZZZZZZZZZZ", drawn)); + EXPECT_EQ(round.letterCount(), 9u); + EXPECT_TRUE(LettersRound::isValidSubset("", round.drawnLetters())); + EXPECT_FALSE(LettersRound::isValidSubset("ZZZZZZZZZZ", round.drawnLetters())); +} - // Submit word - CommandResult res = round.submitWord(0x101, drawn.substr(0, 5)); - EXPECT_TRUE(res.accepted); +TEST(CountdownTest, LettersRoundClaimsAndPresentation) { + LettersRound round; + // Manually set letters for determinism + for (char c : std::string("AEIORCNTS")) { + // Use applyCommand to add each letter + CommandContext ctx{0x10, 0}; + std::vector payload = {static_cast(c)}; + round.applyCommand(ctx, CommandType::LetPickVowel, payload); + } - std::vector players = {0x101, 0x102}; - RoundResult rr = round.evaluate(players); - EXPECT_EQ(rr.winnerBoardId, 0x101u); - EXPECT_EQ(rr.scoreAwards[0x101], 5); -} + // Claim length 5 + auto r1 = round.submitClaim(0x101, 5); + EXPECT_TRUE(r1.accepted); -TEST(CountdownTest, ConundrumRoundBuzzerAndSolving) { - ConundrumRound round("PERFECTLY", "CYPTERELF"); + // Duplicate claim rejected + EXPECT_FALSE(round.submitClaim(0x101, 6).accepted); - EXPECT_EQ(round.solution(), "PERFECTLY"); - EXPECT_EQ(round.scramble(), "CYPTERELF"); + // Length must be 1-9 + EXPECT_FALSE(round.submitClaim(0x102, 0).accepted); + EXPECT_TRUE(round.submitClaim(0x102, 7).accepted); + EXPECT_TRUE(round.bothClaimsSubmitted()); +} - // Buzz player 1 - CommandResult buzzRes = round.buzz(0x201); - EXPECT_TRUE(buzzRes.accepted); - EXPECT_EQ(round.activeBuzzerBoardId(), 0x201u); +// --------------------------------------------------------------------------- +// ConundrumRound — simultaneous attempts (no buzz) +// --------------------------------------------------------------------------- - // Player 2 cannot buzz while player 1 has active window - CommandResult buzzRes2 = round.buzz(0x202); - EXPECT_FALSE(buzzRes2.accepted); +TEST(CountdownTest, ConundrumRoundSimultaneousAttempts) { + ConundrumRound round("BLUEPRINT", "TNPULEBRI", "A detailed plan or design"); - // Incorrect answer - CommandResult submitWrong = round.submitSolution(0x201, "WRONGWORD"); - EXPECT_FALSE(submitWrong.accepted); - EXPECT_EQ(round.activeBuzzerBoardId(), 0u); + EXPECT_EQ(round.solution(), "BLUEPRINT"); + EXPECT_EQ(round.hint(), "A detailed plan or design"); + EXPECT_FALSE(round.isSolved()); - // Player 2 can now buzz - CommandResult buzzRes3 = round.buzz(0x202); - EXPECT_TRUE(buzzRes3.accepted); + // Player 1 incorrect — no lockout + auto r1 = round.submitAttempt(0x201, "WRONGWORD"); + EXPECT_FALSE(r1.accepted); + EXPECT_FALSE(round.isSolved()); - // Correct answer - CommandResult submitRight = round.submitSolution(0x202, "PERFECTLY"); - EXPECT_TRUE(submitRight.accepted); + // Player 2 can immediately attempt — simultaneous + auto r2 = round.submitAttempt(0x202, "BLUEPRINT"); + EXPECT_TRUE(r2.accepted); EXPECT_TRUE(round.isSolved()); EXPECT_EQ(round.winnerBoardId(), 0x202u); } -TEST(CountdownTest, MatchEngineLeaderHostMigration) { - uint32_t hostId = 0xA1; - uint32_t guestId = 0xB2; - uint32_t gameId = 999; +TEST(CountdownTest, ConundrumRoundAlreadySolved) { + ConundrumRound round("BLUEPRINT", "TNPULEBRI", "A hint"); + round.submitAttempt(0x201, "BLUEPRINT"); + + // Further attempts rejected once solved + auto r = round.submitAttempt(0x202, "BLUEPRINT"); + EXPECT_FALSE(r.accepted); + EXPECT_EQ(r.errorCode, "ALREADY_SOLVED"); +} + +TEST(CountdownTest, ConundrumRoundCreateRandom) { + auto random = makeRoundRandom(999u, 5u); + ConundrumRound round = ConundrumRound::createRandom(random); + + EXPECT_FALSE(round.solution().empty()); + EXPECT_FALSE(round.scramble().empty()); + EXPECT_FALSE(round.hint().empty()); + EXPECT_EQ(round.solution().size(), 9u); +} + +// --------------------------------------------------------------------------- +// MatchEngine — startNextRound creates active round; dual-role recordRoundResult +// --------------------------------------------------------------------------- + +TEST(CountdownTest, MatchEngineStartNextRoundCreatesRound) { + CountdownMatchEngine engine(0xA1, 0xB2, 999); + + EXPECT_EQ(engine.activeRound(), nullptr); + + auto random = makeRoundRandom(999u, 1u); + engine.startNextRound(RoundType::Numbers, random, 2); + + EXPECT_NE(engine.activeRound(), nullptr); + EXPECT_EQ(engine.activeRound()->roundType(), RoundType::Numbers); + EXPECT_NE(engine.numbersRound(), nullptr); + EXPECT_EQ(engine.lettersRound(), nullptr); + EXPECT_EQ(engine.conundrumRound(), nullptr); +} + +TEST(CountdownTest, MatchEngineLeaderHostMigrationAndChooserUpdate) { + uint32_t hostId = 0xA1; + uint32_t guestId = 0xB2; + uint32_t gameId = 999; CountdownMatchEngine engine(hostId, guestId, gameId); - EXPECT_EQ(engine.hostBoardId(), hostId); - EXPECT_EQ(engine.guestBoardId(), guestId); + EXPECT_EQ(engine.hostBoardId(), hostId); + EXPECT_EQ(engine.guestBoardId(), guestId); + EXPECT_EQ(engine.chooserBoardId(), hostId); // host chooses first + EXPECT_EQ(engine.hostTerm(), 1u); - SeededRandomSource random(777); - engine.startNextRound(RoundType::Numbers, random); + auto random = makeRoundRandom(gameId, 1u); + engine.startNextRound(RoundType::Numbers, random, 2); + // Guest wins the round — becomes chooser AND takes score lead → becomes host RoundResult rr; - rr.roundType = RoundType::Numbers; - rr.scoreAwards[guestId] = 10; // Guest takes lead! + rr.roundType = RoundType::Numbers; rr.winnerBoardId = guestId; + rr.scoreAwards[guestId] = 10; engine.recordRoundResult(rr); - // ADR-005 Leader-Based Host Migration: guest should become new host! - EXPECT_EQ(engine.hostBoardId(), guestId); + // ADR-005: guest took score lead → guest becomes new host + EXPECT_EQ(engine.hostBoardId(), guestId); EXPECT_EQ(engine.guestBoardId(), hostId); + EXPECT_EQ(engine.hostTerm(), 2u); // incremented on transfer + + // chooser = round winner = guestId + EXPECT_EQ(engine.chooserBoardId(), guestId); +} + +TEST(CountdownTest, MatchEngineChooserRetainedOnTie) { + CountdownMatchEngine engine(0xA1, 0xB2, 1); + auto random = makeRoundRandom(1u, 1u); + engine.startNextRound(RoundType::Letters, random); + + // Tie round — no winner + RoundResult rr; + rr.roundType = RoundType::Letters; + rr.isTie = true; + rr.scoreAwards[0xA1] = 5; + rr.scoreAwards[0xB2] = 5; + + uint32_t prevChooser = engine.chooserBoardId(); + engine.recordRoundResult(rr); + + // Tie: previous chooser retained; no host migration (scores equal) + EXPECT_EQ(engine.chooserBoardId(), prevChooser); + EXPECT_EQ(engine.hostTerm(), 1u); // no transfer +} + +// --------------------------------------------------------------------------- +// IRound interface — applyCommand dispatch +// --------------------------------------------------------------------------- + +TEST(CountdownTest, IRoundNumbersApplyCommandClaim) { + auto random = makeRoundRandom(10u, 1u); + NumbersRound round = NumbersRound::createRandom(random, 2); + + CommandContext ctx{0x01, 0}; + // Encode claimed value 350 as little-endian uint32 + std::vector payload = {0x5E, 0x01, 0x00, 0x00}; // 0x15E = 350 + auto result = round.applyCommand(ctx, CommandType::NumSubmitClaim, payload); + EXPECT_TRUE(result.accepted); + EXPECT_EQ(round.claimFor(0x01), 350); +} + +TEST(CountdownTest, IRoundConundrumApplyCommand) { + ConundrumRound round("STARTLING", "GTLNRSAIT", "Surprising"); + CommandContext ctx{0x10, 0}; + std::vector attempt{'S','T','A','R','T','L','I','N','G'}; + auto result = round.applyCommand(ctx, CommandType::ConSubmitAttempt, attempt); + EXPECT_TRUE(result.accepted); + EXPECT_TRUE(round.isSolved()); } + +// --------------------------------------------------------------------------- +// FixedVector +// --------------------------------------------------------------------------- + +TEST(CountdownTest, FixedVectorCapacity) { + FixedVector v; + EXPECT_TRUE(v.empty()); + EXPECT_TRUE(v.push_back(1)); + EXPECT_TRUE(v.push_back(2)); + EXPECT_TRUE(v.push_back(3)); + EXPECT_TRUE(v.full()); + EXPECT_FALSE(v.push_back(4)); // over capacity + EXPECT_EQ(v.size(), 3u); + EXPECT_EQ(v[0], 1); + EXPECT_TRUE(v.pop_back()); + EXPECT_EQ(v.size(), 2u); + EXPECT_FALSE(v.full()); +} + diff --git a/include/countdown_wire.h b/include/countdown_wire.h index 2f5064c..319d5d0 100644 --- a/include/countdown_wire.h +++ b/include/countdown_wire.h @@ -4,34 +4,75 @@ #include #include -// Wire-safe (POD, memcpy-able) synchronized state for the Countdown game -// mode. This mirrors MastermindState's role: the countdown_engine.hpp -// classes (NumbersRound/LettersRound/ConundrumRound/CommandResult) use -// std::string/std::vector and are NOT sent over ESP-NOW directly. This -// struct captures just enough of the match state (host/guest identity, -// phase, round number, scores) to keep both boards' Countdown screens in -// sync. Round-specific puzzle content (numbers tiles, letters, conundrum -// scramble) is intentionally NOT synchronized in this pass. +// Wire-safe (POD, memcpy-able) synchronized state for the Countdown game mode. +// +// Identity fields +// hostBoardId — the score leader; owns authoritative game state. +// guestBoardId — the non-leader player. +// chooserBoardId — the previous round winner; picks the next round type. +// Initialised to hostBoardId; retained on no-winner rounds. +// hostTerm — incremented on every host-authority transfer; authoritative +// packets with a stale hostTerm are rejected. +// +// Round-specific puzzle content (tiles, letters, conundrum scramble) is +// transmitted via CountdownFullStatePacket, not stored here. enum class CountdownWirePhase : uint8_t { - Setup = 1, + Setup = 1, InRound, BetweenRounds, MatchComplete, Exited, }; +// Sub-phase within InRound. Stored in roundSubPhase; 0 = none. +enum class CountdownRoundSubPhase : uint8_t { + None = 0, + Intro = 1, // 30-second get-ready animation (all round types) + NumPicking = 2, // chooser selects large-number count + NumThinking = 3, // 30-second silent calculation + NumClaimEntry = 4, // private achieved-value entry + NumClaimReveal = 5, // both values revealed simultaneously + NumPresentPlayerA = 6, // first presenter (closer to target) + NumPresentPlayerB = 7, // second presenter + NumResult = 8, + LetPicking = 9, // host draws vowels and consonants + LetThinking = 10, // 30-second solve timer + LetClaimEntry = 11, // private word-length claim + LetClaimReveal = 12, + LetPresentPlayerA = 13, + LetPresentPlayerB = 14, + LetResult = 15, + ConReady = 16, // conundrum intro + ConActive = 17, // circular tiles visible; both players attempt + ConResult = 18, // someone solved it + ConResultNoWinner = 19, // timeout; solution revealed; no points + HostTransfer = 20, // old host sending committed snapshot + HostTransferAck = 21, // new host acknowledged +}; + struct CountdownWireState { - uint32_t gameId; - uint32_t revision; + uint32_t gameId; // offset 0 + uint32_t revision; // offset 4 + uint32_t hostBoardId; // offset 8 + uint32_t guestBoardId; // offset 12 + uint32_t chooserBoardId; // offset 16 (previous round winner) + uint32_t hostTerm; // offset 20 (incremented on authority transfer) + int32_t hostScore; // offset 24 + int32_t guestScore; // offset 28 + uint32_t roundNumber; // offset 32 + CountdownWirePhase phase; // offset 36 + uint8_t roundType; // offset 37 + uint8_t roundSubPhase; // offset 38 (CountdownRoundSubPhase cast) + uint8_t reserved; // offset 39 must be zero +}; + +// Host-authority record — carried in transfer packets; every authoritative +// packet includes hostTerm so delayed old-host packets can be rejected. +struct HostAuthority { uint32_t hostBoardId; - uint32_t guestBoardId; - int32_t hostScore; - int32_t guestScore; - uint32_t roundNumber; - CountdownWirePhase phase; - uint8_t roundType; - uint8_t reserved[2]; + uint32_t hostTerm; + uint32_t nextSequenceNumber; }; inline bool sameCountdownWireState(const CountdownWireState& first, @@ -43,15 +84,15 @@ inline bool isValidCountdownWireState(const CountdownWireState& state) { if (state.gameId == 0 || state.hostBoardId == 0 || state.guestBoardId == 0 || state.hostBoardId == state.guestBoardId || + state.hostTerm == 0 || + (state.chooserBoardId != 0 && + state.chooserBoardId != state.hostBoardId && + state.chooserBoardId != state.guestBoardId) || state.phase < CountdownWirePhase::Setup || - state.phase > CountdownWirePhase::Exited) { + state.phase > CountdownWirePhase::Exited || + state.reserved != 0) { return false; } - for (uint8_t value : state.reserved) { - if (value != 0) { - return false; - } - } return true; } @@ -59,11 +100,14 @@ inline CountdownWireState makeCountdownMatch(uint32_t hostBoardId, uint32_t guestBoardId, uint32_t gameId) { CountdownWireState state{}; - state.gameId = gameId; - state.hostBoardId = hostBoardId; - state.guestBoardId = guestBoardId; - state.roundNumber = 1; - state.phase = CountdownWirePhase::Setup; + state.gameId = gameId; + state.revision = 1; + state.hostBoardId = hostBoardId; + state.guestBoardId = guestBoardId; + state.chooserBoardId = hostBoardId; // host chooses the first round + state.hostTerm = 1; + state.roundNumber = 1; + state.phase = CountdownWirePhase::Setup; return state; } @@ -135,12 +179,49 @@ inline bool selectCountdownRoundType(CountdownWireState& state, uint32_t actorBoardId, uint8_t roundType) { if (state.phase != CountdownWirePhase::BetweenRounds || - actorBoardId != state.hostBoardId || roundType < 1 || - roundType > 3) { + actorBoardId != state.chooserBoardId || // chooser picks, not host + roundType < 1 || roundType > 3) { return false; } - state.roundType = roundType; - state.phase = CountdownWirePhase::InRound; + state.roundType = roundType; + state.roundSubPhase = static_cast(CountdownRoundSubPhase::Intro); + state.phase = CountdownWirePhase::InRound; + ++state.revision; + return true; +} + +// Advance to a new round sub-phase. Host-only. +inline bool advanceCountdownSubPhase(CountdownWireState& state, + uint32_t actorBoardId, + CountdownRoundSubPhase next) { + if (actorBoardId != state.hostBoardId || + state.phase != CountdownWirePhase::InRound) { + return false; + } + state.roundSubPhase = static_cast(next); + ++state.revision; + return true; +} + +// Apply a committed host-transfer snapshot. Both boards increment hostTerm. +inline bool applyCountdownHostTransfer(CountdownWireState& state, + uint32_t newHostBoardId, + uint32_t expectedHostTerm) { + if (state.hostTerm != expectedHostTerm || + (newHostBoardId != state.hostBoardId && + newHostBoardId != state.guestBoardId)) { + return false; + } + if (newHostBoardId != state.hostBoardId) { + // Swap host and guest identity + uint32_t tmp = state.hostBoardId; + state.hostBoardId = state.guestBoardId; + state.guestBoardId = tmp; + int32_t tmpScore = state.hostScore; + state.hostScore = state.guestScore; + state.guestScore = tmpScore; + } + ++state.hostTerm; ++state.revision; return true; } @@ -163,7 +244,7 @@ inline bool applyCountdownExitSignal(CountdownWireState& current, return exitCountdownMatch(current, senderBoardId); } -static_assert(sizeof(CountdownWireState) == 32, +static_assert(sizeof(CountdownWireState) == 40, "CountdownWireState wire format changed"); static_assert(offsetof(CountdownWireState, gameId) == 0, "CountdownWireState gameId offset changed"); @@ -173,13 +254,19 @@ static_assert(offsetof(CountdownWireState, hostBoardId) == 8, "CountdownWireState hostBoardId offset changed"); static_assert(offsetof(CountdownWireState, guestBoardId) == 12, "CountdownWireState guestBoardId offset changed"); -static_assert(offsetof(CountdownWireState, hostScore) == 16, +static_assert(offsetof(CountdownWireState, chooserBoardId) == 16, + "CountdownWireState chooserBoardId offset changed"); +static_assert(offsetof(CountdownWireState, hostTerm) == 20, + "CountdownWireState hostTerm offset changed"); +static_assert(offsetof(CountdownWireState, hostScore) == 24, "CountdownWireState hostScore offset changed"); -static_assert(offsetof(CountdownWireState, guestScore) == 20, +static_assert(offsetof(CountdownWireState, guestScore) == 28, "CountdownWireState guestScore offset changed"); -static_assert(offsetof(CountdownWireState, roundNumber) == 24, +static_assert(offsetof(CountdownWireState, roundNumber) == 32, "CountdownWireState roundNumber offset changed"); -static_assert(offsetof(CountdownWireState, phase) == 28, +static_assert(offsetof(CountdownWireState, phase) == 36, "CountdownWireState phase offset changed"); -static_assert(offsetof(CountdownWireState, roundType) == 29, +static_assert(offsetof(CountdownWireState, roundType) == 37, "CountdownWireState roundType offset changed"); +static_assert(offsetof(CountdownWireState, roundSubPhase) == 38, + "CountdownWireState roundSubPhase offset changed"); diff --git a/include/protocol.h b/include/protocol.h index 808f626..9d1e137 100644 --- a/include/protocol.h +++ b/include/protocol.h @@ -3,6 +3,8 @@ #include #include +#include "flip7/countdown_engine.hpp" + #include "mastermind_logic.h" #include "puzzle_logic.h" #include "countdown_wire.h" @@ -47,6 +49,8 @@ enum class MessageType : uint8_t { CountdownFullState, CountdownAck, CountdownRequestState, + CountdownAction, // in-round event (PickLetter, PresentStep, etc.) + CountdownPuzzleDescriptor, // host sends versioned seed + checksum }; struct PacketHeader { @@ -131,6 +135,33 @@ struct CountdownStatePacket { CountdownWireState state; }; +// Full round state — used for letter sync during picking and reconciliation. +// letters[0..letterCount-1] holds the 9 drawn letters; rest are zero. +struct CountdownFullStatePacket { + PacketHeader header; + CountdownWireState wireState; + char letters[9]; + uint8_t letterCount; + uint8_t reserved[2]; // must be zero +}; + +// In-round event packet — lightweight peer-to-peer action transport. +// actionType is cast to flip7::countdown::CommandType. +// payload encoding is command-specific (see CommandType comments in engine). +struct CountdownActionPacket { + PacketHeader header; + uint8_t actionType; // flip7::countdown::CommandType + uint8_t payload[15]; // command-specific encoding +}; + +// Versioned puzzle descriptor — host sends after generating a round; +// guest verifies checksum before using the seeded content. +struct CountdownPuzzleDescriptorPacket { + PacketHeader header; + uint32_t gameId; + flip7::countdown::PuzzleDescriptor descriptor; +}; + struct CountdownAckPacket { PacketHeader header; uint32_t targetBoardId; @@ -165,8 +196,14 @@ static_assert(sizeof(GameAckPacket) == 40, "GameAckPacket wire format changed"); static_assert(sizeof(MastermindStateRequestPacket) == 32, "MastermindStateRequestPacket wire format changed"); -static_assert(sizeof(CountdownStatePacket) == 52, +static_assert(sizeof(CountdownStatePacket) == 60, "CountdownStatePacket wire format changed"); +static_assert(sizeof(CountdownFullStatePacket) == 72, + "CountdownFullStatePacket wire format changed"); +static_assert(sizeof(CountdownActionPacket) == 36, + "CountdownActionPacket wire format changed"); +static_assert(sizeof(CountdownPuzzleDescriptorPacket) == 36, + "CountdownPuzzleDescriptorPacket wire format changed"); static_assert(sizeof(CountdownAckPacket) == 40, "CountdownAckPacket wire format changed"); static_assert(sizeof(CountdownStateRequestPacket) == 32, diff --git a/src/main.cpp b/src/main.cpp index 56c721e..61d3a1e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -64,6 +64,34 @@ constexpr int16_t kCountdownNumbersX = 5; constexpr int16_t kCountdownLettersX = 115; constexpr int16_t kCountdownConundrumX = 225; +// In-round layout constants +constexpr int16_t kCdTimerCX = 280; // arc centre x (top-right corner) +constexpr int16_t kCdTimerCY = 26; +constexpr int16_t kCdTimerR = 22; +constexpr int16_t kCdTileY = 48; // tile strip top-left y +constexpr int16_t kCdTileH = 34; +constexpr int16_t kCdTileW = 46; +constexpr int16_t kCdTileGap = 4; +// Numpad (4 rows x 3 cols) — bottom half of screen +constexpr int16_t kCdPadX = 48; // left edge of pad +constexpr int16_t kCdPadY = 105; +constexpr int16_t kCdPadW = 70; +constexpr int16_t kCdPadH = 32; +constexpr int16_t kCdPadGap = 4; +// Operation buttons row +constexpr int16_t kCdOpY = 185; +constexpr int16_t kCdOpW = 58; +constexpr int16_t kCdOpH = 30; +// Large-count picker (0-4) for Numbers picking phase +constexpr int16_t kCdLcY = 120; +constexpr int16_t kCdLcW = 50; +constexpr int16_t kCdLcH = 44; +// Admin FORCE-END button (host only, stalemate fallback) +constexpr int16_t kCdForceEndX = 230; +constexpr int16_t kCdForceEndY = 210; +constexpr int16_t kCdForceEndW = 80; +constexpr int16_t kCdForceEndH = 24; + struct PuzzleLayout { int16_t x; int16_t y; @@ -196,6 +224,38 @@ bool countdownStateReady = false; bool countdownRequestStateSoon = false; bool countdownReconciliationPending = false; bool sendCountdownFullStateSoon = false; + +// Per-round UI state — local only, not transmitted. +struct NumUiState { + int32_t draftValue{0}; // numpad entry during ClaimEntry + bool claimSent{false}; // claim packet delivered to host + uint8_t largeCount{2}; // large-number count chosen in NumPicking +}; +NumUiState numUi{}; + +struct LetUiState { + uint8_t claimLength{0}; // digit claim (1-9) + bool claimSent{false}; + char word[10]{}; // word being built from tiles + uint8_t wordLen{0}; + bool presentationSent{false}; + bool verificationSent{false}; + bool verificationAnswer{false}; // YES=true / NO=false + bool showConsonants{false}; // toggle in LetPicking view + bool presenterIsHost{true}; // which board presents first +}; +LetUiState letUi{}; + +struct ConUiState { + uint8_t circleOrder[9]{0,1,2,3,4,5,6,7,8}; // display permutation + uint8_t selected[9]{}; // circleOrder indices tapped, in order + uint8_t selectedCount{0}; + bool showGoodTry{false}; + uint32_t goodTryMs{0}; +}; +ConUiState conUi{}; + +uint32_t roundPhaseStartMs = 0; // millis() when current sub-phase began bool touchWasDown = false; bool lastOnline = false; uint8_t selectedPeg = 0; @@ -761,109 +821,1133 @@ void renderMastermind(bool online, const MastermindState& state, display.drawString("GUESS", 257, 204, 4); } -void renderCountdown(bool online, const CountdownWireState& state) { +// --------------------------------------------------------------------------- +// Countdown render helpers +// --------------------------------------------------------------------------- + +// Shared header row: "ROUND N | HOST XX : GUEST XX | EXIT" +static void renderCountdownRoundHeader(const CountdownWireState& state, + bool online) { + display.fillRect(0, 0, 320, 36, TFT_NAVY); + display.setTextDatum(ML_DATUM); + display.setTextColor(TFT_LIGHTGREY, TFT_NAVY); + char rlabel[20]; + snprintf(rlabel, sizeof(rlabel), "RD %lu", + static_cast(state.roundNumber)); + display.drawString(rlabel, 6, 18, 2); + + display.setTextDatum(MC_DATUM); + display.setTextColor(TFT_WHITE, TFT_NAVY); + char scores[28]; + snprintf(scores, sizeof(scores), "HOST %d : %d GUEST", + state.hostScore, state.guestScore); + display.drawString(scores, 155, 18, 2); + + display.fillRoundRect(kExitX, kExitY, kExitWidth, kExitHeight, 5, + online ? TFT_RED : TFT_ORANGE); + display.drawRoundRect(kExitX, kExitY, kExitWidth, kExitHeight, 5, + TFT_WHITE); + display.setTextColor(TFT_WHITE, online ? TFT_RED : TFT_ORANGE); + display.drawString("EXIT", kExitX + kExitWidth / 2, + kExitY + kExitHeight / 2, 2); +} + +// Silent visual timer \u2014 procedural arc fallback (production will use MJPEG). +// Draws a ring from full to empty as elapsedMs grows toward durationMs. +static void renderCountdownAnimation(int16_t cx, int16_t cy, int16_t r, + uint32_t elapsedMs, uint32_t durationMs) { + // Background ring + display.drawCircle(cx, cy, r, TFT_DARKGREY); + display.drawCircle(cx, cy, r - 1, TFT_DARKGREY); + + float fraction = 1.0f - static_cast(elapsedMs) / + static_cast(durationMs); + if (fraction < 0.0f) fraction = 0.0f; + if (fraction > 1.0f) fraction = 1.0f; + + // Draw coloured arc (yellow > green; red when <20%) + uint16_t colour = (fraction > 0.2f) ? TFT_YELLOW : TFT_RED; + int segments = static_cast(fraction * 60.0f); // 60 = full circle + for (int i = 0; i < segments; ++i) { + float angle = static_cast(i) * 6.0f - 90.0f; // degrees + float rad = angle * 3.14159f / 180.0f; + int16_t px = cx + static_cast(r * cosf(rad)); + int16_t py = cy + static_cast(r * sinf(rad)); + display.drawPixel(px, py, colour); + px = cx + static_cast((r - 1) * cosf(rad)); + py = cy + static_cast((r - 1) * sinf(rad)); + display.drawPixel(px, py, colour); + } + + // Remaining seconds label inside the ring + uint32_t secsLeft = (durationMs > elapsedMs) + ? (durationMs - elapsedMs + 999) / 1000 + : 0; + display.setTextDatum(MC_DATUM); + display.setTextColor(colour, TFT_NAVY); + display.drawNumber(static_cast(secsLeft), cx, cy, 2); +} + +// Draw the 6 Numbers tiles in a horizontal strip. +static void renderCountdownTileStrip(const flip7::countdown::NumbersRoundProjection& proj, + uint16_t usedMask = 0) { + for (uint8_t i = 0; i < proj.tileCount; ++i) { + int16_t tx = static_cast(kCdTileGap + + i * (kCdTileW + kCdTileGap)); + bool used = (usedMask >> i) & 1u; + uint16_t bg = used ? TFT_DARKGREY : TFT_DARKCYAN; + display.fillRoundRect(tx, kCdTileY, kCdTileW, kCdTileH, 5, bg); + display.drawRoundRect(tx, kCdTileY, kCdTileW, kCdTileH, 5, TFT_WHITE); + display.setTextDatum(MC_DATUM); + display.setTextColor(TFT_WHITE, bg); + display.drawNumber(proj.tiles[i], tx + kCdTileW / 2, + kCdTileY + kCdTileH / 2, 2); + } +} + +// Draw 3x4 numpad for claim entry. Returns nothing; touch handler checks bounds. +static void renderCountdownNumpad(int32_t draftValue, bool submitted) { + // Digit grid: [7][8][9] / [4][5][6] / [1][2][3] / [0][CLR][SUBMIT] + const int digits[4][3] = {{7,8,9},{4,5,6},{1,2,3},{0,-1,-2}}; + for (int row = 0; row < 4; ++row) { + for (int col = 0; col < 3; ++col) { + int16_t bx = static_cast( + kCdPadX + col * (kCdPadW + kCdPadGap)); + int16_t by = static_cast( + kCdPadY + row * (kCdPadH + kCdPadGap)); + int d = digits[row][col]; + uint16_t bg = TFT_DARKGREY; + const char* label = ""; + char dbuf[4]; + if (d >= 0) { + bg = TFT_DARKCYAN; + snprintf(dbuf, sizeof(dbuf), "%d", d); + label = dbuf; + } else if (d == -1) { + bg = TFT_MAROON; + label = "CLR"; + } else { // SUBMIT + bg = submitted ? TFT_DARKGREY : TFT_DARKGREEN; + label = "OK"; + } + display.fillRoundRect(bx, by, kCdPadW, kCdPadH, 5, bg); + display.drawRoundRect(bx, by, kCdPadW, kCdPadH, 5, TFT_WHITE); + display.setTextDatum(MC_DATUM); + display.setTextColor(TFT_WHITE, bg); + display.drawString(label, bx + kCdPadW / 2, by + kCdPadH / 2, 2); + } + } + // Draft value display above numpad + display.fillRect(kCdPadX, kCdPadY - 36, 3 * kCdPadW + 2 * kCdPadGap, 32, + TFT_BLACK); + display.drawRect(kCdPadX, kCdPadY - 36, 3 * kCdPadW + 2 * kCdPadGap, 32, + TFT_WHITE); + display.setTextDatum(MR_DATUM); + display.setTextColor(TFT_YELLOW, TFT_BLACK); + char dvbuf[8]; + snprintf(dvbuf, sizeof(dvbuf), "%ld", static_cast(draftValue)); + display.drawString(dvbuf, + kCdPadX + 3 * kCdPadW + 2 * kCdPadGap - 4, + kCdPadY - 20, 4); +} + +// --------------------------------------------------------------------------- +// Round intro screen (Intro sub-phase) \u2014 all round types +// --------------------------------------------------------------------------- +static void renderCountdownIntro(const CountdownWireState& state, + uint32_t elapsedMs) { + display.fillScreen(TFT_BLACK); + display.setTextDatum(MC_DATUM); + + const char* title = "COUNTDOWN"; + uint16_t titleColour = TFT_WHITE; + if (state.roundType == static_cast(flip7::countdown::RoundType::Numbers)) { + title = "NUMBERS ROUND"; + titleColour = TFT_CYAN; + } else if (state.roundType == static_cast(flip7::countdown::RoundType::Letters)) { + title = "LETTERS ROUND"; + titleColour = TFT_YELLOW; + } else if (state.roundType == static_cast(flip7::countdown::RoundType::Conundrum)) { + title = "CONUNDRUM ROUND"; + titleColour = TFT_MAGENTA; + } + + display.setTextColor(titleColour, TFT_BLACK); + display.drawString(title, 160, 75, 4); + display.setTextColor(TFT_LIGHTGREY, TFT_BLACK); + display.drawString("Get ready...", 160, 130, 2); + + renderCountdownAnimation(160, 175, 30, elapsedMs, 5000); +} + +// --------------------------------------------------------------------------- +// Numbers sub-phase renders +// --------------------------------------------------------------------------- + +static void renderCountdownNumPicking(const CountdownWireState& state, + uint8_t selectedLarge) { + display.fillScreen(TFT_BLACK); + renderCountdownRoundHeader(state, false); + + display.setTextDatum(MC_DATUM); + display.setTextColor(TFT_CYAN, TFT_BLACK); + display.drawString("HOW MANY LARGE NUMBERS?", 160, 55, 2); + display.setTextColor(TFT_LIGHTGREY, TFT_BLACK); + display.drawString("Large: 25 50 75 100 Small: 1-10", 160, 75, 2); + + // 5 buttons: 0 1 2 3 4 + for (uint8_t i = 0; i <= 4; ++i) { + int16_t bx = static_cast(16 + i * 58); + uint16_t bg = (i == selectedLarge) ? TFT_CYAN : TFT_DARKGREY; + uint16_t fg = (i == selectedLarge) ? TFT_BLACK : TFT_WHITE; + display.fillRoundRect(bx, kCdLcY, kCdLcW, kCdLcH, 8, bg); + display.drawRoundRect(bx, kCdLcY, kCdLcW, kCdLcH, 8, TFT_WHITE); + display.setTextColor(fg, bg); + display.drawNumber(i, bx + kCdLcW / 2, kCdLcY + kCdLcH / 2, 4); + } + + if (boardId == state.chooserBoardId) { + display.fillRoundRect(95, 185, 130, 36, 8, TFT_DARKGREEN); + display.drawRoundRect(95, 185, 130, 36, 8, TFT_WHITE); + display.setTextColor(TFT_WHITE, TFT_DARKGREEN); + display.drawString("CONFIRM", 160, 203, 4); + } else { + display.setTextColor(TFT_YELLOW, TFT_BLACK); + display.drawString("Waiting for chooser...", 160, 200, 2); + } +} + +static void renderCountdownNumThinking(const CountdownWireState& state, + const flip7::countdown::NumbersRoundProjection& proj, + uint32_t elapsedMs) { + display.fillScreen(TFT_BLACK); + renderCountdownRoundHeader(state, false); + renderCountdownTileStrip(proj); + + display.setTextDatum(MC_DATUM); + display.setTextColor(TFT_CYAN, TFT_BLACK); + display.drawString("TARGET", 130, 100, 2); + display.setTextColor(TFT_WHITE, TFT_BLACK); + display.drawNumber(static_cast(proj.target), 130, 135, 6); + + display.setTextColor(TFT_LIGHTGREY, TFT_BLACK); + display.drawString("Calculate on paper", 130, 190, 2); + + renderCountdownAnimation(kCdTimerCX, kCdTimerCY, kCdTimerR, + elapsedMs, 30000); +} + +static void renderCountdownNumClaimEntry(const CountdownWireState& state, + const flip7::countdown::NumbersRoundProjection& proj, + int32_t draftValue, bool claimSent) { + display.fillScreen(TFT_BLACK); + renderCountdownRoundHeader(state, false); + renderCountdownTileStrip(proj); + + display.setTextDatum(MC_DATUM); + display.setTextColor(TFT_CYAN, TFT_BLACK); + display.drawString("TARGET", 75, 100, 2); + display.setTextColor(TFT_WHITE, TFT_BLACK); + display.drawNumber(static_cast(proj.target), 75, 130, 4); + + if (claimSent) { + display.setTextColor(TFT_GREEN, TFT_BLACK); + display.drawString("Claim sent!", 75, 165, 2); + display.drawString("Waiting for opponent...", 75, 185, 2); + } else { + display.setTextColor(TFT_YELLOW, TFT_BLACK); + display.drawString("Your answer:", 75, 100 + 3, 2); + renderCountdownNumpad(draftValue, claimSent); + } +} + +static void renderCountdownNumClaimReveal(const CountdownWireState& state, + const flip7::countdown::NumbersRoundProjection& proj, + int32_t hostClaim, int32_t guestClaim) { + display.fillScreen(TFT_BLACK); + renderCountdownRoundHeader(state, false); + + display.setTextDatum(MC_DATUM); + display.setTextColor(TFT_CYAN, TFT_BLACK); + display.drawString("CLAIMS REVEALED", 160, 52, 4); + + display.setTextColor(TFT_WHITE, TFT_BLACK); + display.drawString("TARGET", 160, 90, 2); + display.drawNumber(static_cast(proj.target), 160, 115, 4); + + display.setTextColor(TFT_YELLOW, TFT_BLACK); + char hbuf[20], gbuf[20]; + snprintf(hbuf, sizeof(hbuf), "HOST: %ld", static_cast(hostClaim)); + snprintf(gbuf, sizeof(gbuf), "GUEST: %ld", static_cast(guestClaim)); + display.drawString(hbuf, 160, 155, 4); + display.drawString(gbuf, 160, 190, 4); +} + +static void renderCountdownNumPresenting(const CountdownWireState& state, + const flip7::countdown::NumbersRoundProjection& proj, + bool isPresenter) { + display.fillScreen(TFT_BLACK); + renderCountdownRoundHeader(state, false); + renderCountdownTileStrip(proj); + + display.setTextDatum(MC_DATUM); + + if (!isPresenter) { + display.setTextColor(TFT_YELLOW, TFT_BLACK); + display.drawString("Opponent is presenting...", 160, 130, 2); + return; + } + + display.setTextColor(TFT_CYAN, TFT_BLACK); + display.drawString("TARGET", 160, 96, 2); + display.setTextColor(TFT_WHITE, TFT_BLACK); + display.drawNumber(static_cast(proj.target), 160, 120, 4); + + // Operation buttons + const char* ops[4] = {"+", "-", "x", "/"}; + for (int i = 0; i < 4; ++i) { + int16_t bx = static_cast(6 + i * (kCdOpW + 6)); + display.fillRoundRect(bx, kCdOpY, kCdOpW, kCdOpH, 6, TFT_DARKGREY); + display.drawRoundRect(bx, kCdOpY, kCdOpW, kCdOpH, 6, TFT_WHITE); + display.setTextColor(TFT_WHITE, TFT_DARKGREY); + display.drawString(ops[i], bx + kCdOpW / 2, kCdOpY + kCdOpH / 2, 4); + } + + // UNDO, END buttons + display.fillRoundRect(256, kCdOpY, 58, kCdOpH, 6, TFT_MAROON); + display.setTextColor(TFT_WHITE, TFT_MAROON); + display.drawString("UNDO", 285, kCdOpY + kCdOpH / 2, 2); + + display.fillRoundRect(256, kCdOpY + kCdOpH + 6, 58, kCdOpH, 6, + TFT_DARKGREEN); + display.setTextColor(TFT_WHITE, TFT_DARKGREEN); + display.drawString("END", 285, kCdOpY + kCdOpH + 6 + kCdOpH / 2, 2); +} + +// --------------------------------------------------------------------------- +// BetweenRounds scoreboard +// --------------------------------------------------------------------------- +static void renderCountdownBetweenRounds(bool online, + const CountdownWireState& state) { display.fillScreen(TFT_NAVY); display.setTextDatum(MC_DATUM); display.setTextColor(TFT_WHITE, TFT_NAVY); renderExitButton(online); - if (state.phase == CountdownWirePhase::BetweenRounds) { - display.drawString("CHOOSE NEXT ROUND", 145, 20, 4); - display.setTextColor(TFT_YELLOW, TFT_NAVY); - display.drawString(boardId == state.hostBoardId - ? "HOST: PICK A ROUND TYPE" - : "WAITING FOR HOST TO PICK", - 160, 55, 2); - - if (boardId == state.hostBoardId) { - display.fillRoundRect(kCountdownNumbersX, kCountdownRoundTypeY, - kCountdownRoundTypeWidth, - kCountdownRoundTypeHeight, 8, TFT_DARKCYAN); - display.drawRoundRect(kCountdownNumbersX, kCountdownRoundTypeY, - kCountdownRoundTypeWidth, - kCountdownRoundTypeHeight, 8, TFT_WHITE); - display.setTextColor(TFT_WHITE, TFT_DARKCYAN); - display.drawString("NUMBERS", - kCountdownNumbersX + kCountdownRoundTypeWidth / 2, - kCountdownRoundTypeY + - kCountdownRoundTypeHeight / 2, - 2); - - display.fillRoundRect(kCountdownLettersX, kCountdownRoundTypeY, - kCountdownRoundTypeWidth, - kCountdownRoundTypeHeight, 8, TFT_MAGENTA); - display.drawRoundRect(kCountdownLettersX, kCountdownRoundTypeY, - kCountdownRoundTypeWidth, - kCountdownRoundTypeHeight, 8, TFT_WHITE); - display.setTextColor(TFT_WHITE, TFT_MAGENTA); - display.drawString("LETTERS", - kCountdownLettersX + kCountdownRoundTypeWidth / 2, - kCountdownRoundTypeY + - kCountdownRoundTypeHeight / 2, - 2); - - display.fillRoundRect(kCountdownConundrumX, kCountdownRoundTypeY, - kCountdownRoundTypeWidth, - kCountdownRoundTypeHeight, 8, TFT_DARKGREEN); - display.drawRoundRect(kCountdownConundrumX, kCountdownRoundTypeY, - kCountdownRoundTypeWidth, - kCountdownRoundTypeHeight, 8, TFT_WHITE); + display.drawString("CHOOSE NEXT ROUND", 145, 20, 4); + + // Show both roles when they differ + bool isChooser = (boardId == state.chooserBoardId); + bool isHost = (boardId == state.hostBoardId); + display.setTextColor(TFT_YELLOW, TFT_NAVY); + if (isChooser) { + display.drawString("YOUR TURN TO CHOOSE", 160, 55, 2); + } else if (isHost) { + display.drawString("WAITING FOR PEER TO CHOOSE", 160, 55, 2); + } else { + display.drawString("WAITING FOR CHOOSER", 160, 55, 2); + } + + if (isChooser) { + // NUMBERS button + display.fillRoundRect(kCountdownNumbersX, kCountdownRoundTypeY, + kCountdownRoundTypeWidth, kCountdownRoundTypeHeight, + 8, TFT_DARKCYAN); + display.drawRoundRect(kCountdownNumbersX, kCountdownRoundTypeY, + kCountdownRoundTypeWidth, kCountdownRoundTypeHeight, + 8, TFT_WHITE); + display.setTextColor(TFT_WHITE, TFT_DARKCYAN); + display.drawString("NUMBERS", + kCountdownNumbersX + kCountdownRoundTypeWidth / 2, + kCountdownRoundTypeY + kCountdownRoundTypeHeight / 2, + 2); + // LETTERS button + display.fillRoundRect(kCountdownLettersX, kCountdownRoundTypeY, + kCountdownRoundTypeWidth, kCountdownRoundTypeHeight, + 8, TFT_YELLOW); + display.drawRoundRect(kCountdownLettersX, kCountdownRoundTypeY, + kCountdownRoundTypeWidth, kCountdownRoundTypeHeight, + 8, TFT_WHITE); + display.setTextColor(TFT_BLACK, TFT_YELLOW); + display.drawString("LETTERS", + kCountdownLettersX + kCountdownRoundTypeWidth / 2, + kCountdownRoundTypeY + kCountdownRoundTypeHeight / 2, + 2); + // CONUNDRUM button + display.fillRoundRect(kCountdownConundrumX, kCountdownRoundTypeY, + kCountdownRoundTypeWidth, kCountdownRoundTypeHeight, + 8, TFT_MAGENTA); + display.drawRoundRect(kCountdownConundrumX, kCountdownRoundTypeY, + kCountdownRoundTypeWidth, kCountdownRoundTypeHeight, + 8, TFT_WHITE); + display.setTextColor(TFT_WHITE, TFT_MAGENTA); + display.drawString("CONUNDRUM", + kCountdownConundrumX + kCountdownRoundTypeWidth / 2, + kCountdownRoundTypeY + kCountdownRoundTypeHeight / 2, + 2); + } + + // Scores + round number + display.setTextColor(TFT_WHITE, TFT_NAVY); + char hostLabel[20], guestLabel[20]; + snprintf(hostLabel, sizeof(hostLabel), "HOST %d", state.hostScore); + snprintf(guestLabel, sizeof(guestLabel), "GUEST %d", state.guestScore); + display.drawString(hostLabel, 80, 185, 4); + display.drawString(guestLabel, 240, 185, 4); + + char roundLabel[24]; + snprintf(roundLabel, sizeof(roundLabel), "Round %lu", + static_cast(state.roundNumber)); + display.drawString(roundLabel, 160, 220, 2); +} + +// --------------------------------------------------------------------------- +// Main renderCountdown dispatcher +// --------------------------------------------------------------------------- +// Forward declaration — renderCountdownLetConPhases is defined after the helpers. +static void renderCountdownLetConPhases( + bool online, const CountdownWireState& state, + CountdownRoundSubPhase subPhase, uint32_t elapsed); + +void renderCountdown(bool online, const CountdownWireState& state) { + using SubPhase = CountdownRoundSubPhase; + auto subPhase = static_cast(state.roundSubPhase); + uint32_t now = millis(); + uint32_t elapsed = (now >= roundPhaseStartMs) + ? now - roundPhaseStartMs + : 0; + + if (state.phase == CountdownWirePhase::BetweenRounds || + state.phase == CountdownWirePhase::Setup) { + renderCountdownBetweenRounds(online, state); + return; + } + + // Intro sub-phase \u2014 common to all round types + if (subPhase == SubPhase::Intro) { + renderCountdownIntro(state, elapsed); + return; + } + + // Numbers sub-phases + if (subPhase == SubPhase::NumPicking) { + renderCountdownNumPicking(state, numUi.largeCount); + return; + } + if (subPhase == SubPhase::NumThinking) { + flip7::countdown::NumbersRoundProjection proj{}; + portENTER_CRITICAL(&gameMux); + proj = countdownEngine.numbersProjection(); + portEXIT_CRITICAL(&gameMux); + renderCountdownNumThinking(state, proj, elapsed); + return; + } + if (subPhase == SubPhase::NumClaimEntry) { + flip7::countdown::NumbersRoundProjection proj{}; + portENTER_CRITICAL(&gameMux); + proj = countdownEngine.numbersProjection(); + portEXIT_CRITICAL(&gameMux); + renderCountdownNumClaimEntry(state, proj, numUi.draftValue, + numUi.claimSent); + return; + } + if (subPhase == SubPhase::NumClaimReveal) { + flip7::countdown::NumbersRoundProjection proj{}; + int32_t hClaim = 0, gClaim = 0; + portENTER_CRITICAL(&gameMux); + proj = countdownEngine.numbersProjection(); + auto* nr = countdownEngine.numbersRound(); + if (nr) { + hClaim = nr->claimFor(state.hostBoardId); + gClaim = nr->claimFor(state.guestBoardId); + } + portEXIT_CRITICAL(&gameMux); + renderCountdownNumClaimReveal(state, proj, hClaim, gClaim); + return; + } + if (subPhase == SubPhase::NumPresentPlayerA || + subPhase == SubPhase::NumPresentPlayerB) { + flip7::countdown::NumbersRoundProjection proj{}; + portENTER_CRITICAL(&gameMux); + proj = countdownEngine.numbersProjection(); + portEXIT_CRITICAL(&gameMux); + // Determine if this board is the active presenter + // (simple heuristic: PlayerA = host, PlayerB = guest for now) + bool isPresenterA = (boardId == state.hostBoardId); + bool isActive = (subPhase == SubPhase::NumPresentPlayerA && isPresenterA) || + (subPhase == SubPhase::NumPresentPlayerB && !isPresenterA); + renderCountdownNumPresenting(state, proj, isActive); + return; + } + if (subPhase == SubPhase::NumResult) { + display.fillScreen(TFT_BLACK); + renderCountdownRoundHeader(state, online); + display.setTextDatum(MC_DATUM); + display.setTextColor(TFT_GREEN, TFT_BLACK); + display.drawString("ROUND COMPLETE", 160, 120, 4); + display.setTextColor(TFT_LIGHTGREY, TFT_BLACK); + display.drawString("Next round starting...", 160, 175, 2); + return; + } + + // Letters and Conundrum phases — defined after helpers below. + renderCountdownLetConPhases(online, state, subPhase, elapsed); +} + +// --------------------------------------------------------------------------- +// Letters render helpers +// --------------------------------------------------------------------------- + +// 9-letter strip reused across LetThinking, LetPresenting, LetResult. +// tappable=true draws each tile as a pressable button. +// usedMask: bit N set = tile N dimmed (already placed in word). +static void renderLetterStrip(const char* letters, uint8_t count, + bool tappable = false, + uint16_t usedMask = 0) { + constexpr int16_t W = 30, H = 34, GAP = 4; + int16_t startX = static_cast((320 - count * (W + GAP) + GAP) / 2); + for (uint8_t i = 0; i < count; ++i) { + int16_t tx = static_cast(startX + i * (W + GAP)); + bool used = tappable && ((usedMask >> i) & 1u); + uint16_t bg = used ? TFT_DARKGREY + : (tappable ? TFT_DARKCYAN : 0x1082 /*dark navy*/); + display.fillRoundRect(tx, kCdTileY, W, H, 5, bg); + display.drawRoundRect(tx, kCdTileY, W, H, 5, TFT_WHITE); + display.setTextDatum(MC_DATUM); + display.setTextColor(TFT_WHITE, bg); + char s[2] = {letters[i], '\0'}; + display.drawString(s, tx + W / 2, kCdTileY + H / 2, 2); + } +} + +// Vowel button row (A E I O U) for LetPicking. +static void renderVowelButtons(uint8_t vowelCount) { + constexpr int16_t W = 50, H = 44, Y = 95; + const char vowels[5] = {'A', 'E', 'I', 'O', 'U'}; + for (int i = 0; i < 5; ++i) { + int16_t bx = static_cast(10 + i * 60); + uint16_t bg = (vowelCount >= 5) ? TFT_DARKGREY : TFT_DARKCYAN; + display.fillRoundRect(bx, Y, W, H, 8, bg); + display.drawRoundRect(bx, Y, W, H, 8, TFT_WHITE); + display.setTextDatum(MC_DATUM); + display.setTextColor(TFT_WHITE, bg); + char s[2] = {vowels[i], '\0'}; + display.drawString(s, bx + W / 2, Y + H / 2, 4); + } +} + +// Consonant keyboard (4 rows) for LetPicking. +static void renderConsonantKeyboard(uint8_t consonantCount) { + // BCDFGH / JKLMNP / QRSTVW / XYZ + const char rows[4][7] = { + {'B','C','D','F','G','H', 0}, + {'J','K','L','M','N','P', 0}, + {'Q','R','S','T','V','W', 0}, + {'X','Y','Z', 0, 0, 0, 0}, + }; + const int rowLen[4] = {6, 6, 6, 3}; + constexpr int16_t W = 42, H = 32, GAP = 4; + bool maxed = (consonantCount >= 6); + for (int row = 0; row < 4; ++row) { + int16_t startX = static_cast( + (320 - rowLen[row] * (W + GAP) + GAP) / 2); + int16_t by = static_cast(88 + row * (H + GAP)); + for (int col = 0; col < rowLen[row]; ++col) { + int16_t bx = static_cast(startX + col * (W + GAP)); + uint16_t bg = maxed ? TFT_DARKGREY : TFT_DARKGREY; + // Consonants always dark-grey unless enabled — keep simple + bg = maxed ? 0x2104 /*very dark*/ : TFT_DARKGREY; + display.fillRoundRect(bx, by, W, H, 5, bg); + display.drawRoundRect(bx, by, W, H, 5, TFT_WHITE); + display.setTextDatum(MC_DATUM); + display.setTextColor(TFT_WHITE, bg); + char s[2] = {rows[row][col], '\0'}; + display.drawString(s, bx + W / 2, by + H / 2, 2); + } + } +} + +static void renderCountdownLetPicking(const CountdownWireState& state, + const flip7::countdown::LettersRoundProjection& proj, + bool showConsonants) { + display.fillScreen(TFT_BLACK); + renderCountdownRoundHeader(state, false); + + bool isChooser = (boardId == state.chooserBoardId); + + // Drawn-letters progress strip + if (proj.letterCount > 0) + renderLetterStrip(proj.letters, proj.letterCount); + + display.setTextDatum(MC_DATUM); + if (!isChooser) { + display.setTextColor(TFT_YELLOW, TFT_BLACK); + display.drawString("Waiting for chooser...", 160, 130, 2); + return; + } + + // Toggle tabs: [VOWELS] [CONSONANTS] + uint16_t vbg = showConsonants ? TFT_DARKGREY : TFT_DARKCYAN; + uint16_t cbg = showConsonants ? TFT_DARKGREY : TFT_DARKGREY; + // Actually light up the active tab + vbg = !showConsonants ? TFT_DARKCYAN : TFT_DARKGREY; + cbg = showConsonants ? TFT_DARKGREY : TFT_DARKGREY; + display.fillRoundRect(5, 50, 100, 28, 6, vbg); + display.setTextColor(TFT_WHITE, vbg); + display.drawString("VOWELS", 55, 64, 2); + display.fillRoundRect(215, 50, 100, 28, 6, cbg); + display.setTextColor(TFT_WHITE, cbg); + display.drawString("CONSONANTS", 265, 64, 2); + + // Progress label + char prog[36]; + snprintf(prog, sizeof(prog), "V:%u/4 C:%u/5 Total:%u/9", + static_cast(proj.letterCount), // rough + 0u, static_cast(proj.letterCount)); + display.setTextColor(TFT_LIGHTGREY, TFT_BLACK); + display.drawString(prog, 160, 83, 2); + + if (!showConsonants) { + // Count vowels in drawn set + uint8_t vc = 0; + for (uint8_t i = 0; i < proj.letterCount; ++i) { + char c = proj.letters[i]; + if (c=='A'||c=='E'||c=='I'||c=='O'||c=='U') ++vc; + } + renderVowelButtons(vc); + } else { + uint8_t cc = 0; + for (uint8_t i = 0; i < proj.letterCount; ++i) { + char c = proj.letters[i]; + if (c!='A'&&c!='E'&&c!='I'&&c!='O'&&c!='U') ++cc; + } + renderConsonantKeyboard(cc); + } + + if (proj.letterCount >= 9) { + display.fillRoundRect(95, 200, 130, 34, 8, TFT_DARKGREEN); + display.setTextColor(TFT_WHITE, TFT_DARKGREEN); + display.drawString("LOCK IN", 160, 217, 4); + } +} + +static void renderCountdownLetThinking(const CountdownWireState& state, + const flip7::countdown::LettersRoundProjection& proj, + uint32_t elapsedMs) { + display.fillScreen(TFT_BLACK); + renderCountdownRoundHeader(state, false); + renderLetterStrip(proj.letters, proj.letterCount); + + display.setTextDatum(MC_DATUM); + display.setTextColor(TFT_YELLOW, TFT_BLACK); + display.drawString("Make the longest word you can", 160, 100, 2); + display.setTextColor(TFT_LIGHTGREY, TFT_BLACK); + display.drawString("Your time starts now", 160, 120, 2); + + renderCountdownAnimation(kCdTimerCX, kCdTimerCY, kCdTimerR, + elapsedMs, 30000); +} + +static void renderCountdownLetClaimEntry(const CountdownWireState& state, + const flip7::countdown::LettersRoundProjection& proj, + uint8_t selectedLen, bool sent) { + display.fillScreen(TFT_BLACK); + renderCountdownRoundHeader(state, false); + renderLetterStrip(proj.letters, proj.letterCount); + + display.setTextDatum(MC_DATUM); + display.setTextColor(TFT_YELLOW, TFT_BLACK); + display.drawString("Longest word you found:", 160, 95, 2); + display.setTextColor(TFT_LIGHTGREY, TFT_BLACK); + display.drawString("Keep your claim private", 160, 113, 2); + + if (sent) { + display.setTextColor(TFT_GREEN, TFT_BLACK); + display.drawString("Claim sent!", 160, 155, 4); + display.setTextColor(TFT_LIGHTGREY, TFT_BLACK); + display.drawString("Waiting for opponent...", 160, 190, 2); + return; + } + + // 9 digit buttons (1-9) in a row + constexpr int16_t DW = 28, DH = 34, DY = 130, DGAP = 3; + int16_t startX = static_cast((320 - 9*(DW+DGAP)+DGAP) / 2); + for (int d = 1; d <= 9; ++d) { + int16_t bx = static_cast(startX + (d-1)*(DW+DGAP)); + uint16_t bg = (selectedLen == d) ? TFT_CYAN : TFT_DARKGREY; + uint16_t fg = (selectedLen == d) ? TFT_BLACK : TFT_WHITE; + display.fillRoundRect(bx, DY, DW, DH, 5, bg); + display.drawRoundRect(bx, DY, DW, DH, 5, TFT_WHITE); + display.setTextDatum(MC_DATUM); + display.setTextColor(fg, bg); + display.drawNumber(d, bx + DW/2, DY + DH/2, 2); + } + + uint16_t sbg = (selectedLen > 0) ? TFT_DARKGREEN : TFT_DARKGREY; + display.fillRoundRect(95, 180, 130, 34, 8, sbg); + display.drawRoundRect(95, 180, 130, 34, 8, TFT_WHITE); + display.setTextColor(TFT_WHITE, sbg); + display.drawString("SUBMIT CLAIM", 160, 197, 2); +} + +static void renderCountdownLetClaimReveal(const CountdownWireState& state, + const flip7::countdown::LettersRoundProjection& proj, + uint8_t hostClaim, uint8_t guestClaim) { + display.fillScreen(TFT_BLACK); + renderCountdownRoundHeader(state, false); + renderLetterStrip(proj.letters, proj.letterCount); + + display.setTextDatum(MC_DATUM); + display.setTextColor(TFT_CYAN, TFT_BLACK); + display.drawString("CLAIMS REVEALED", 160, 58, 4); + display.setTextColor(TFT_WHITE, TFT_BLACK); + char hbuf[24], gbuf[24]; + snprintf(hbuf, sizeof(hbuf), "HOST: %u letters", hostClaim); + snprintf(gbuf, sizeof(gbuf), "GUEST: %u letters", guestClaim); + display.drawString(hbuf, 160, 120, 4); + display.drawString(gbuf, 160, 158, 4); + + uint8_t bigger = (hostClaim >= guestClaim) ? hostClaim : guestClaim; + bool hostFirst = (hostClaim >= guestClaim); + display.setTextColor(TFT_YELLOW, TFT_BLACK); + char who[24]; + snprintf(who, sizeof(who), "%s presents first", + hostFirst ? "HOST" : "GUEST"); + display.drawString(who, 160, 195, 2); + (void)bigger; +} + +static void renderCountdownLetPresenting(const CountdownWireState& state, + const flip7::countdown::LettersRoundProjection& proj, + bool isPresenter, + const char* word, uint8_t wordLen, + bool sent) { + display.fillScreen(TFT_BLACK); + renderCountdownRoundHeader(state, false); + + // Build used-letter bitmask: count letters in word vs available + uint16_t usedMask = 0; + if (isPresenter && wordLen > 0) { + char pool[10]{}; + memcpy(pool, proj.letters, proj.letterCount); + for (uint8_t wi = 0; wi < wordLen; ++wi) { + for (uint8_t ti = 0; ti < proj.letterCount; ++ti) { + if (!(usedMask >> ti & 1u) && pool[ti] == word[wi]) { + usedMask |= (1u << ti); + break; + } + } + } + } + renderLetterStrip(proj.letters, proj.letterCount, isPresenter && !sent, + usedMask); + + display.setTextDatum(MC_DATUM); + if (!isPresenter) { + display.setTextColor(TFT_YELLOW, TFT_BLACK); + display.drawString("Opponent is presenting...", 160, 110, 2); + if (wordLen > 0) { + // Show word being built in real-time (updated via wire) + char wbuf[12]{}; + memcpy(wbuf, word, wordLen); + display.setTextColor(TFT_WHITE, TFT_BLACK); + display.drawString(wbuf, 160, 150, 4); + } + return; + } + + // Word strip (building) + char wbuf[12]{}; + memcpy(wbuf, word, wordLen); + display.fillRect(0, 95, 320, 40, TFT_BLACK); + display.drawRect(10, 98, 230, 34, TFT_DARKGREY); + display.setTextColor(TFT_WHITE, TFT_BLACK); + display.setTextDatum(ML_DATUM); + display.drawString(wordLen > 0 ? wbuf : "_", 18, 115, 4); + display.setTextDatum(MC_DATUM); + + if (!sent) { + // CLEAR and PRESENT buttons + display.fillRoundRect(250, 98, 64, 34, 6, TFT_MAROON); + display.setTextColor(TFT_WHITE, TFT_MAROON); + display.drawString("CLR", 282, 115, 2); + + uint16_t pbg = (wordLen > 0) ? TFT_DARKGREEN : TFT_DARKGREY; + display.fillRoundRect(95, 155, 130, 34, 8, pbg); + display.drawRoundRect(95, 155, 130, 34, 8, TFT_WHITE); + display.setTextColor(TFT_WHITE, pbg); + display.drawString("PRESENT WORD", 160, 172, 2); + } else { + display.setTextColor(TFT_GREEN, TFT_BLACK); + display.drawString("Waiting for verification...", 160, 175, 2); + } +} + +static void renderCountdownLetVerification(const CountdownWireState& state, + const char* word, uint8_t wordLen, + bool isVerifier, bool sent) { + display.fillScreen(TFT_BLACK); + renderCountdownRoundHeader(state, false); + + display.setTextDatum(MC_DATUM); + display.setTextColor(TFT_CYAN, TFT_BLACK); + display.drawString(isVerifier ? "VERIFY WORD" : "Waiting for verification...", + 160, 55, 4); + + // Show the word letter by letter in boxes + if (wordLen > 0) { + constexpr int16_t W = 30, H = 38, GAP = 4; + int16_t startX = static_cast( + (320 - wordLen * (W + GAP) + GAP) / 2); + display.setTextColor(TFT_WHITE, 0x18C3 /*dark teal*/); + for (uint8_t i = 0; i < wordLen; ++i) { + int16_t bx = static_cast(startX + i * (W + GAP)); + display.fillRect(bx, 100, W, H, 0x18C3); + display.drawRect(bx, 100, W, H, TFT_WHITE); + char s[2] = {word[i], '\0'}; + display.drawString(s, bx + W/2, 100 + H/2, 2); + } + } + + if (isVerifier && !sent) { + display.setTextColor(TFT_LIGHTGREY, TFT_BLACK); + display.drawString("Is this in the dictionary?", 160, 158, 2); + + display.fillRoundRect(20, 178, 120, 44, 10, TFT_DARKGREEN); + display.drawRoundRect(20, 178, 120, 44, 10, TFT_WHITE); + display.setTextColor(TFT_WHITE, TFT_DARKGREEN); + display.drawString("YES", 80, 200, 4); + + display.fillRoundRect(180, 178, 120, 44, 10, TFT_MAROON); + display.drawRoundRect(180, 178, 120, 44, 10, TFT_WHITE); + display.setTextColor(TFT_WHITE, TFT_MAROON); + display.drawString("NO", 240, 200, 4); + } +} + +static void renderCountdownLetResult(const CountdownWireState& state, + const flip7::countdown::LettersRoundProjection& proj, + bool accepted, const char* word, + uint8_t wordLen, int32_t points, + bool morePresentersComing) { + display.fillScreen(TFT_BLACK); + renderCountdownRoundHeader(state, false); + renderLetterStrip(proj.letters, proj.letterCount); + + display.setTextDatum(MC_DATUM); + if (accepted) { + display.setTextColor(TFT_GREEN, TFT_BLACK); + display.drawString("ACCEPTED!", 160, 65, 4); + char wbuf[12]{}; + memcpy(wbuf, word, wordLen); + char detail[32]; + snprintf(detail, sizeof(detail), "%s \u2014 %u letters", wbuf, + static_cast(wordLen)); + display.setTextColor(TFT_WHITE, TFT_BLACK); + display.drawString(detail, 160, 115, 2); + char ptsbuf[16]; + snprintf(ptsbuf, sizeof(ptsbuf), "+%ld POINTS", + static_cast(points)); + display.setTextColor(TFT_YELLOW, TFT_BLACK); + display.drawString(ptsbuf, 160, 145, 4); + } else { + display.setTextColor(TFT_RED, TFT_BLACK); + display.drawString("REJECTED", 160, 90, 4); + display.setTextColor(TFT_LIGHTGREY, TFT_BLACK); + display.drawString("0 POINTS", 160, 145, 4); + } + if (morePresentersComing) { + display.setTextColor(TFT_CYAN, TFT_BLACK); + display.drawString("Next up: opponent", 160, 198, 2); + } + (void)proj; +} + +// --------------------------------------------------------------------------- +// Conundrum render helpers +// --------------------------------------------------------------------------- + +// Compute the pixel centre of the i-th letter in the circle. +// Uses integer maths; accurate enough for a 320x240 touch screen. +static void conCirclePos(uint8_t idx, uint8_t circleOrder[9], + int16_t cx, int16_t cy, int16_t r, + int16_t& outX, int16_t& outY) { + // Angles: 0° = top, clockwise. i * 40°. + int32_t deg = static_cast(circleOrder[idx]) * 40 - 90; + float rad = deg * 3.14159f / 180.0f; + outX = static_cast(cx + static_cast(r * cosf(rad))); + outY = static_cast(cy + static_cast(r * sinf(rad))); +} + +static void renderCountdownConActive(const CountdownWireState& state, + const flip7::countdown::ConundrumRoundProjection& proj, + const ConUiState& con, + uint32_t elapsedMs) { + display.fillScreen(TFT_BLACK); + renderCountdownRoundHeader(state, false); + + constexpr int16_t CX = 160, CY = 125, CR = 75; + constexpr int16_t TW = 26, TH = 26; + + // Hint + display.setTextDatum(MC_DATUM); + display.setTextColor(TFT_LIGHTGREY, TFT_BLACK); + char hint[52]{}; + strncpy(hint, proj.hint, 50); + display.drawString(hint, CX, 44, 2); + + // "Good Try" flash + if (con.showGoodTry) { + display.setTextColor(TFT_ORANGE, TFT_BLACK); + display.drawString("Good try! Try again...", CX, 60, 2); + } + + // Circular letter tiles + for (uint8_t i = 0; i < 9; ++i) { + int16_t tx, ty; + conCirclePos(i, const_cast(con.circleOrder), CX, CY, CR, + tx, ty); + tx = static_cast(tx - TW / 2); + ty = static_cast(ty - TH / 2); + + // Check if this position has been selected + bool sel = false; + for (uint8_t s = 0; s < con.selectedCount; ++s) { + if (con.selected[s] == i) { sel = true; break; } + } + uint16_t bg = sel ? TFT_DARKGREY : TFT_MAGENTA; + display.fillRoundRect(tx, ty, TW, TH, 4, bg); + display.drawRoundRect(tx, ty, TW, TH, 4, TFT_WHITE); + display.setTextColor(TFT_WHITE, bg); + // Map circle position to scramble letter via circleOrder + char s[2] = {proj.scramble[con.circleOrder[i]], '\0'}; + display.drawString(s, tx + TW/2, ty + TH/2, 2); + } + + // Reshuffle icon (top-right, outside header) + display.fillRoundRect(284, 38, 30, 22, 4, TFT_DARKGREY); + display.drawRoundRect(284, 38, 30, 22, 4, TFT_WHITE); + display.setTextColor(TFT_WHITE, TFT_DARKGREY); + display.drawString("~>", 299, 49, 2); + + // Answer strip + for (uint8_t i = 0; i < 9; ++i) { + int16_t bx = static_cast(6 + i * 34); + uint16_t bg = (i < con.selectedCount) ? TFT_DARKCYAN : TFT_DARKGREY; + display.fillRect(bx, 205, 30, 28, bg); + display.drawRect(bx, 205, 30, 28, TFT_WHITE); + if (i < con.selectedCount) { + display.setTextDatum(MC_DATUM); + display.setTextColor(TFT_WHITE, bg); + char ch[2] = {proj.scramble[con.circleOrder[con.selected[i]]], '\0'}; + display.drawString(ch, bx + 15, 219, 2); + } + } + + // Timer (top-right corner of circle area) + renderCountdownAnimation(kCdTimerCX, kCdTimerCY, kCdTimerR, + elapsedMs, 30000); +} + +static void renderCountdownConResult(bool won, const char* solution) { + display.fillScreen(TFT_BLACK); + display.setTextDatum(MC_DATUM); + if (won) { + display.setTextColor(TFT_GREEN, TFT_BLACK); + display.drawString("CONUNDRUM SOLVED!", 160, 60, 4); + display.setTextColor(TFT_WHITE, TFT_BLACK); + display.drawString(solution, 160, 120, 4); + display.setTextColor(TFT_YELLOW, TFT_BLACK); + display.drawString("YOU WIN! +10 POINTS", 160, 175, 4); + } else { + display.setTextColor(TFT_CYAN, TFT_BLACK); + display.drawString("CONUNDRUM SOLVED", 160, 60, 4); + display.setTextColor(TFT_WHITE, TFT_BLACK); + display.drawString(solution, 160, 120, 4); + display.setTextColor(TFT_LIGHTGREY, TFT_BLACK); + display.drawString("Good try!", 160, 175, 2); + } +} + +static void renderCountdownConNoWinner(const char* solution) { + display.fillScreen(TFT_BLACK); + display.setTextDatum(MC_DATUM); + display.setTextColor(TFT_RED, TFT_BLACK); + display.drawString("TIME'S UP", 160, 60, 4); + display.setTextColor(TFT_LIGHTGREY, TFT_BLACK); + display.drawString("The answer was:", 160, 120, 2); + display.setTextColor(TFT_WHITE, TFT_BLACK); + display.drawString(solution, 160, 155, 4); + display.setTextColor(TFT_DARKGREY, TFT_BLACK); + display.drawString("No points awarded", 160, 205, 2); +} + +// --------------------------------------------------------------------------- +// Letters + Conundrum sub-phase dispatcher +// --------------------------------------------------------------------------- +static void renderCountdownLetConPhases( // NOLINT + bool online, const CountdownWireState& state, + CountdownRoundSubPhase subPhase, uint32_t elapsed) { + using SubPhase = CountdownRoundSubPhase; + + // Letters sub-phases + if (subPhase == SubPhase::LetPicking) { + flip7::countdown::LettersRoundProjection proj{}; + portENTER_CRITICAL(&gameMux); + proj = countdownEngine.lettersProjection(); + portEXIT_CRITICAL(&gameMux); + renderCountdownLetPicking(state, proj, letUi.showConsonants); + return; + } + if (subPhase == SubPhase::LetThinking) { + flip7::countdown::LettersRoundProjection proj{}; + portENTER_CRITICAL(&gameMux); + proj = countdownEngine.lettersProjection(); + portEXIT_CRITICAL(&gameMux); + renderCountdownLetThinking(state, proj, elapsed); + return; + } + if (subPhase == SubPhase::LetClaimEntry) { + flip7::countdown::LettersRoundProjection proj{}; + portENTER_CRITICAL(&gameMux); + proj = countdownEngine.lettersProjection(); + portEXIT_CRITICAL(&gameMux); + renderCountdownLetClaimEntry(state, proj, + letUi.claimLength, letUi.claimSent); + return; + } + if (subPhase == SubPhase::LetClaimReveal) { + flip7::countdown::LettersRoundProjection proj{}; + uint8_t hClaim = 0, gClaim = 0; + portENTER_CRITICAL(&gameMux); + proj = countdownEngine.lettersProjection(); + auto* lr = countdownEngine.lettersRound(); + if (lr) { + hClaim = lr->claimFor(state.hostBoardId); + gClaim = lr->claimFor(state.guestBoardId); + } + portEXIT_CRITICAL(&gameMux); + renderCountdownLetClaimReveal(state, proj, hClaim, gClaim); + return; + } + if (subPhase == SubPhase::LetPresentPlayerA || + subPhase == SubPhase::LetPresentPlayerB) { + flip7::countdown::LettersRoundProjection proj{}; + portENTER_CRITICAL(&gameMux); + proj = countdownEngine.lettersProjection(); + portEXIT_CRITICAL(&gameMux); + bool presenterIsHost = letUi.presenterIsHost; + bool aPhase = (subPhase == SubPhase::LetPresentPlayerA); + bool isPresenter = + (aPhase && ((presenterIsHost && boardId == state.hostBoardId) || + (!presenterIsHost && boardId == state.guestBoardId))) || + (!aPhase && ((presenterIsHost && boardId == state.guestBoardId) || + (!presenterIsHost && boardId == state.hostBoardId))); + bool inVerifyMode = letUi.presentationSent; + if (inVerifyMode) { + // Show verification UI to the other board; waiting for presenter + bool isVerifier = !isPresenter; + portENTER_CRITICAL(&gameMux); + auto* lr = countdownEngine.lettersRound(); + uint32_t presenterBoardId = + (aPhase && presenterIsHost) ? state.hostBoardId + : state.guestBoardId; + const std::string& pw = + lr ? lr->presentedWordFor(presenterBoardId) : std::string{}; + portEXIT_CRITICAL(&gameMux); + renderCountdownLetVerification(state, pw.c_str(), + static_cast(pw.size()), + isVerifier, letUi.verificationSent); + } else { + renderCountdownLetPresenting(state, proj, isPresenter, + letUi.word, letUi.wordLen, + letUi.presentationSent); + } + return; + } + if (subPhase == SubPhase::LetResult) { + flip7::countdown::LettersRoundProjection proj{}; + portENTER_CRITICAL(&gameMux); + proj = countdownEngine.lettersProjection(); + portEXIT_CRITICAL(&gameMux); + // Show accepted/points from last verification result + bool accepted = letUi.verificationAnswer; + int32_t points = accepted + ? (letUi.claimLength == 9 ? 18 : letUi.claimLength) + : 0; + renderCountdownLetResult(state, proj, accepted, + letUi.word, letUi.wordLen, + points, false); + return; + } + + // Conundrum sub-phases + if (subPhase == SubPhase::ConReady) { + renderCountdownIntro(state, elapsed); // reuse intro screen + return; + } + if (subPhase == SubPhase::ConActive) { + // "Good Try" auto-clear after 1.5 seconds + if (conUi.showGoodTry && millis() - conUi.goodTryMs > 1500) { + conUi.showGoodTry = false; + } + flip7::countdown::ConundrumRoundProjection proj{}; + portENTER_CRITICAL(&gameMux); + proj = countdownEngine.conundrumProjection(); + portEXIT_CRITICAL(&gameMux); + renderCountdownConActive(state, proj, conUi, elapsed); + return; + } + if (subPhase == SubPhase::ConResult) { + flip7::countdown::ConundrumRoundProjection proj{}; + portENTER_CRITICAL(&gameMux); + proj = countdownEngine.conundrumProjection(); + portEXIT_CRITICAL(&gameMux); + bool won = (proj.winnerBoardId == boardId); + renderCountdownConResult(won, proj.scramble); // show solution + return; + } + if (subPhase == SubPhase::ConResultNoWinner) { + flip7::countdown::ConundrumRoundProjection proj{}; + portENTER_CRITICAL(&gameMux); + proj = countdownEngine.conundrumProjection(); + portEXIT_CRITICAL(&gameMux); + renderCountdownConNoWinner(proj.scramble); + return; + } + if (subPhase == SubPhase::HostTransfer || + subPhase == SubPhase::HostTransferAck) { + display.fillScreen(TFT_BLACK); + renderCountdownRoundHeader(state, false); + display.setTextDatum(MC_DATUM); + bool isOldHost = (boardId != state.hostBoardId); + display.setTextColor(TFT_YELLOW, TFT_BLACK); + display.drawString(isOldHost ? "TRANSFERRING HOST..." + : "ACKNOWLEDGED", + 160, 90, 4); + display.setTextColor(TFT_LIGHTGREY, TFT_BLACK); + display.drawString(isOldHost ? "Sending game state..." + : "State received. You are now the Host.", + 160, 150, 2); + if (subPhase == SubPhase::HostTransferAck) { + display.fillCircle(160, 195, 14, TFT_DARKGREEN); + display.drawCircle(160, 195, 14, TFT_WHITE); display.setTextColor(TFT_WHITE, TFT_DARKGREEN); - display.drawString("CONUNDRUM", - kCountdownConundrumX + - kCountdownRoundTypeWidth / 2, - kCountdownRoundTypeY + - kCountdownRoundTypeHeight / 2, - 2); + display.drawString("OK", 160, 195, 2); } - - display.setTextColor(TFT_WHITE, TFT_NAVY); - char roundLabel[32]; - snprintf(roundLabel, sizeof(roundLabel), "ROUND %lu", - static_cast(state.roundNumber)); - display.drawString(roundLabel, 160, 230, 2); return; } - display.drawString("COUNTDOWN MATCH", 145, 20, 4); - - display.setTextColor(TFT_YELLOW, TFT_NAVY); - display.drawString("SYNCHRONIZED NUM / LET / CON", 160, 55, 2); - - // Host & Guest Scores - display.fillRoundRect(20, 85, 130, 80, 8, TFT_DARKCYAN); - display.drawRoundRect(20, 85, 130, 80, 8, TFT_WHITE); - display.setTextColor(TFT_WHITE, TFT_DARKCYAN); - display.drawString("HOST", 85, 105, 2); - display.drawNumber(state.hostScore, 85, 135, 4); - - display.fillRoundRect(170, 85, 130, 80, 8, TFT_MAGENTA); - display.drawRoundRect(170, 85, 130, 80, 8, TFT_WHITE); - display.setTextColor(TFT_WHITE, TFT_MAGENTA); - display.drawString("GUEST", 235, 105, 2); - display.drawNumber(state.guestScore, 235, 135, 4); - - display.setTextColor(TFT_GREEN, TFT_NAVY); - if (boardId == state.hostBoardId) { - display.drawString("HOST PLAYER (IN CONTROL)", 160, 195, 2); - } else { - display.drawString("GUEST PLAYER (SYNCED)", 160, 195, 2); - } - - if (boardId == state.hostBoardId && - state.phase != CountdownWirePhase::Exited) { - display.fillRoundRect(kCountdownNewRoundX, kCountdownNewRoundY, - kCountdownNewRoundWidth, - kCountdownNewRoundHeight, 7, TFT_DARKGREEN); - display.drawRoundRect(kCountdownNewRoundX, kCountdownNewRoundY, - kCountdownNewRoundWidth, - kCountdownNewRoundHeight, 7, TFT_WHITE); - display.setTextColor(TFT_WHITE, TFT_DARKGREEN); - display.drawString("NEW ROUND", - kCountdownNewRoundX + kCountdownNewRoundWidth / 2, - kCountdownNewRoundY + kCountdownNewRoundHeight / 2, - 2); - } + // Generic fallback for any remaining sub-phase + display.fillScreen(TFT_BLACK); + renderCountdownRoundHeader(state, online); + display.setTextDatum(MC_DATUM); + display.setTextColor(TFT_YELLOW, TFT_BLACK); + display.drawString("IN ROUND", 160, 100, 4); + char spLabel[32]; + snprintf(spLabel, sizeof(spLabel), "Sub-phase %u", state.roundSubPhase); + display.setTextColor(TFT_LIGHTGREY, TFT_BLACK); + display.drawString(spLabel, 160, 145, 2); } void renderScreen() { @@ -2080,28 +3164,512 @@ void handleTouch() { } return; } + // --- NumPicking: chooser taps a large-count button (0-4) then CONFIRM --- if (mode == ScreenMode::Countdown && - x >= kCountdownNewRoundX && - x < kCountdownNewRoundX + kCountdownNewRoundWidth && - y >= kCountdownNewRoundY && - y < kCountdownNewRoundY + kCountdownNewRoundHeight) { - CountdownWireState countdownSnapshot{}; + static_cast(countdownState.roundSubPhase) == + CountdownRoundSubPhase::NumPicking) { + // Large-count buttons: x = 16 + i*58, y = kCdLcY, w=kCdLcW, h=kCdLcH + if (y >= kCdLcY && y < kCdLcY + kCdLcH) { + for (uint8_t i = 0; i <= 4; ++i) { + int16_t bx = static_cast(16 + i * 58); + if (x >= bx && x < bx + kCdLcW && + boardId == countdownState.chooserBoardId) { + numUi.largeCount = i; + displayDirty = true; + break; + } + } + } + // CONFIRM button: x=95..225, y=185..221 + if (y >= 185 && y < 221 && x >= 95 && x < 225 && + boardId == countdownState.chooserBoardId) { + // Chooser confirmed large count: transition to NumThinking + // Host starts the round in the engine; both advance sub-phase + CountdownWireState snap{}; + portENTER_CRITICAL(&gameMux); + snap = countdownState; + portEXIT_CRITICAL(&gameMux); + if (countdownStateReady && boardId == snap.hostBoardId) { + auto random = flip7::countdown::makeRoundRandom( + snap.gameId, snap.roundNumber); + portENTER_CRITICAL(&gameMux); + countdownEngine.startNextRound( + flip7::countdown::RoundType::Numbers, random, + numUi.largeCount); + portEXIT_CRITICAL(&gameMux); + + CountdownWireState advanced = snap; + if (advanceCountdownSubPhase( + advanced, boardId, + CountdownRoundSubPhase::NumThinking)) { + roundPhaseStartMs = millis(); + portENTER_CRITICAL(&gameMux); + if (sameCountdownWireState(countdownState, snap)) { + countdownState = advanced; + countdownPendingDelivery = CountdownPendingDelivery{ + true, MessageType::CountdownState, advanced, 0}; + displayDirty = true; + } + portEXIT_CRITICAL(&gameMux); + } + } + } + return; + } + + // --- NumClaimEntry: numpad interaction --- + if (mode == ScreenMode::Countdown && + static_cast(countdownState.roundSubPhase) == + CountdownRoundSubPhase::NumClaimEntry && + !numUi.claimSent) { + // Numpad rows: y = kCdPadY + row*(kCdPadH+kCdPadGap) + // Cols: x = kCdPadX + col*(kCdPadW+kCdPadGap) + for (int row = 0; row < 4; ++row) { + int16_t by = static_cast(kCdPadY + row * (kCdPadH + kCdPadGap)); + if (y < by || y >= by + kCdPadH) continue; + for (int col = 0; col < 3; ++col) { + int16_t bx = static_cast(kCdPadX + col * (kCdPadW + kCdPadGap)); + if (x < bx || x >= bx + kCdPadW) continue; + const int digits[4][3] = {{7,8,9},{4,5,6},{1,2,3},{0,-1,-2}}; + int d = digits[row][col]; + if (d >= 0) { + int32_t next = numUi.draftValue * 10 + d; + if (next <= 999) numUi.draftValue = next; + } else if (d == -1) { // CLR + numUi.draftValue = 0; + } else { // SUBMIT (d==-2) + // Send claim to engine (host) — locally mark sent + portENTER_CRITICAL(&gameMux); + auto* nr = countdownEngine.numbersRound(); + if (nr) nr->submitClaim(boardId, numUi.draftValue); + portEXIT_CRITICAL(&gameMux); + numUi.claimSent = true; + // If host: also check if both claims are in and advance + // (guest claim will come via action packet — handled later) + } + displayDirty = true; + break; + } + break; + } + return; + } + + // ----------------------------------------------------------------------- + // Letters sub-phase touch handlers + // ----------------------------------------------------------------------- + + // LetPicking: chooser taps vowel/consonant letter buttons or LOCK IN + if (mode == ScreenMode::Countdown && + static_cast(countdownState.roundSubPhase) == + CountdownRoundSubPhase::LetPicking && + boardId == countdownState.chooserBoardId) { + + flip7::countdown::LettersRoundProjection proj{}; portENTER_CRITICAL(&gameMux); - countdownSnapshot = countdownState; + proj = countdownEngine.lettersProjection(); portEXIT_CRITICAL(&gameMux); - if (countdownStateReady && boardId == countdownSnapshot.hostBoardId && - countdownSnapshot.phase != CountdownWirePhase::BetweenRounds) { - CountdownWireState advanced = countdownSnapshot; - if (advanceCountdownRound(advanced, boardId)) { + + // Tab toggle: VOWELS (x=5..105, y=50..78) / CONSONANTS (x=215..315) + if (y >= 50 && y < 78) { + if (x >= 5 && x < 105) { letUi.showConsonants = false; displayDirty = true; return; } + if (x >= 215 && x < 315) { letUi.showConsonants = true; displayDirty = true; return; } + } + + char picked = 0; + bool isVowel = false; + + if (!letUi.showConsonants) { + // Vowel buttons: A E I O U at x=10+i*60, y=95..139, W=50, H=44 + if (y >= 95 && y < 139) { + const char vowels[5] = {'A','E','I','O','U'}; + for (int i = 0; i < 5; ++i) { + int16_t bx = static_cast(10 + i * 60); + if (x >= bx && x < bx + 50) { + picked = vowels[i]; + isVowel = true; + break; + } + } + } + } else { + // Consonant keyboard: BCDFGH / JKLMNP / QRSTVW / XYZ + const char rows[4][7] = { + {'B','C','D','F','G','H', 0}, + {'J','K','L','M','N','P', 0}, + {'Q','R','S','T','V','W', 0}, + {'X','Y','Z', 0, 0, 0, 0}, + }; + const int rowLen[4] = {6, 6, 6, 3}; + constexpr int16_t W = 42, H = 32, GAP = 4; + for (int row = 0; row < 4; ++row) { + int16_t startX = static_cast( + (320 - rowLen[row] * (W + GAP) + GAP) / 2); + int16_t by = static_cast(88 + row * (H + GAP)); + if (y < by || y >= by + H) continue; + for (int col = 0; col < rowLen[row]; ++col) { + int16_t bx = static_cast(startX + col * (W + GAP)); + if (x >= bx && x < bx + W && rows[row][col] != 0) { + picked = rows[row][col]; + isVowel = false; + break; + } + } + if (picked) break; + } + } + + if (picked && proj.letterCount < 9) { + // Apply to engine and sync via wire + flip7::countdown::CommandContext ctx{boardId, 0}; + std::vector payload = {static_cast(picked)}; + portENTER_CRITICAL(&gameMux); + auto* lr = countdownEngine.lettersRound(); + if (lr) { + lr->applyCommand(ctx, + isVowel ? flip7::countdown::CommandType::LetPickVowel + : flip7::countdown::CommandType::LetPickConsonant, + payload); + } + portEXIT_CRITICAL(&gameMux); + displayDirty = true; + + // Auto-advance to LetThinking when 9 letters are drawn (host only) + portENTER_CRITICAL(&gameMux); + auto proj2 = countdownEngine.lettersProjection(); + bool done = (proj2.letterCount >= 9); + portEXIT_CRITICAL(&gameMux); + if (done && boardId == countdownState.hostBoardId) { + CountdownWireState snap{}; + portENTER_CRITICAL(&gameMux); + snap = countdownState; + portEXIT_CRITICAL(&gameMux); + CountdownWireState adv = snap; + if (advanceCountdownSubPhase(adv, boardId, + CountdownRoundSubPhase::LetThinking)) { + roundPhaseStartMs = millis(); + portENTER_CRITICAL(&gameMux); + if (sameCountdownWireState(countdownState, snap)) { + countdownState = adv; + countdownPendingDelivery = CountdownPendingDelivery{ + true, MessageType::CountdownState, adv, 0}; + displayDirty = true; + } + portEXIT_CRITICAL(&gameMux); + } + } + } + + // LOCK IN button: x=95..225, y=200..234 (when 9 letters) + if (proj.letterCount >= 9 && y >= 200 && y < 234 && + x >= 95 && x < 225 && boardId == countdownState.hostBoardId) { + CountdownWireState snap{}; + portENTER_CRITICAL(&gameMux); + snap = countdownState; + portEXIT_CRITICAL(&gameMux); + CountdownWireState adv = snap; + if (advanceCountdownSubPhase(adv, boardId, + CountdownRoundSubPhase::LetThinking)) { + roundPhaseStartMs = millis(); portENTER_CRITICAL(&gameMux); - if (sameCountdownWireState(countdownState, countdownSnapshot)) { - countdownState = advanced; - countdownStateReady = true; + if (sameCountdownWireState(countdownState, snap)) { + countdownState = adv; countdownPendingDelivery = CountdownPendingDelivery{ - true, MessageType::CountdownState, advanced, 0}; + true, MessageType::CountdownState, adv, 0}; + displayDirty = true; + } + portEXIT_CRITICAL(&gameMux); + } + } + return; + } + + // LetClaimEntry: digit picker (1-9) + SUBMIT CLAIM + if (mode == ScreenMode::Countdown && + static_cast(countdownState.roundSubPhase) == + CountdownRoundSubPhase::LetClaimEntry && + !letUi.claimSent) { + + // Digit row: y=130..164, x = (320 - 9*31+3)/2 + (d-1)*31 + constexpr int16_t DW = 28, DH = 34, DY = 130, DGAP = 3; + int16_t startX = static_cast((320 - 9*(DW+DGAP)+DGAP) / 2); + if (y >= DY && y < DY + DH) { + for (int d = 1; d <= 9; ++d) { + int16_t bx = static_cast(startX + (d-1)*(DW+DGAP)); + if (x >= bx && x < bx + DW) { + letUi.claimLength = static_cast(d); + displayDirty = true; + return; + } + } + } + // SUBMIT CLAIM button: x=95..225, y=180..214 + if (y >= 180 && y < 214 && x >= 95 && x < 225 && letUi.claimLength > 0) { + portENTER_CRITICAL(&gameMux); + auto* lr = countdownEngine.lettersRound(); + if (lr) lr->submitClaim(boardId, letUi.claimLength); + portEXIT_CRITICAL(&gameMux); + letUi.claimSent = true; + displayDirty = true; + } + return; + } + + // LetPresenting: tile taps, CLR, PRESENT WORD + if (mode == ScreenMode::Countdown && + (static_cast(countdownState.roundSubPhase) == + CountdownRoundSubPhase::LetPresentPlayerA || + static_cast(countdownState.roundSubPhase) == + CountdownRoundSubPhase::LetPresentPlayerB) && + !letUi.presentationSent) { + + auto sp = static_cast( + countdownState.roundSubPhase); + bool aPhase = (sp == CountdownRoundSubPhase::LetPresentPlayerA); + bool isPresenter = + (aPhase && letUi.presenterIsHost && + boardId == countdownState.hostBoardId) || + (aPhase && !letUi.presenterIsHost && + boardId == countdownState.guestBoardId) || + (!aPhase && letUi.presenterIsHost && + boardId == countdownState.guestBoardId) || + (!aPhase && !letUi.presenterIsHost && + boardId == countdownState.hostBoardId); + if (!isPresenter) return; + + flip7::countdown::LettersRoundProjection proj{}; + portENTER_CRITICAL(&gameMux); + proj = countdownEngine.lettersProjection(); + portEXIT_CRITICAL(&gameMux); + + // Letter tile row: y=kCdTileY..kCdTileY+34 + if (y >= kCdTileY && y < kCdTileY + 34 && letUi.wordLen < 9) { + constexpr int16_t W = 30, GAP = 4; + int16_t startX = static_cast( + (320 - proj.letterCount * (W + GAP) + GAP) / 2); + for (uint8_t ti = 0; ti < proj.letterCount; ++ti) { + int16_t tx = static_cast(startX + ti * (W + GAP)); + if (x >= tx && x < tx + W) { + letUi.word[letUi.wordLen++] = proj.letters[ti]; displayDirty = true; + return; + } + } + } + // CLR button: x=250..314, y=98..132 + if (y >= 98 && y < 132 && x >= 250 && x < 314 && letUi.wordLen > 0) { + letUi.wordLen--; + displayDirty = true; + return; + } + // PRESENT WORD button: x=95..225, y=155..189 + if (y >= 155 && y < 189 && x >= 95 && x < 225 && letUi.wordLen > 0) { + flip7::countdown::CommandContext ctx{boardId, 0}; + std::string wstr(letUi.word, letUi.wordLen); + std::vector payload(wstr.begin(), wstr.end()); + portENTER_CRITICAL(&gameMux); + auto* lr = countdownEngine.lettersRound(); + if (lr) lr->applyCommand(ctx, + flip7::countdown::CommandType::LetPresentComplete, payload); + portEXIT_CRITICAL(&gameMux); + letUi.presentationSent = true; + displayDirty = true; + } + return; + } + + // LetVerification: YES/NO buttons (verifier only) + if (mode == ScreenMode::Countdown && + (static_cast(countdownState.roundSubPhase) == + CountdownRoundSubPhase::LetPresentPlayerA || + static_cast(countdownState.roundSubPhase) == + CountdownRoundSubPhase::LetPresentPlayerB) && + letUi.presentationSent && !letUi.verificationSent) { + + auto sp = static_cast( + countdownState.roundSubPhase); + bool aPhase = (sp == CountdownRoundSubPhase::LetPresentPlayerA); + bool presenterIsHost = letUi.presenterIsHost; + bool isVerifier = + !((aPhase && presenterIsHost && boardId == countdownState.hostBoardId) || + (aPhase && !presenterIsHost && boardId == countdownState.guestBoardId) || + (!aPhase && presenterIsHost && boardId == countdownState.guestBoardId) || + (!aPhase && !presenterIsHost && boardId == countdownState.hostBoardId)); + if (!isVerifier) return; + + // YES: x=20..140, y=178..222 + if (y >= 178 && y < 222 && x >= 20 && x < 140) { + letUi.verificationAnswer = true; + letUi.verificationSent = true; + flip7::countdown::CommandContext ctx{boardId, 0}; + std::vector payload = {1}; + portENTER_CRITICAL(&gameMux); + auto* lr = countdownEngine.lettersRound(); + if (lr) lr->applyCommand(ctx, + flip7::countdown::CommandType::LetVerifyWord, payload); + portEXIT_CRITICAL(&gameMux); + // Host advances to LetResult or PlayerB + if (boardId == countdownState.hostBoardId) { + CountdownWireState snap{}; + portENTER_CRITICAL(&gameMux); + snap = countdownState; + portEXIT_CRITICAL(&gameMux); + CountdownWireState adv = snap; + auto nextSP = aPhase ? CountdownRoundSubPhase::LetPresentPlayerB + : CountdownRoundSubPhase::LetResult; + if (advanceCountdownSubPhase(adv, boardId, nextSP)) { + roundPhaseStartMs = millis(); + letUi = LetUiState{}; // reset for next presenter + portENTER_CRITICAL(&gameMux); + if (sameCountdownWireState(countdownState, snap)) { + countdownState = adv; + countdownPendingDelivery = CountdownPendingDelivery{ + true, MessageType::CountdownState, adv, 0}; + displayDirty = true; + } + portEXIT_CRITICAL(&gameMux); } + } + displayDirty = true; + return; + } + // NO: x=180..300, y=178..222 + if (y >= 178 && y < 222 && x >= 180 && x < 300) { + letUi.verificationAnswer = false; + letUi.verificationSent = true; + flip7::countdown::CommandContext ctx{boardId, 0}; + std::vector payload = {0}; + portENTER_CRITICAL(&gameMux); + auto* lr = countdownEngine.lettersRound(); + if (lr) lr->applyCommand(ctx, + flip7::countdown::CommandType::LetVerifyWord, payload); + portEXIT_CRITICAL(&gameMux); + if (boardId == countdownState.hostBoardId) { + CountdownWireState snap{}; + portENTER_CRITICAL(&gameMux); + snap = countdownState; portEXIT_CRITICAL(&gameMux); + CountdownWireState adv = snap; + auto nextSP = aPhase ? CountdownRoundSubPhase::LetPresentPlayerB + : CountdownRoundSubPhase::LetResult; + if (advanceCountdownSubPhase(adv, boardId, nextSP)) { + roundPhaseStartMs = millis(); + letUi = LetUiState{}; + portENTER_CRITICAL(&gameMux); + if (sameCountdownWireState(countdownState, snap)) { + countdownState = adv; + countdownPendingDelivery = CountdownPendingDelivery{ + true, MessageType::CountdownState, adv, 0}; + displayDirty = true; + } + portEXIT_CRITICAL(&gameMux); + } + } + displayDirty = true; + return; + } + return; + } + + // ----------------------------------------------------------------------- + // Conundrum touch handlers + // ----------------------------------------------------------------------- + + if (mode == ScreenMode::Countdown && + static_cast(countdownState.roundSubPhase) == + CountdownRoundSubPhase::ConActive) { + + constexpr int16_t CX = 160, CY = 125, CR = 75; + constexpr int16_t TW = 26, TH = 26; + + // Reshuffle button: x=284..314, y=38..60 + if (y >= 38 && y < 60 && x >= 284 && x < 314) { + // Fisher-Yates shuffle using millis() as entropy + uint32_t seed = millis(); + for (int i = 8; i > 0; --i) { + seed = seed * 1664525u + 1013904223u; + int j = seed % static_cast(i + 1); + uint8_t tmp = conUi.circleOrder[i]; + conUi.circleOrder[i] = conUi.circleOrder[j]; + conUi.circleOrder[j] = tmp; + } + displayDirty = true; + return; + } + + // Letter circles: check if tap is within TW/2 of any circle centre + flip7::countdown::ConundrumRoundProjection proj{}; + portENTER_CRITICAL(&gameMux); + proj = countdownEngine.conundrumProjection(); + portEXIT_CRITICAL(&gameMux); + + for (uint8_t i = 0; i < 9; ++i) { + // Skip already selected + bool alreadySel = false; + for (uint8_t s = 0; s < conUi.selectedCount; ++s) + if (conUi.selected[s] == i) { alreadySel = true; break; } + if (alreadySel) continue; + + int16_t tx, ty; + conCirclePos(i, conUi.circleOrder, CX, CY, CR, tx, ty); + if (abs(x - tx) <= TW/2 + 4 && abs(y - ty) <= TH/2 + 4) { + conUi.selected[conUi.selectedCount++] = i; + displayDirty = true; + + if (conUi.selectedCount == 9) { + // Build attempt string and submit + char attempt[10]{}; + for (uint8_t s = 0; s < 9; ++s) + attempt[s] = proj.scramble[conUi.circleOrder[conUi.selected[s]]]; + + flip7::countdown::CommandContext ctx{boardId, 0}; + std::vector payload(attempt, attempt + 9); + bool correct = false; + portENTER_CRITICAL(&gameMux); + auto* cr = countdownEngine.conundrumRound(); + if (cr) { + auto res = cr->applyCommand(ctx, + flip7::countdown::CommandType::ConSubmitAttempt, + payload); + correct = res.accepted; + } + portEXIT_CRITICAL(&gameMux); + + if (correct && boardId == countdownState.hostBoardId) { + // Advance to ConResult + CountdownWireState snap{}; + portENTER_CRITICAL(&gameMux); + snap = countdownState; + portEXIT_CRITICAL(&gameMux); + CountdownWireState adv = snap; + if (advanceCountdownSubPhase(adv, boardId, + CountdownRoundSubPhase::ConResult)) { + portENTER_CRITICAL(&gameMux); + if (sameCountdownWireState(countdownState, snap)) { + countdownState = adv; + countdownPendingDelivery = CountdownPendingDelivery{ + true, MessageType::CountdownState, adv, 0}; + displayDirty = true; + } + portEXIT_CRITICAL(&gameMux); + } + } else if (!correct) { + // Reset attempt — forced local reshuffle + conUi.selectedCount = 0; + conUi.showGoodTry = true; + conUi.goodTryMs = millis(); + // Shuffle circle order + uint32_t seed2 = millis(); + for (int si = 8; si > 0; --si) { + seed2 = seed2 * 1664525u + 1013904223u; + int j = seed2 % static_cast(si + 1); + uint8_t tmp = conUi.circleOrder[si]; + conUi.circleOrder[si] = conUi.circleOrder[j]; + conUi.circleOrder[j] = tmp; + } + } + } + return; } } return; @@ -2125,10 +3693,13 @@ void handleTouch() { portENTER_CRITICAL(&gameMux); countdownSnapshot = countdownState; portEXIT_CRITICAL(&gameMux); + // Chooser (not host) picks the round type. if (countdownStateReady && - boardId == countdownSnapshot.hostBoardId) { + boardId == countdownSnapshot.chooserBoardId) { CountdownWireState selected = countdownSnapshot; if (selectCountdownRoundType(selected, boardId, roundType)) { + roundPhaseStartMs = millis(); + numUi = NumUiState{}; // reset per-round UI state portENTER_CRITICAL(&gameMux); if (sameCountdownWireState(countdownState, countdownSnapshot)) { @@ -2446,6 +4017,282 @@ void loop() { refreshPeerIdentity(); serviceProtocol(now); advanceMastermindRoundIfReady(now); + + // --- Countdown sub-phase auto-advance (host only) and animation ticks --- + { + CountdownWireState snap{}; + bool cdReady = false; + bool cdHost = false; + portENTER_CRITICAL(&gameMux); + snap = countdownState; + cdReady = countdownStateReady; + cdHost = (boardId == countdownState.hostBoardId); + portEXIT_CRITICAL(&gameMux); + + if (cdReady && snap.phase == CountdownWirePhase::InRound) { + using SP = CountdownRoundSubPhase; + auto sp = static_cast(snap.roundSubPhase); + + // Force re-render during timed animation sub-phases so the arc updates. + bool animating = (sp == SP::Intro || + sp == SP::NumThinking || + sp == SP::LetThinking || + sp == SP::ConActive); + if (animating) { + portENTER_CRITICAL(&gameMux); + displayDirty = true; + portEXIT_CRITICAL(&gameMux); + } + + // Host-only: auto-advance when a timed phase expires. + if (cdHost) { + uint32_t elapsed = (now >= roundPhaseStartMs) + ? now - roundPhaseStartMs : 0; + CountdownRoundSubPhase nextSP = SP::None; + uint32_t timeout = 0; + + if (sp == SP::Intro) { + timeout = 5000; + // Advance to picking sub-phase for the selected round type. + auto rt = static_cast( + snap.roundType); + if (rt == flip7::countdown::RoundType::Numbers) + nextSP = SP::NumPicking; + else if (rt == flip7::countdown::RoundType::Letters) + nextSP = SP::LetPicking; + else + nextSP = SP::ConReady; + } else if (sp == SP::NumThinking) { + timeout = 30000; + nextSP = SP::NumClaimEntry; + } else if (sp == SP::LetThinking) { + timeout = 30000; + nextSP = SP::LetClaimEntry; + } else if (sp == SP::ConActive) { + timeout = 30000; + nextSP = SP::ConResultNoWinner; + } + + if (nextSP != SP::None && elapsed >= timeout) { + CountdownWireState advanced = snap; + if (advanceCountdownSubPhase(advanced, boardId, nextSP)) { + roundPhaseStartMs = now; + portENTER_CRITICAL(&gameMux); + if (sameCountdownWireState(countdownState, snap)) { + countdownState = advanced; + countdownPendingDelivery = CountdownPendingDelivery{ + true, MessageType::CountdownState, advanced, 0}; + displayDirty = true; + } + portEXIT_CRITICAL(&gameMux); + } + } + + // Auto-advance result sub-phases → BetweenRounds (with host handoff) + bool isResultPhase = (sp == SP::NumResult || + sp == SP::LetResult || + sp == SP::ConResult || + sp == SP::ConResultNoWinner); + if (isResultPhase && elapsed >= 3000) { + // 1. Finalize the round in the engine and get the result. + std::optional result; + portENTER_CRITICAL(&gameMux); + if (countdownEngine.activeRound()) { + std::vector players = {snap.hostBoardId, + snap.guestBoardId}; + result = countdownEngine.activeRound()->tryFinalize(players); + if (result) { + countdownEngine.recordRoundResult(*result); + } + } + portEXIT_CRITICAL(&gameMux); + + // 2. Detect host change; build updated wire state. + uint32_t newHostId{}; + uint32_t newChooserId{}; + int32_t newHostScore{}; + int32_t newGuestScore{}; + portENTER_CRITICAL(&gameMux); + newHostId = countdownEngine.hostBoardId(); + newChooserId = countdownEngine.chooserBoardId(); + newHostScore = countdownEngine.hostPlayer().score; + newGuestScore = countdownEngine.guestPlayer().score; + portEXIT_CRITICAL(&gameMux); + + bool hostChanged = (newHostId != snap.hostBoardId); + + CountdownWireState updated = snap; + ++updated.revision; + ++updated.roundNumber; + updated.chooserBoardId = newChooserId; + updated.hostTerm += hostChanged ? 1 : 0; + + // Swap host/guest scores and IDs if leadership transferred. + if (hostChanged) { + updated.hostBoardId = newHostId; + updated.guestBoardId = snap.hostBoardId; + updated.hostScore = newGuestScore; + updated.guestScore = newHostScore; + updated.roundSubPhase = + static_cast(SP::HostTransfer); + } else { + updated.hostScore = newHostScore; + updated.guestScore = newGuestScore; + updated.roundSubPhase = 0; + updated.phase = CountdownWirePhase::BetweenRounds; + } + + roundPhaseStartMs = now; + // Reset per-round UI + numUi = NumUiState{}; + letUi = LetUiState{}; + conUi = ConUiState{}; + + portENTER_CRITICAL(&gameMux); + if (sameCountdownWireState(countdownState, snap)) { + countdownState = updated; + countdownPendingDelivery = CountdownPendingDelivery{ + true, MessageType::CountdownState, updated, 0}; + displayDirty = true; + } + portEXIT_CRITICAL(&gameMux); + } + + // HostTransfer: after 2s animation, commit and go to BetweenRounds + if (sp == SP::HostTransfer && elapsed >= 2000) { + CountdownWireState committed = snap; + committed.roundSubPhase = 0; + committed.phase = CountdownWirePhase::BetweenRounds; + ++committed.revision; + roundPhaseStartMs = now; + portENTER_CRITICAL(&gameMux); + if (sameCountdownWireState(countdownState, snap)) { + countdownState = committed; + countdownPendingDelivery = CountdownPendingDelivery{ + true, MessageType::CountdownState, committed, 0}; + displayDirty = true; + } + portEXIT_CRITICAL(&gameMux); + } + + // NumClaimEntry: if both claims are in (host side), auto-advance + if (sp == SP::NumClaimEntry && cdHost) { + portENTER_CRITICAL(&gameMux); + auto* nr = countdownEngine.numbersRound(); + bool both = nr && nr->bothClaimsSubmitted(); + portEXIT_CRITICAL(&gameMux); + if (both) { + CountdownWireState advanced = snap; + if (advanceCountdownSubPhase(advanced, boardId, + SP::NumClaimReveal)) { + roundPhaseStartMs = now; + portENTER_CRITICAL(&gameMux); + if (sameCountdownWireState(countdownState, snap)) { + countdownState = advanced; + countdownPendingDelivery = CountdownPendingDelivery{ + true, MessageType::CountdownState, advanced, 0}; + displayDirty = true; + } + portEXIT_CRITICAL(&gameMux); + } + } + } + + // NumClaimReveal: after 2s, advance to presentation + if (sp == SP::NumClaimReveal && elapsed >= 2000) { + CountdownWireState advanced = snap; + if (advanceCountdownSubPhase(advanced, boardId, + SP::NumPresentPlayerA)) { + roundPhaseStartMs = now; + portENTER_CRITICAL(&gameMux); + if (sameCountdownWireState(countdownState, snap)) { + countdownState = advanced; + countdownPendingDelivery = CountdownPendingDelivery{ + true, MessageType::CountdownState, advanced, 0}; + displayDirty = true; + } + portEXIT_CRITICAL(&gameMux); + } + } + + // LetClaimEntry: both claims in → reveal + if (sp == SP::LetClaimEntry && cdHost) { + portENTER_CRITICAL(&gameMux); + auto* lr = countdownEngine.lettersRound(); + bool both = lr && lr->bothClaimsSubmitted(); + portEXIT_CRITICAL(&gameMux); + if (both) { + CountdownWireState advanced = snap; + if (advanceCountdownSubPhase(advanced, boardId, + SP::LetClaimReveal)) { + roundPhaseStartMs = now; + portENTER_CRITICAL(&gameMux); + if (sameCountdownWireState(countdownState, snap)) { + countdownState = advanced; + countdownPendingDelivery = CountdownPendingDelivery{ + true, MessageType::CountdownState, advanced, 0}; + displayDirty = true; + } + portEXIT_CRITICAL(&gameMux); + } + } + } + + // LetClaimReveal: after 2s → first presentation + if (sp == SP::LetClaimReveal && elapsed >= 2000) { + // Determine presenter order from claims + portENTER_CRITICAL(&gameMux); + auto* lr = countdownEngine.lettersRound(); + uint8_t hc = lr ? lr->claimFor(snap.hostBoardId) : 0; + uint8_t gc = lr ? lr->claimFor(snap.guestBoardId) : 0; + portEXIT_CRITICAL(&gameMux); + letUi.presenterIsHost = + (hc >= gc); // ties: host presents first + CountdownWireState advanced = snap; + if (advanceCountdownSubPhase(advanced, boardId, + SP::LetPresentPlayerA)) { + roundPhaseStartMs = now; + portENTER_CRITICAL(&gameMux); + if (sameCountdownWireState(countdownState, snap)) { + countdownState = advanced; + countdownPendingDelivery = CountdownPendingDelivery{ + true, MessageType::CountdownState, advanced, 0}; + displayDirty = true; + } + portEXIT_CRITICAL(&gameMux); + } + } + + // ConReady: after 2s → ConActive + if (sp == SP::ConReady && elapsed >= 2000) { + // Start conundrum round in engine + portENTER_CRITICAL(&gameMux); + auto random = flip7::countdown::makeRoundRandom( + snap.gameId, snap.roundNumber); + countdownEngine.startNextRound( + flip7::countdown::RoundType::Conundrum, random); + // Reset conundrum circle order + for (uint8_t ci = 0; ci < 9; ++ci) conUi.circleOrder[ci] = ci; + portEXIT_CRITICAL(&gameMux); + + CountdownWireState advanced = snap; + if (advanceCountdownSubPhase(advanced, boardId, + SP::ConActive)) { + roundPhaseStartMs = now; + portENTER_CRITICAL(&gameMux); + if (sameCountdownWireState(countdownState, snap)) { + countdownState = advanced; + countdownPendingDelivery = CountdownPendingDelivery{ + true, MessageType::CountdownState, advanced, 0}; + displayDirty = true; + } + portEXIT_CRITICAL(&gameMux); + } + } + } + } + } + handleTouch(); const bool online = peerOnline(); From 087d2d7fa0bad2b8541b1c589ff9f9d40c0d2ef0 Mon Sep 17 00:00:00 2001 From: Christopher Mayfield Date: Wed, 5 Aug 2026 09:46:55 -0500 Subject: [PATCH 08/12] fix(countdown): fix round selection, flicker, target sync, and build flags - selectCountdownRoundType: accept Setup phase so first-game round tap works - Wire: rename reserved->roundConfig; store largeCount for guest engine sync - Guest engine: call startNextRound on state adoption so target/tiles project - Rendering: firstFrame pattern eliminates fillScreen flicker on timed phases (Intro, NumThinking, LetThinking draw background once, only timer circle updated on subsequent 100ms ticks; ConActive reduced to 500ms) - platformio.ini: restore build_unflags + std=gnu++17 (was wiped by editor) - Build tooling: scripts/upload.py auto-detects and flashes both CYD boards, releases port locks before upload, supports --monitor-only and --port flags - VS Code: .vscode/launch.json + tasks.json wired to upload.py; compound 'Monitor Both CYDs' config opens two simultaneous serial terminal sessions - content/conundrum/default_words.csv: 50 nine-letter conundrum words - docs: add architecture markdown conversion --- content/conundrum/default_words.csv | 50 ++ docs/Flip7 CYD Scorekeeper Architecture.docx | Bin 0 -> 25616 bytes docs/Flip7 CYD Scorekeeper Architecture.md | 778 +++++++++++++++++++ include/countdown_wire.h | 11 +- platformio.ini | 1 + scripts/upload.py | 201 +++++ src/main.cpp | 151 ++-- 7 files changed, 1133 insertions(+), 59 deletions(-) create mode 100644 content/conundrum/default_words.csv create mode 100644 docs/Flip7 CYD Scorekeeper Architecture.docx create mode 100644 docs/Flip7 CYD Scorekeeper Architecture.md create mode 100644 scripts/upload.py diff --git a/content/conundrum/default_words.csv b/content/conundrum/default_words.csv new file mode 100644 index 0000000..0321d7b --- /dev/null +++ b/content/conundrum/default_words.csv @@ -0,0 +1,50 @@ +BLUEPRINT|TNPULEBRI|A detailed plan or design +COUNTDOWN|DTWNOCNUO|A backward sequence before an event +STARTLING|GTLNRSAIT|Surprising or astonishing +CARPETING|TICNGPEAR|Floor covering material +GRENADIER|AIGNRDERE|A type of soldier +PETROLEUM|MOUERTPLE|Crude oil and its derivatives +RECTANGLE|CTRELAGNE|A four-sided flat shape +IMPORTANT|TNAMIPORT|Of great significance +SPECTATOR|POSTCATER|Someone watching an event +TRANSPORT|RNOTTARPS|Moving people or goods +CARNATION|TONRAANIC|A type of flower +CELEBRATE|EBTELRACE|To mark a happy occasion +DEMANDING|DEMNIGDAN|Requiring great effort +ELABORATE|EOBARATEL|Detailed and complicated +FORMATION|IOATONMRF|An arrangement or pattern +GREATNESS|ESNAERTGS|The quality of being great +HAPPINESS|NASEPHSIP|The state of being happy +IDENTICAL|TCALDENII|Exactly the same +MILESTONE|EMILESOTN|A significant achievement +NIGHTMARE|HIRMNGETA|A very frightening dream +ORCHESTRA|OCHRTRASE|A large group of musicians +PARACHUTE|PHATACRUO|Device for safe descent +QUALITIES|SQUITAILE|Characteristics or features +RASPBERRY|BSYRARPRE|A red or black berry +SCULPTURE|LCETRSUPU|A three-dimensional artwork +TESTIMONY|STIOYMNTE|A formal statement +UNCERTAIN|NCTEUNRAI|Not definitely known +VALENTINE|VLANITEEN|A token of love or affection +WATERFALL|LTFEARAWL|Water falling from a height +YESTERDAY|SEATDREHY|The day before today +ZEALOUSLY|LOUZSYEAL|With great enthusiasm +AFTERMATH|RFTHMAAET|Consequences of an event +BRILLIANT|BTRANLILI|Very bright or intelligent +CHEMISTRY|RHMYSTICE|The science of substances +DIALOGUES|GODSILEAU|Conversations in text +ELEPHANTS|PHANTELSE|Large grey mammals +FIREWORKS|RFISWOKRE|Explosive displays of light +GEOGRAPHY|PHYROEAGP|The study of the earth +HURRICANE|NARRIHUCE|A violent tropical storm +INVISIBLE|SINIBVILE|Unable to be seen +JUDGEMENT|JDTUGEMEN|An opinion or decision +LABYRINTH|NLBHYTIRA|A maze or complex system +MECHANISM|MHCNIAMSE|A system of moving parts +NOTORIOUS|TOUNORISE|Well known for a bad reason +OPERATION|PNOOAIRET|A process or procedure +PARCHMENT|PRACMNHET|A writing material +QUARTERLY|TAYRLEQU|Happening four times a year +RELENTLESS|SLNLEETER|Without stopping or weakening +SUBMARINE|BNMAURIES|An underwater vessel +TORMENTED|DTRMONETE|Caused to suffer greatly diff --git a/docs/Flip7 CYD Scorekeeper Architecture.docx b/docs/Flip7 CYD Scorekeeper Architecture.docx new file mode 100644 index 0000000000000000000000000000000000000000..80086d230957bd028d76a92bd7081341a18a9f17 GIT binary patch literal 25616 zcmY(KV{m5C60T!RY$p@jwr$(C{l&J;iEZ1qZQGgn&N;Vk)jdDf-mCVi+ST>0{?T3i zJ_^#HV5mSKP*6ZXK!iY079BG|z(7DTkU&5v|GsDo+uOOA+PUbfcsiIm>(Y7H+BBz3 z$_+9giu#dGpp)8Z{tkZE0MZpAoq%k6DT)c_P31j5yv-nC5rRrgZOM|DnacL|F{Um& zpFy360F2GY9vX6m6W9H6wNpR*>%%C0b9ipjw8jmE)VjeCyDD-c7@ogkU?MRea4Z$N zBXLly4^6%trW4SH2YVANByB+XClX0sOg?_RA^*&Cv=&DbKdx}Jq&5+IbfKL{J+Y1E zqT;XXoG=m>8N1p~T^c}B+Oy#X`f@8I3m$q&WZe=w-LxGfrhi=y#6E3=x$m??1Du1D03OaG&D937Mz_a>;ys$`JdS~#dY zr_1P0B=yse#Gb=w<@qA%q=3cDj#Ch)S~urq6a5}GdH2c7WovL0w*|G>Ny~1ykg}^j zVI=5VI!w4pLd`v|mdmBk($DHRxx)Y4S*v@|?diX}S^xn8g8BE>cQUncrlKLIvXtoJed}T7hGJ{@)1O8)ElV1q*Vbd9h$1P&^$wUk@k=g#EVL1I>%BToteB+ zUi+xXNrcx(R7>~BXA*FjOmhqN!-kxotsY9=K&%hwLc!|J8%T-b8Pl3kWF64+IG5pC0b^PA2pw_QtNZ{}TB> z8GP*{ZGSZ8cJlolDS*WQm@kl0y3l-e!%>o4uC7V@t*W|NDMo^ofkjIel!jnx3I~g+ z=fMhNT;g~glGd1g+(-SB`AzCP69JW&QIMRWk|dL=Qg)O0(9dztb^DmZ|MPWPKWis~ zDvbvCu<>@j#6k(Ezy!KF~rw! zu!60Hkr02xmt6=G?LUH%;m4U^ZkCqY=M^j74e(50NwX`}W0S;Ij?h0TPw~Itu)q2o znq6(q%C@woda%)DenIG)nduFYyVKZHZ2yw?wK`}UIt$SQi-7BX!C7BP-BIAg34Dd)>-hE{538g4q+$t$>CZ!UL~dnqwTr+#91Y2?@miHN6J9{|j^Csx*M z`s!`|6OmFckSD#FMTehL)m5WevPI~bJkiA#k1&V*Bf|1WpdGd5XRxnBg7?bS4NiNC4i0y8rNt-2(gR;%_?hS zeOc6gbbbB$TF3Sp7rFF?8sC$Vk!|n>JELMY$Z#U8pryR$^CS!IMT@it)+z^aEYuNO zBCSXu=HsNx`)4uxuV*3I6%FFNLx;bvwHch(xVvAECsKca8RO26eV%MlTC|?cmnF%O zLvq*J&K8W|K^aD2_=gh|RW(cl5E=x4Qcl1rqxBqeaPPeR6{?5%Hy4KY?5($7=hYWh ze|r*)+`VSo?*Y4Dlu~0JmF9Ec&8({__>r0?gJ+2`Zm7Qu$WiR2?fDUt z91$7VbtVOCpSlFl_zixjJqV>IDkFW?B`*);#PH%q*lmRp;15v-!Wsr1h#G7bvm%QO zz-|`s`(`N_dHyT!f&e+S^hap?dyp_}M~zRQ@LL$9{CQGH}wypk>VkqH#e z-7Bzde#z66E3o2FT9-gIF1RsXR(BJz;4Sl><)LZeN)z;Iz7bxf;VLo+9$sdi!8vv* zh^#S_qJ>~lkn}c~23X8C|8}6)QGI`Nt_Y`iSFLVf6Ja3XzO<}^9S(8sD9sHYf>Ki# zGQlK6DnuF4zH@U;-^iUfr4aKZpY)6e_TBg?Ba;Lhf3Ki8h>c?LK|6;0TP^>1(0brn zU&h)%x*`zucO{h5%0Djb5|e>nlF5~TlZXIVlDBr2dmmi(h`p_Wn{l8c1an3Wxx1|8 zT|6w0-#Pn!f&+JLtnC+}=sL(;=cyJv(hFhkJP}mCEnp=8i$cz&3*Ez>=Q!N)^Bx1# zH5Sw^o7xV5d+Zy_qJ{nNn@XRr?Q?4`NB$n}T9$IiIzp_&?PyeYagROwqA)FjEzw+% zun3YZq9u=Gjwf)hkSAKfFQ+2cq0h`c*aw_3GGY#^=)#7GSLHdV6Sw?{P`eedZ)0S% z6-gjii5MrP znyiN{cECzx8TpQ(dBP~DWE%;TdcO<=oUM|VDdRDYM;rx}&qkIEUhNbRC?{~U6H`kY zyLiolBw_@GfgZh}qUK_-^ejL4^K_@cwpbAItbVwx<% z5?~QQfwbN$tJ{)kA=Tp|vLMX1dD!a`+v2(#tGW5XN<%R&lF5k}(1Pg~<6j0@=7nyj z$M29a=a3u-Mn6VrW)Hl9wu{>J!oA#In1GtD$QSq(&pFP=tal%;f+y9{i+zd>&>#x} z2sjM;`Ubk>RN4&VF2ri0T#RE$(%|mlFHoI;7O<+7OW;T)bJ0+|uA+xKI)5{@wklkq zHpGIUsUUJxYBYDtzQ9A2(^%pkb>i)%opuu)nZc+WDwcIezQA|Hikb~2*k*ESW%Wd; zp5CRYfZ&kUD*i?u(UnQRM8DsHVgzV*#6%ri4fU#yU>Q8eaNgp14)^=sjM&WZ8Iy#H z0)*K7$}ET&Cw2w61rAB1bmJEoDv#IRy=1PEVa1X=T8&z_)BRO{JeJb(X#%WrA?nZ= z#q{QUAHa4pVz>U@DkHKxH0%{YFBx%K)(g{tB9a*&pSwWd?Dj2U#Fo^lZm~Z$8J1Qk zcAHyfB7FF9=6Z{OT^{ z{dK+i^R`+0qjP@STYnX|+}^;C;CVHrMLT5+_zUt0)AeVXOOif!KOv_Q09T(m9kaY_Acy=?qQ@G4jHZj57|AIgu8>n^*Q{E;%m zD^W>t4wK7r{ZTD;Q!m}o?H2J%Ka7D}@J;mnILiLBE$8=NT1eU7*wTmUw-EDb`7WcV zD540(P$}FOBmI0IG)Z#RjZqONi|`4bym!%Cua&Y5C)@F+l;pX{j9!E=uE5Fcv#wN@ zMDuWY!+1@FWc@sK0{&k3y6z({TpAYMc<9XOE)BNZBda@PlaZrZbv-+2=JSc>S{CFO zr9;4nHcPGqxV+jtBRbGI#90Ig(Ic}*Luc5iIQyFV!q@n!4u)T%*IYMv*jA=E4%IY6 z++^-~$Man&igLsHKi)nOI$s{2n_O~6-)N)=}p}uCT>Qo>a5+;aBt6hNUz`L zAQ66Fwxi>M+nDnlHHiX7;$r)4a009j)_{$*GvSpzx}o`Aw4RCaPIq7NcU&O=oeFVAMv`Dz zU^Nx4r?-824#Sa0S@f@xXiArN3g_UfC+C5TsC{Z0*9ZyNgROxS$OJ8p^{<_P=Z7=wToAWet7b59m|7v=Qku@MkPEZc8R4cuJ`>o}E(VjwFbVa& zIY+@=D>VT)rXhPSHg^SDG6Ru``(e*T!DuV6LYgbs`u5g*YA|sN5)+Pmds>i^n(G2Wg5Jjr!2I)U}8B<*)xbL@!Z%aS=#5k*_rG_m@|YFUo}rZ z_wtn#4BbjR?O5Puo#7Z^D}Q9Nu@KX-(bDm`nS~oF58`8(LfpkZAW~LJ>dhlz&y)Yq z7!8#5)rCaZz_*+jBR&F$VsTP!r?6DKx6f%rZ`ZM?w4DG-^WY(l{i`l#oG%LB?Dvbl z%IP}n$r}K+mtR^kNCkj5G~i)jI4%!&tDOn{9rD`?;oL=E#KZ47bK@)>w5C^~Kl^cg$eZ`}G!kS8rPf|N5%A%C)ebQF@4LR5 zr;W1KOzysqKlI=~&H4d>yPubqGD*Pu)g*lyK9tsQaab8!?T3%ra+*njAi?0QKK+WZ z2EiA#I`~AED6Lf*kf~z)RX@2i?*YXyBRAy{qlJ9tI?(UzpYKtmv}^(MgnFylyHfcT;Brg zTW0sDEcaaOO)HaMDT}fTB%U~ueTTZNaHH}BMwex)BC|7h?aH*{_35)1_~3Kqnl6Rf z?^OWV=%GrX!2nKq9_G$+lYGSns&ALq5B#Ld{J{)ohDJHUu7{s5h>Jx3A4+^VxC8FR*Tg4M?aajaRu{@ zO>f7MriQyj-O5jmFlF`|D9AA!t5w^nJWR3bkLKya&n_3iw_oP2Hf;ih@+;?uKmlY6 z>u#3}#1x~~yGq){N$T6j&jsMGA5FW8wdU&P`|h)W_h^XLu(#&!>d1@L*Cxp3q7@37 zP|bH~3fCzCTGWL633@R}oY!twj!JGE{IS*yA24LG!UnVRgecQ=oh#3{l79w)Qc)Xl)B=5OZp6 z5FwtnlzhcoHef*x%6_VL^E1+_nuU&feZjNN8++Z&CxT zoC9D-b@58=Qgng_r#%q{1BXAq8a*Oy?`4##&eKL3R7wSkpoxHy(YHXhMi)Y8T~1;a z38^aUT8xj2D?zYo5}91v+NW!-Eb@CjRbd!#BoF)GJa!T(Oqd+LmaKh0{S3jg)!F)X z;H&5IGkSDmsyc@#U|0RIos_B|X|Y1poA94Rm`mQh=7!2ES7$K|G!X(vG{GEr&!ER& z^bxfF>MP-oyKK?1_L_}iJvMCSlXmVUY$hV68@tY8iYaC4M>^St*iCCGuy`k|1{QtNwMxy@GOxC0CyPRS z57?b`3$eRk@~a5aeAMAYR4v198>Tr2HCr7Mr+W{Q7Hn##ME3BVJ46Y5B*(kcIyn?i zWRHyVqa87f@85{vKJFigp$c@5V&v{L(F$_Hi+T4(=0v z2q>_#9ed@{psJhlF5_~?(aSt*s?+6TEznReD+R#N546 z%<#dm%gTIPl~T+GW7W$cTCy9^-{n(d>=7yHMfPEZC)r8^9+9s;%oGbMXl-)0vTSpC zK$fA6Av6(Ey;xiD-4xl38x{xBtEud;tU4T5*}G~V6;cB z(Yttj^jU(i9h=6>4A+#xk&0F9rDYLNZ~?w>k1? z{pP)LJqNK?GJg5B$d|4eQUWiKiy7Qf2bGlkp#cSgTTR*Q<*MGUSKi>Gqd3M#A_ALw zis?f@2f(O-(b?umu+v`3-Zqex>}*iOxajd3wV?s!3^nHM6JHxa zE#fVzpE)!h7twUzqA7n3kVs|d4mm6A1bNFtA$v##=SnY;88)KDe9zuR$CR*DyC&0h-wueD{V+ZRZ24y(LIHh> zAT}IrOAEU^SD&D6>V^xqY4;JLj{VK-5Xziv@d|LGx<A&OKni6wj+J&&&PfRYaQFoQSz!`Us4JWDCjRU%iE z(0mH6`Ehrsskvf?=S-9yA)M&yhG!$kJJoPL=UuxH&{-#VsrIg^O7fo4%+ew96mgI+ zS_G+%hN-eRJwt_TTcfWs?>EQMJ(`>>t5c8i)PszxD`B(uxX&wCiu~P;sNvcQRKhuiC12&-GSM2 zqRD9@vVFzR_q+ZI!CcjRj^fj;1jM=9Vu83pF?C}dgF~xAui1+o&?xM3jTg5Ef$hx( z%tHl-LE&l;PZQvR`i3!Wu#+hx5#tZNM)cT2i?~?TNUwT}wrHANqH4~EimP0IoQ8Iu z?4@pJ3g!>owb3M+<@s$gk6n`cwNX5kTpI8^zcH1vE~gvH{m1uZ%YZvOHhIS!XtzCfiC??nVA)&M7yl=EZQexl`6e zO`@BCP^^K8@SZEc90&`Tfhb4#5qrt+W0$$#*r0Nvn7D3FU#_=aUBmo(yVn14ZyY>` zZp6#vi=^yr`Wkm>Q^*?^XM6-n4ei8~7Sh!N3!kFBYf7Em>)}s~K>grRAvy9Hz{D`A18M=&NmaYlj^UQPE zRkE1v%8U?dF`pNxYb3_2ok;SShQW(i?y_oX%R$BOJ~che)!+NU=V(t9BPj7IW&JFc zVOU-UysSt*`+>HYvvc z<8FF2Xk4HMTUlnFN2^ff2WSRtXv{N66z9lPwCM4|X7`V4a@Dxvunu{nU3=Izu5V=s z7LC{re^*ZLCDG!7o^#$nj1O-ZCXp9jhGxw^l(Pk>Q)T8KAVceK-Th{?$@AJM{BjZREUw< zVvhBHTlp#HjUy9bESBqJ)Z~wG%~EG9!@q;-zM#nnEzpX3`=z#N&6ra*rrjYY-RDTR z$Rkbg4X-@Ew%(-mdS3S$`+F4C?yC-3fzYs~3XXiF)}@@9+tVJ6lYU zGx3}AvwewkgXWmJ_T|ST+^6}1Wp8#AC`tB`hkv)F7p8RJt7#yp^+G#3CbaeSc7X#v%i~ldnZNwqtxskV(gQlrM#Z6ji3_N z(Z7);-cQoGkX}Ad$b=OiQ@B4uc;9FoE~;LD$^jhoxQo8v1VoW(#D#?_kWkw-AD}Ng z=Ok+0|Gx5e@+J9=;i(YvFrq)^n?F z)+AQzU#!{?1 z3jNERrc>Z|jNF_J0vO?uPPY0lgBG!;&c)T7{MTb`-|d*gyTi^$xw-OokP_qymIT4( zKJP;n<-;@ed?=T|4QXHX!E}^6c3Ghb+mJa^%*;k zR6wKXQCDk+hD_#4Y=SXgi+>%*#B$TR;tW=#Usq`o=wPhRUtjE`(@vbgTG_obvMqVV z>u6}ZV`%z=tfZ1AK?hdnS((pp=hxU`%u!Vff0lq6l1@*=MA#mCh>v=wx>d4=2k4jJ zU5r6W&9=w$N@fwI_yDa`Wu}MDV^Xu`^$i_@0JR$F^$E`eWA?IMpaUd#k=5&vMh3qC zHQl;`YMjRUsk0FhI`3;2n2NDTB0(VEJ8SJP(F!DAT>V)B3?4z@P1bO?!si`9wBbgm zu1~FW5}UTu?fh?A1O_=PRuX;qU~bnh(z~}63uOM^k^W=EQfuK&D8c%q9w^D8XN%X9 zVoYRtD1_{a_v8>|>OoRl+Tl1&>V0qvs|yL3PuO?Z`#t?MYt1J&?ReFS&cEZp&VN&B zug4WbYJLC|s%E|;ON4U+DYMs)8%Nc17tM6VVH4VS z#(ak+d3|~jp8Z;%3TqRgG*%v}RlgG}j^shRNXr_}^e?kU32^u{pQn%VQnSaoWbBT) zVrMab9TeBRKi3>%772cc^h$v)%%%D^iC%xGfMkDsG$io5)yp06Xt256yR7HZZ5D`Y z_l^zcjLFDGUCFOSHr>+u5e{TZ-V!BoWRM+jM0&(XX^>}}Mkw{{6=OU{1n@D={eoRW zsd>m*A>{Fa4x0DJ$6*;^3 z;;a~&|2ueI;iS0#n-VD_^^i1hU*y0u9$71(c81#s@oK=fe&Kly?#eWhtO@ZWu`7Q_ zQUJCf8(Qpkz--}2GfC1yq3bg>gFhzVQSw|hAZNE#ms^X$=5RzU0pjNe_1AS~xF8=0eKWd^8*CXP0no;y1)JhVpA$`|^ za{pYMeEYj64lgR)?ul#7mHF${$T_f{IB^m*_&D&Kn2QbH>1tz@gY8A&^|B^3>dQYd zTv+k=YNFT$#+CRpDv)M1f>(9d&R}0$(s_Tn|2|z~!N<0W!6Z5HhthkBH=3_TN_C zSEg(l-Ull(8kKD0BAgwR4l>UzHbjQ%N+K&I_Ez?2;ien?;zZ%@EftK6LQ+6ku@Ic50Fr`!7izlH&lpvU5 zi>UP~r{)=fq#$C_r}n9$mV5kKz()vqm3DkI7f}a3oJ)n~7Arv>QuCpE+#%~^Aa0l( z2#?8)Ys3-U8yXI!Aae^eaD|3Y%Yd>AMsV{{exM4|xL;NvDu^MRLvE-spw4$JW(}-9 z>k{Izb}&xEH@)7MOwE-nh7aHTU~Lh=b>_%>z80ZWk}!8Kn9)m)5#-^q9beEE+WIBw zwGqQ=ph}pBoSX<1>Wq|8ir4Ne?#1o;3t`*nX3P8E=~|2iSiTys?TD65C{)VFP|=DZ zIgumrsz#yKt8ZcY{Ma@LIF4Ij4jMCmo!esEB8Q+KEI`Xi-xoMZ9Wa6p%5=}_OYH-a z)W?HFB*y@d)XtbXRZiK#n4*1lzdHJcKRmh14nXZuH>M8TIca%z@VG*QQ!_=u-)r5h zNj)~%W^SGCY9@ax8A8<3E@*Zq&tOMeNz+p*&hXPt(^F5}-LW#?f^MjlpuEIhUdpeS z@XqMuYrb|qc8iB|$98DHhBQ#iRmbwVd)(Oh`Fc?x0W! z@9IhsG*rQ%J*uG|2&S?Z>3jcOO`d+>Ppc~s10K49>S>xzum%j~3FHI?yGn&=C+Ld!@syyo`JbH#$sLYC8uYoNhj`_@c5jep z(oZ|X+LbS%+7$q-IAA5IHfi^HZPG@-%?!dOc{is7>6pMg1sKOunpZF8$tMLmx=wfU_Ro=Vruw2@KqQNKIg^-{d!SVk^9E_+?!&B8 zb}X|XSucw%r)uFxisfvnE#p>_tnkH9= z{jZSj!55eiO=nkwWVV~tG`H5y@tPB=S-V%+T5W=N1JvC3NvNt`#cTj|@bog%#2f+9Ll9#EOu$d?tM6}%AyOL`B$B-m)k#AIs?ZXnj> zm8kPL80)0Pzdl%iSn25?bj&J`4TkFpf163vV4G4ljVxa7JsEcLgpHLmE;MwuPqRS$ zaSjA+$g}7Zn|%Ry#f=Vb?J@Im(-7GkhRe$708cNUD|oNMaG2_XTX|Kk?+;BtG{Ebp zChZ=$*y(V0upTJA*6#92BYHy0F6qcc|zyLp6-KQ*Q6i_($_T{qm?vf)FnG08EPLB!gAw5ODROF^~(Z z!omiP7S5sC2j=rO&CaP;rf2q-!54(@6k44w#pI8r9_Xtz<&EmUZ1Kl~45>KPXrny& zyL9Tfy=kIu7Qeh@0ST^?&_j>=l)93M-PGDXEw__h55uc)*uqs8EaK$%U4@-jGQLXo z>w~Iq-EaT|!N!X4?F-;Y3@}TL{2isC532(4D8LENN#)NNizy~wHN2#e|CXD8i_X^;(KcmN7&Im7HGF1nj9LGQrHD*9LTzv_=9Av zT6)ixnP`t$|GW+w`EK-I(q?@BNd(o)6Nko#3@H8d_3!OBJ8*#XxrjAW3J{+sP| zZ(su0Vuv9{KRChfj;*Y&ZUnc`_d7Y*8c0auyzO$-c`FxbhKqYZ21BlzED4=VjNmDf zREwesPsdNzB8S~HO;N*GYJGlryBlG_U#5#GJ4zIAl=i*`F-lUv7#Eqn>nxQ1P+xcOd#TvO0C-zZ0RxN|@8_ zkGwT&%m&31->AT-)>_JwPl)dj@lqnT2nCs9$!I4v$UlOtiBMUq$-e?tSj#M2vfByl z4Ri)?r^|s2%SJO<6hBV@8=Y2ZHr5^cVUAReFL#$^nq#qOQYfB{TVa?hD^! zXO!z4)LzLiQ9~z&*Y9?_e9JxjH2!*T3tz#&%|Z{kB?b5Sv%ah$QX{Alq<@{O!#cO5I3~VUw+?Szy0G+zkM0qnF{px**|x z6+yWqkX0xsE|O}D1i=|saUVQ4V~`P@fZ?1N_yF{oW`t7yL&1elDF_12AbY0EA*_vT*b6udZ6ka|NRhq5LWv+clxaO~sqX-2z=tZ?`~n=7)<3K7p|ny&zuklbBq>{ChBf|Vx(~R>-U(#M0)-1aH-evn@)$%(+ybpNz;y{xhRDg z`&v*6RC%-U+TPd`_PyVy{}%Hnskqo-&<~7_h#bh;fbT`KtSD|ft>RtfpWndaNbK^$ z+T^nx`1sd7wWDvda{5C->9A{RP=?IXL>C5{e@=!{j2 z?AM4Znh2raLYysI7exeBVF@JN^n+~|)Kdcz`)#$* zQ*kV0A|wAiTvXMxs>X;${pC_5EP!3O&zFmeqg4GQ}A~7 zIMyxju9bi<@SLF8kVLh}qvY`4&wHkj3%CX87|jnn*ct@jqXZY>JS!9Pu;xLXmAC|7 zw6R9pR&t2m{|w(u*D|?r0!3wj2a11Dz8q^#OV%TP9@YVU|1lI}88NulI;G>D4YL;f z=voR-IRnN3mOx!*c@n7AvaUfu5gqlHYBq_tx{~RN1j9fngH;h+ePP#$QFByt-o`Z= zIqdq|p{;z!2|lTXCbf24u;3(lP6UmJL!_)dgLCTnb=R`h0!Xbv|g3$s^)Xjd@96`UzE zSmK?^bC(SDSB;&*R~{0yz6q|zfqkq6rn+!)yI}o&)8*J#+=3vCDwY%k$8vc=^{@xF z!Z^CQPdQjtlA+dmIpe6^`oP|k}|#p+)bY{@jB0x!=(k`aFlEI)mc75o*_nFC8( zYIeRvd>*{t+O>HM$?Y~c+J!B{y))}-(SHZ@fRuzFkNaJUgVmOwldpw3e{Peltd=#= zZVQv;S>|FCH-qxeLb#niiDn3o>7w+jE8|X;eaw$mMB}V#fEO|goR6<3?-=#I?*z^E zh>`|}*%ktD(ai|Ar3f}b=+#v2`cdHF2>qiECFIq_I{V{}7le9}FaIPKA6H?v(<|Mz zOE=Gro%X2$M{sYocTBI!5HAddGH&Kjq3wp`p|<$ibU`xm=V=%agnC^zfDuQp=Wa}< z2K)2(CfhMCO!D!K2u<*A@%Lgvb%LB0Q;V8y#JPIbIQeFfcwi>C80#w7SjGuuoF>rG zFJ7C5w2r_Sq}M$3@f$7x<@0TGtli^#VTc63_~v7mFa6<=$_I+!YS~?s~#j%fHidx2<^mfiqx28{n}~s3_~U1 z{oh#R4l2S;R3i5j2_ZC@qO3C|13iB`J*I`=pC5C3NJ9(i!roCHpMF$4g&&NKMi=Wq zv$;KF4OuE^9?1$?EH!LR>E9H(7xC@T!aVo-mYa|(_Yh3HFnSQ_v!G!4Zb$9(j%lJ&+*GA0kc+Y>VTy~(3{^K`P>U!i{dE9XS+d9rel%zy$n^O@a}s+ zX{=C)(uCyDdOO^P28FX~F334Wq%@~m)-8hg0&oWy>RjbJ|JXhEo4UXFU{(S2@dcj9 z^?UR8UzRZpmU$qM)06G;ZEkLCOz_`_$}0+O#K9ez@1yO6JO{o)c)-x10Wx55^0Icv zibd^vRD8MMr>OsaPr(Gd?}L@3f4!97Wto9$b7SF3JKF7vFANfd-O=+X${UoHYFEzm z?riSx(|GfKd_HEFlX=Bc;J~^gP7wRoM9iCL0xo6xg7HlZz?;i|vXiA)JD^=5)e7=S|Cdr*`6=)$Pl z!|EVfQ3qjtY|J1lXw<6Sl@A7c{&wkczo~gJ;k8b9al&|@mP4yjb5JFgIeTCIEdXn( z3CUq8=LEe@5uS~<+kj&d2$6qnph19p(~#ub*0M2&mju$bu=1dcr30<{t??Tbf3w7w z9wfjAlLB5LQhbWx)`27(w00Px@LhX|ozY)A+s(7vtwr}l8j3fe@5TuAEnqUs!ZuNd z2E9WZ?jL!vVqz~S16H(8wJJ;8*}72yT=Ln}&CKQ)EXw&O)4$*7a^J$F^vzx zZo7F+_h^EQqUIF?Ts#|IAahK~qGz;o(lgU4)r2>zt^*w>v;RAU7%hCwjDFxIb{`KP zDrLme$CZ7)o96+_2yKccxeax)7$VJsL8s$5AXvi@sz4 z?kFfVQ4RY)_wjgalyQSH_sO+sFZ+%fkkNtOk&Ue_cZYL6@xdJQ($IO736aQNn{sUr zZ2RG&H0}p@=aMZLJyEj^ILqq?#)Jp*Ys!eTLI(JfJCe!V_>rb^09%Ez|Lal z#6dQN{XYwD!!aT>$=6nqd7n5rC;v~d7)?S}(ShU>_7=48=bLZ713!NN;ger?L_eeZtwEwX(x3dtV+WLSCj4-*_DX2GG= zZ>%J?i%;aS%v#0U5Ig^h{h;1Av7iY6gJtEb+n=}h8^pkxjl+LPK;{7d0)`e+a(_$* z+ge^p4H=B_`J`!7h`}=cFlpiU#3zIrjBZQGu0N&Ypz9dwg-p%8DCagQyA>hH=)9pK z$MuaJ+~1v^*2@lV@9fuifW5;SH`m7Q4pw*$Uj6NJ+}2m7{qhZrTg4i8N*y&yJ9-P| zja1mzZlvR|%6g4njq&cRJXoC(n6u<9>;r!!j7lzaVD*eETX+cK95jTep=#aCO|=?j zyF5$+SZwSG=~Aw6L^gZ{3b;T(y=A7xq38zay-3m5zcMP8{0=?TS0)XT6opg>J<+sx2SmyfS)zJJ@-7Wm;mXRClhc&FDv(os1G1Lk{!42hjp?_LMSBDQ!Q z#=Snv#>vOFE-zHh<{L1(r_SUKAxm7s(2Atq{`Z7~cBQm-QUI%?3%q~39o1aVFANQZ zzCfWQiPBl)&49pS!ttS+rIoqvp4zrh5acM2LD;}M|28(7C~OY=y5jc%G;Sy&n=oT5 zAP9ZU5!sw;w7p!kYoIRgzCdFRSC=KFRWwH)2_(G6Jx#KaZ3pepZL8_{@hJAq6dLt) z1qj1TJL%?Sv{IJEnW&9}^eNh$w^Rx;k_Z$P6$>v_-Ii)u+Y;&6kfUKI-?`eY}HF3f6a3h85(4ol7afS3YkPh zrt^s%O7>}B<^)UB1-d32-@&8w!>CB<|1pIDOMoHnvY(VVNdUrj92 zRa7mNtFpjt)7?{C_l!teQ2HS;mw!~X`$4l@1>5c-;4aaq7hHvAZZYp|0D&4z956q8 z@p%}cgsaiMaz^s6jPAd@%%mxH5z@zPlkFQRuLFJ5vlaD3`ybQI%z6?k4xH)+3JYd~ zU#4S%&&I&H1n+*d_qx!@8KjMEfbIVZvj6JDj}}Z5{F=`eL@BiG`cP6uGGXYnw2qA5 z7F|t&J(*g&DI1e6`oYk~oCHcfkWoCLW^dM(WZc;{sdebn;jLlxb9aPWEz{Tn9c2(8 z6Xx`l{g|$79&Ul4CKJQ>gr6+suA0|5LGHTN*)qy?XE$>KP}`!OrW6nTks-!k6hj$` zR3C0+oOb3rbVYXbNdw-|EE5%Xvsz%=3GyJ4LMm5z?7Qml4V4QM5)3RdJNMXiY zG(K$kvs}Tb^d@FH9?r#0SD68M7)5TRfx+|DjWy5AsF~zVcJRjF7mCk=lz$2C`6{&d zIk)m^F@r~LRJE*U)gNLB4w{0^DM{`ezu*rx+@3(L(>Kd0Rz6J>Z$gz0UsZW>Nn`Qe z+0B}yi#I6LH!xrOG$2q;q7X5rZ$k62zXJ1KrDH{+*vTK*SxCKq5>q9WYX}MO^%eop zlEe62{+GkNBcJt7}}>V&5|3vxM8Sew%gCaqco{tJt78u^f{ePVYhSHLV0*2KHwF z`xt+s$$&qKEjO~%d&OJ3RR*1!{QJKtyY@SMpJ-?p^#sIO_?&K3!?fO~Vl9L$s}>Un z3a#HLmMd)%=pO*%+W-iiS(9VSp&ywh*+uFtP1hxhv$*~(T-Ok~y8t{Z;2Q;uKB;pp z5@6-20UZOLjDbZ<0e&R(1P8kD-Z&A%EJX)y6df(AL2k9#UM%+n#>sNh7*>@DtG;nZ z0z74z>ijykU#W_Bcr)!1eyFL@#p~OEbb?{h5;cm-UL}z#qJvUd2;b+|bpXi!su#Q- zO>Fy5HNd}x_rL0y+1tC=*}IrJ{|6rauY9u6g76@Ou%fj>9qy5I(TsyrcJKyIKyYfm z;X@RRczS4bzq?+)yL=z*__kQk938T0FwymMB?E&-eYkZDbWd<@m%{!qsL#fq;8XijQkXJm8>x8guYPHVm|Z{8aV z_4TrAO95EZW$}fXE_>rH*B1zu9qHI&2mEtm(lZC($4)nV=m}{PwMKST#z-_GSBoSG zxr!DBi%l_O#yYb`UX3JZxp_Nu#MMvNWDd{cLsKyx10bB84?^nf$EQ?TbI~?bTA`F5 zV(Khi-db}56a;XM^VOPdzm9Q}5U>Py=}x$$V8Yg!U@hOx0OADtr%kQ<|3FF1g^vNi zy|4+w$p5M9D}dr!x~*|{cXxto@Zjz)!7aE$a2qVRy9F6ExVyU(+}#p_`{Um4zArcY z|C^aJRZ}%<)j8cg-FvUzXARqz#m3U;s@{ey0e7z~JF+S+_sH_PSczq5Kr=MpoeU)I z84<2{Jsj&aIO~AY2Q!ywSumv!oJT~dVhQqotNhimKxeRieiD58Opqc^llrT1bb|!t zTi##1JW1Ma!bw~%JH4@x?EWx9$dZ4W=2AA065-rpeN+X}1Xe)f2t@mD2W1*}i=(U-<$ z%F#rc>L_x4Y6O+LQVe^n3ttd?NG~Y|d$5|j?pa({T}WKer#~egd@j5SM0>EJ4}D%- zujNrP@U?OIwRvKv4MSViQIBzy_!N1}08d!bSiyI926-IJ299sL_}xfjqv< zMiP)LJK`W;-1Gc!>{U9Q`nK>3gbOXt){)m+>C_#btah$LGL#b8N*wyt#_fb@zn^=H z1sj0!e+}Y&z1gFa=8h0Pa0xbW8CGNo*0>JcY*rqpC6jsEwVPY6hrpc4UvWKvJDOyi zv)2Q`!Ifkj-H)wA*1=^w1|x({MK_Zz$PlKR8Nv`JfEnQf4uAwf1O;FS)59EOh||rC zV2D#;vOuyP=#^dVWh@!qlSpfu^DOifUnn4I%<}gOR@C_QH8fd_H1O2He+HqvgDQ#t z;q|^Z5*rBruiRi#4iIr$?3IOk1hP8%2O%B**rFTf`KZ5`HD;yXm80CQj z0kQc6@~SBP<1ygm>|y<%N#ayb$0?l~!)IN8^W5|MEx@7ZFe-j_X4Ey^`m}xmY=i~> zizX@c*zhAYFRa)*W~BV?Pm+t{r!EHn`d&Kd#3@>q#$@wL%ABoj-xt zG}J>@#=0zsBqNBY6(Tb`gXEIm@h)FHDA_5Pe)bf0-!?S)uwImRn)$7R|J&;G^7?0f zx=kpp@AmLWNatE|iXlJjRk79k5*=vYF~!O}uFY6&=+w6rl66KrueqZ7bl7{hD9|P; zD9Wq7o@2sMF@0Osz=mH zVtXI7-?ttOJSv5`otwo}i>MB2hDU7-ec6ZYzu-4m^rtY8A%TRtieQdPCZb-ZEk%9A zDi)_PQYYOiKA-BMtldr>pIz)cg*M^u91EW`!}ew$LdV+VH5y29xlO^2H_&U5K}hcnERC~Q9C#Br zsy;cRxd-KtNk$N4mJ?9tJx)dT#n8}M%YqPqwj9EuwyA*$#dL37ZN zGsrmI5v|s`V6Frq}H39n4u63AzCRgC8Fei(LjpIh;Yn z#vMFtpOTdebC~jmG>&7Y&yHl`B<77i*=NM`FCWhgnN0lZFAjcI1Lq91*fr>uuOE7^^?h;j$PNQUwAKqQn$|I9x}cCxwx^N7c)AG3j+L7Xz?0Ig53d ztp&eL-kq88m<#Gbb#Do|!X!r#Ib4L(7MZLG2pxEvcq0T2BeT!_OjHT&_VbYAGB21z z#Lv#GE_$@N4;pi*v*y}6nv?O|w$)}`R02z1e63b89IHr)P4k#h<7LsSD=egM$jRHs zTP>+Qtm%{kA~sR2#>hNQ1E}N3k(QmUF&6Hc9$6#+z>+5?p4fYm7MLWyb`NAwUNyp` zc`IzzOF(1*T_$EP876h?#SEv?2p&lD;I-`9-v-a^y{OpWFw6R;qPKCvO$|rR3TH?3H6Jxc; zBe+KjpG4{7wYb(h?{Y<-z~N-l89Og=iWhY-<55SL*-LRr-dtC(lPSoRyR8)%hYIuweHK*7uitF8j~%gT5SF z;uvgj6IED}d@K;w68uyj$60o_Vcjoj4wSQBbcDukd2*t#4*LDgtBQ&e`S@+RW@gx~oZY;D8ovtG0+_DYu1ulj*TYx&N_Y0O;BB)qf$6+k zlKFAukyr$l89;JgdsfwxJr7E*2_5^3N7>cKY6-7oq-vsB+g8@7Jo-vCTW73W$<5k| zgFNeJ&!){;$JF(Pll!BPjeY}neH2^Z9Q9@?+d}(#U%~cFqi`t`Dj6f0;?Pf)_JYqYQC3~`1Xz=Rqu&H^a$%`smJP=hgcBqKpeZ_}4Hi?t8^!U;h!Zm~ zVQt+zKqoZUs%zTl{7EmouCS7blqxl2NYI@fSpDr36%(7nTvru!ltk~@kAAZlDBBgE z{4+AVfTgvy;_K4=H60HZeaG7B$184y3fF2kvss0JemT3Y?Ow7g78>o8@onL?0&2adoBmg3v11oXxuvMvjLQiGL~Y~+I17c=8GcVOph3mrkS z^V|b_g*20@Bcz4QZv4UCFR;{;G(EDNlb%?PV5NHq{d=OdF?4 z9JauXv5X*71)hI#oQmbcMB*w1KCKT9HlH9u<&#!O<17C2J*JM8uDyjfXq2k8JdpfS z6nu6K5#C!$?2aZr6`hCr*YpR!LD9XS@vzZ2E-+o-tfe+#7ovz2(Tue$a8k*eUuL4X z93RVl_rnO3eAWtBljjz?EE_CvCu913?snBhu%PYKdrC5s412$YoG7?L;4xt88yuG> zvZ3V{DVoT#dvSPwsME!P{$0?xc#MJu%IRYa9o15vLZ$@?XhTAsb{fxdD- zz9Q9&*nV|ah&>vc!NOW|TZeIe$_Ps%r3)nr(^RSDMaUNTrQebMD8I*Jj1T3G`v9m4D{MWJ!C?P!smJw5y21UBnce(gOZ)iyBgWcAZ3qF{T+99Z&UhK5Ohnd*OXr7 zHR1bjviCc zdh+eCE?V+#Buz(%gVc{WEksE@$J0KJu5uWXq*m(4ngd`D}`aBq3o_%;Xw1_fjhKHDjHP;8s)|MIvO)MZb-i1 zma$Z8y7ydzO&ZUQD=uGklD#~QMM@_%U^(>v4AzNl!O<6AgLH>igS7t|f*IS{{N)Gs zcTS%<)fL`Df&#Un@!FL${0U-d*UHzh^~fas?85!Axac?M-={SXaw&HxSTB6Jx6dD; zjX89TLfSDO^vLOs5nadEwH=@@j-ARS zzz8oAZCOcO{3kW{M!RS_()LI`@G@aR%`-3Dj4!~a9Xe}j8SpO+jpn6 zX8zdm{EH@BI_nwq&vuxPC`rJ39ZeGdw8N_^r08g8@5E$m=V@+W~CMF%J==V z4YLka`wWx#{kA};ojCF$YJ(#)viIZ#DY0kEZs0<2aD*1jI2KvE&gW$AgcVo_!^B1E z?#}jeJYh8tD6`fl4+6&;fgIP_GZV@C4TiI-seUUkBrddK{&2j-GR;9OXhhuFg0>S< ztz-NIp2w!4!(y&k7MB6CM$~-^H($NVh_cfVffpgXN!5H6fGQ(R{UT$=BkY_gg=V!E z5_!L>?U8lGmp57wsENF|$!kN-zBVMv>%jVNLmJxK|J9C}%70rVw%*X^t%S*VlsM{< zqHKY{0BJKAJmWRF>rL??d?##Y03_V8GiRgYCuV5v&j&W=NG!NN)OQ^?nBeV)t+toc zn#%2>8As|;&WjGfd#y<@Y3wc4T;GRK8cVrMj;NBJ##nltnD%qlk8(vS*>BJ3AT!qV zdHIZQkO$7>M0Z7tOeF|kRtjJ)a+11$ za&P^#z)eOZO{4)2zM|dKj)K2T(dl@Xxuw-iAh*Qk!pWBUbT@}j3d{!P`|_O$}j0qS7-Tduu|D+PV zuX8fX6_+^&-&NCp)fESTsp*f|axws6QRO0Nlt93n_io0Pr?x*00){MZqUXl@&L?$# z?l>9@EDB{i1>n-tY>fvJOhmY0ugZ0H(;Qp^+UU0lf`*sZ%p$hZ=yN))<}P+iBP7%< z;sZI4s2_SDT{X!69^uXuFIRCKkrqBDCfZt+fZ6?>CAjjdw#xv85&{KdNTT%vDP%z; z2U`X2J0mFb{;OLIH3lD%j5vQcQme^6`*g|}H9eHE)pG*h1+5(_R1^si-|+>M|14Lc z8wA3Ul==ve`iRp9DWOCfiy>N;FFWNDPn^go79(j<>TEr$|1*`aMZ3OwdLf)0&%Frm z#mr5iCwE^eLc5gq-2g{vy>u)ngN1Y4ES)-SvYA#|oOoK=a_xu*djVxyb${8acG2n1 zR6?>v#vD81jP1O%2m3f$WJ_A%ilyp_JW*tN-Z?DN03KR{a(Odd7+9ig)*tOzma6=QBRm+hpLNy&2Wi~Fmg84YR$UqO%Slbyp# z1MFz8T=E6~O&YnMXGM0;9_N&|4-LfY{4cJEF-87%zIR8b#J;w*xr!9eXS>T9OzU1R zSNej2-#76Pu4fvFkWirACRrLKz%3zGlZq$CVNdT+igF|EdktZXzah<|wu7~>1w$Nx z$bBX10jEN*!E!KmET(bUs0r)j>9rA5MDa|BqcZ&rz7o|ZxonFTH9y!#W!cFO6J2Xm z#~L-8D$2@Oev8zh4u=FaDko?k7qhRqtn!YkU2j zd^dX%WQemz=oIJ{;tQMqGwgI7za9kUrokEqE2$Fc!@WGX8I|jpi zyBm!$gkNYMpDLPr?+ZL?h%1Dnv^ zLtBV`4{PQcGnuDF03Vz`b1^S#HZPhm0h8dGKhU1-ibT$1kba?sQE8)v~ljUKQ_UP4E-eCyyGZB#g>4!(pUl zwymF#0g3C>q9HFnQEoGx_@h_v*_H8SxfQxuy^bFI;C1oqM(OR#R9^ii#fn5VfxdGw zHsO(2NV6E>(NdZ7RPtUP)BSWr`(@3U%p8%kt3Gc>1b<5w;{5735(_D`k@O^^#L;DmnG*CmQW%`*n@Z6lMj=NJ)V^Tq088Ts zZ`Kb>RYc|*YQx$SAJz81e`gg;?XHu^_-ReCy;-&LtB3qchpo#n8~va&N&F?`_?$Yq8|KO2+i!`=jY7CLQXh=F_msG^$S+GYjdKxiP+g+`CnI-@v~jX7^1M}5eaUErsgm<4K{YaugZM+6cZBn=apLuUaS z@9xw*;3)+Wso_b(yxf=MKP{Ewt~xU67RR~d6P#+Ku&|YV7V({Zawo=Fi*PtpRml|j zh@HZql(>slmv?4;N1c(dmIfs|wYpfw_x;z%ML*&ywauKe*;H|ENR3bBfm4JQpAQz zkyThPlIu@i10wEymrCZx!?1MBSCQ=;;;l<+yU*12E5IdQCM>n)I#JJ^t6vdbv~3Cg zR4_|ZYu#YOWdd|+iKK?&md4TtO>_Nzl(1Z=tINk$ zTid-6w$)cbV0&-^E+VGd!lEz`aB;!oMmz zm_3MRnn8-t+WY;4crtva8()hzQgf6Q1?dl?A^69$&G&2tNdA zI^M?t@GYd5IsB;&ZGQO2oRaBU=WSB*7+&~|Nm!$P>kDN7uP3M)@zaZ1pff1NS|-+1 zDHWNLIYYkKTmbGCbIix^v$q0U4Jy+Q2xotVvwVDvYCtp%_}H>!Lw#yU)DNYMyB64g zB5`9bqDa55@{Nx+x+wE$1#WCg34A?Bw5?-)S*R92!HtS{^C+xg_G$=FI?<9fb^LY z17r}W1b(6Pn%(IcMLG{X_@Pmt1TrlPX_tCVz_Ro>Ekm#}`{)MZZljRFeeZ$X77lft zAwhB|I7-z?CXa=;>e>Ryv25s%MhBz@D>u|5jmQPVqcJ%1qeZ7lZD`_;B^|t1_by=W3fCZPLp^Kx;8TH#6ey+Z0TeyKWI9lb=PF)aqMz{Rprau!kJ1VsE?>JCZ! zvlTbnn9oTdhfC<5fGT>lEzUcjli6B<+E47Ho70}<3vZlk=;M)VL;NydMwQ54ZK-(y zyvp7=%Gu<;l6oLVarsB5qGIQlBl6eVFzWe&iZYqf+B7{_U>vZ#<32KltB!%x}SOwZh-vXt{sDe_X*^{9Bdm zH-1k3AN;>Hv$ycK;?i&Us=~kX@qeVIxA3=;%5ON8(!b#U2rO^WZ`1$Z=tkv#p?`}3 zZwcO};lBwSRQ|d6w@LY1insB{Z;BS}|LwegEf;U$Z{{v~1!B_wQ literal 0 HcmV?d00001 diff --git a/docs/Flip7 CYD Scorekeeper Architecture.md b/docs/Flip7 CYD Scorekeeper Architecture.md new file mode 100644 index 0000000..e5078e2 --- /dev/null +++ b/docs/Flip7 CYD Scorekeeper Architecture.md @@ -0,0 +1,778 @@ +# Flip7 CYD Scorekeeper +## Android Companion and Bluetooth Integration +### Solution Architecture Document +Project: flip7-cyd-scorekeeper +Repository: abandonrules/flip7-cyd-scorekeeper +Status: Proposed Architecture +Primary platforms: ESP32-2432S028 CYD boards and Android +Primary languages: C++, Kotlin +Communication: ESP-NOW and Bluetooth Low Energy + +## 1. Executive Summary +The Flip7 CYD Scorekeeper currently consists of two ESP32-2432S028 Cheap Yellow Display boards that synchronize games over encrypted ESP-NOW. +Both CYD boards have now been verified to support: +Bluetooth Low Energy +Bluetooth Classic +Dual-mode Bluetooth +Wi-Fi +ESP-NOW +Dual-core ESP32 execution +The proposed architecture adds an Android companion application without replacing the existing CYD-to-CYD ESP-NOW connection. +The Android application will communicate with one or both CYD boards using Bluetooth Low Energy. The existing C++ game rules, state validation, packet serialization, reconciliation logic, and protocol definitions will be extracted into a shared platform-independent core. +The shared C++ core will be compiled directly into: +The ESP32 firmware. +The Android APK through the Android NDK and JNI. +This approach provides one authoritative implementation of the game protocol and game rules while allowing each platform to maintain its own user interface and communication code. + +## 2. Goals +### 2.1 Primary Goals +Allow an Android application to communicate with the CYD boards. +Preserve the existing ESP-NOW connection between the two CYDs. +Reuse the existing C++ game and synchronization logic. +Prevent the Android and ESP32 implementations from diverging. +Support Android as either a player or spectator. +Keep game logic independent of BLE and ESP-NOW. +Establish a reusable architecture for future synchronized games. +Preserve offline operation between the CYD boards. +### 2.2 Secondary Goals +Support match history and statistics. +Allow Android to display synchronized game state. +Support future persistence and cloud backup. +Allow future transports such as Wi-Fi, USB, or WebSocket. +Make protocol behavior testable on desktop, Android, and ESP32. +Keep Android-specific code primarily in Kotlin. + +## 3. Non-Goals +The first implementation will not: +Replace ESP-NOW with Bluetooth. +Require internet access. +Require a cloud service. +Move the Android user interface into C++. +Expose raw C++ pointers or native structs to Kotlin. +Depend on Bluetooth Classic. +Support arbitrary untrusted CYD devices. +Guarantee resistance against modified firmware or physical device extraction. +Rewrite the shared core in Rust or Kotlin Multiplatform. + +## 4. Current System +The existing system contains two CYD boards connected through ESP-NOW. +┌──────────────────┐ ESP-NOW ┌──────────────────┐ +│ │◀─────────────────────────▶│ │ +│ CYD Board A │ │ CYD Board B │ +│ │ │ │ +└──────────────────┘ └──────────────────┘ +The boards synchronize: +Heartbeats +Active game selection +Puzzle state +Mastermind state +Revisions and epochs +Exit state +Recovery state +Game-specific actions +The Android application does not currently have a compatible transport into this network. + +## 5. Proposed System +One or both CYD boards will expose a BLE service that acts as a packet transport for the existing Flip7 protocol. +Bluetooth Low Energy + ┌────────────────────────────┐ + │ │ + ▼ │ + ┌───────────────┐ │ + │ │ │ + │ Android APK │ │ + │ │ │ + └───────────────┘ │ + │ + ▼ +┌──────────────────┐ ESP-NOW ┌──────────────────┐ +│ │◀─────────────────────────▶│ │ +│ CYD Board A │ │ CYD Board B │ +│ │ │ │ +└──────────────────┘ └──────────────────┘ +The Android application sends and receives the same logical protocol packets used by the CYD system. +BLE and ESP-NOW remain transport mechanisms only. +Neither transport owns the game rules. + +## 6. Architectural Principles +### 6.1 One Authoritative Core +Game rules and protocol behavior must exist in one shared C++ implementation. +The same source code will be compiled for both ESP32 and Android. +### 6.2 Transport Independence +Game logic must not directly call: +ESP-NOW APIs +BLE APIs +Android APIs +Arduino APIs +LVGL APIs +Wi-Fi APIs +Instead, transports deliver serialized packets to the shared core. +handleIncomingPacket(packetData, packetLength); +### 6.3 Explicit Serialization +Native C++ structs must not be copied directly onto the wire. +Unsafe example: +memcpy(buffer, &state, sizeof(state)); +The protocol must explicitly define: +Integer width +Field order +Byte order +Packet version +Packet length +Packet type +Validation rules +Optional checksum or digest +Example: +writeU32LE(output, state.gameId); +writeU32LE(output, state.revision); +writeU8(output, static_cast(state.phase)); +### 6.4 Narrow JNI Boundary +The JNI interface should move bytes and simple values rather than expose the complete C++ object model. +Preferred JNI inputs and outputs: +ByteArray +Boolean +Int +Long +Small immutable result objects +JSON or another UI projection where appropriate +### 6.5 Platform-Owned User Interfaces +The ESP32 uses its existing display and touch interface. +Android uses native Android UI, preferably Kotlin with Jetpack Compose. +The shared C++ core does not render screens. + +## 7. Logical Architecture +┌─────────────────────────────────────────────────────────────┐ +│ UI Layer │ +├──────────────────────────────┬──────────────────────────────┤ +│ ESP32 LVGL / Touch UI │ Android Jetpack Compose UI │ +└──────────────────────────────┴──────────────────────────────┘ + +┌─────────────────────────────────────────────────────────────┐ +│ Application Layer │ +├─────────────────────────────────────────────────────────────┤ +│ Start game │ +│ Join game │ +│ Submit move │ +│ Submit Mastermind guess │ +│ Exit game │ +│ Request snapshot │ +│ Observe game │ +└─────────────────────────────────────────────────────────────┘ + +┌─────────────────────────────────────────────────────────────┐ +│ Shared C++ Core │ +├─────────────────────────────────────────────────────────────┤ +│ Game rules │ +│ State validation │ +│ Packet serialization │ +│ Packet deserialization │ +│ Revision comparison │ +│ Epoch management │ +│ Reconciliation decisions │ +│ Digest calculation │ +│ Puzzle generation │ +│ Move validation │ +└─────────────────────────────────────────────────────────────┘ + +┌─────────────────────────────────────────────────────────────┐ +│ Transport Layer │ +├──────────────────────────────┬──────────────────────────────┤ +│ ESP-NOW │ Bluetooth Low Energy │ +│ CYD ↔ CYD │ Android ↔ CYD │ +└──────────────────────────────┴──────────────────────────────┘ + +## 8. Shared C++ Core +The shared core should contain platform-neutral code only. +### 8.1 Suggested Modules +core/ +├── include/ +│ └── flip7/ +│ ├── protocol.hpp +│ ├── packet.hpp +│ ├── serializer.hpp +│ ├── reconciliation.hpp +│ ├── active_game.hpp +│ ├── mastermind.hpp +│ ├── puzzle.hpp +│ ├── aquarium.hpp +│ └── result.hpp +├── src/ +│ ├── packet.cpp +│ ├── serializer.cpp +│ ├── reconciliation.cpp +│ ├── mastermind.cpp +│ ├── puzzle.cpp +│ └── aquarium.cpp +└── tests/ + ├── protocol_test.cpp + ├── serialization_test.cpp + ├── reconciliation_test.cpp + ├── mastermind_test.cpp + └── puzzle_test.cpp +### 8.2 Responsibilities +The shared core will: +Validate incoming packets. +Decode protocol messages. +Apply valid messages to local state. +Reject malformed or stale messages. +Generate outgoing packets. +Calculate packet digests. +Compare revisions and epochs. +Determine reconciliation actions. +Execute game actions. +Validate state invariants. +Produce safe display projections. +### 8.3 Prohibited Dependencies +The shared core must not include: +#include +#include +#include +#include +#include +#include +Platform wrappers may include these dependencies, but the core must not. + +## 9. BLE Architecture +### 9.1 BLE Role +The CYD acts as a BLE peripheral. +The Android application acts as a BLE central. +### 9.2 BLE Service +The firmware will expose one custom Flip7 service. +Suggested conceptual design: +Flip7 Service +├── Command characteristic +├── Event characteristic +└── Device information characteristic +A minimal first version may use only two packet characteristics: +### Command Characteristic +Android writes packets to the CYD. +Supports write with response initially. +Used for actions, requests, and state updates. +### Event Characteristic +CYD sends notifications to Android. +Used for snapshots, acknowledgments, errors, and game events. +### Device Information Characteristic +Optional read-only characteristic containing: +Protocol version +Firmware version +Board identifier +Device role +Active game +Feature flags +### 9.3 Why Two Packet Characteristics +Using separate command and event characteristics makes direction and permissions clear while retaining a generic packet-oriented design. +Android ──write──▶ Command +Android ◀─notify── Event +### 9.4 Packet Fragmentation +BLE payload capacity may be smaller than some protocol messages. +The transport layer must support: +Negotiated MTU +Packet length prefix +Message identifier +Fragment index +Fragment count +Reassembly timeout +Duplicate fragment handling +Maximum message size +The shared game core should receive only a fully reassembled packet. +BLE fragments + ↓ +BLE reassembly + ↓ +Complete Flip7 packet + ↓ +Shared C++ core + +## 10. CYD Gateway Behavior +### 10.1 Preferred Initial Design +Both boards may advertise BLE, but the Android application connects to only one board at a time. +The connected board becomes the Android gateway. +Android + │ + │ BLE + ▼ +CYD A + │ + │ ESP-NOW + ▼ +CYD B +### 10.2 Gateway Responsibilities +The BLE-connected CYD will: +Receive packets from Android. +Validate them. +Apply valid actions locally. +Forward relevant state through ESP-NOW. +Forward synchronized state changes back to Android. +Prevent packet loops. +Track packet origin. +Avoid applying the same message twice. +### 10.3 Packet Origin +Each message should identify its source. +Possible origin types: +enum class PacketOrigin : uint8_t { + Unknown = 0, + CydHost = 1, + CydGuest = 2, + Android = 3, +}; +Each packet should also include a unique message identifier or an origin-specific sequence number. +This prevents the following loop: +Android → CYD A → CYD B → CYD A → Android → CYD A +### 10.4 Gateway Failover +The first release does not require seamless gateway failover. +A later release may allow Android to reconnect to the surviving CYD if the original gateway disconnects. + +## 11. Android Architecture +### 11.1 Suggested Android Stack +Kotlin +Jetpack Compose +Android BLE APIs +Android NDK +CMake +JNI +Kotlin coroutines +StateFlow +Repository pattern +### 11.2 Android Layers +Android UI + ↓ +ViewModel + ↓ +Game Repository + ├── BLE Transport + └── Native Core Adapter + ↓ + JNI + ↓ + Shared C++ Core +### 11.3 Android Responsibilities +The Android application will: +Scan for Flip7 CYD devices. +Display discovered boards. +Connect to a selected board. +Discover the Flip7 BLE service. +Negotiate notifications. +Reassemble BLE fragments. +Pass complete packets to the native core. +Display validated state. +Create user actions. +Ask the native core to generate valid outgoing packets. +Send packets through BLE. +Manage Android lifecycle events. +Handle reconnection. +Store optional local history and preferences. +### 11.4 Native Interface +Suggested first JNI API: +object Flip7Native { + init { + System.loadLibrary("flip7core") + } + + external fun protocolVersion(): Int + + external fun validatePacket(packet: ByteArray): Boolean + + external fun decodePacket(packet: ByteArray): ByteArray + + external fun applyIncomingPacket( + currentState: ByteArray, + packet: ByteArray + ): ByteArray + + external fun createActionPacket( + currentState: ByteArray, + action: ByteArray + ): ByteArray + + external fun stateForDisplay( + currentState: ByteArray + ): String +} +This API is illustrative. The exact interface should be refined while implementing the shared state container. + +## 12. Android Operating Modes +### 12.1 Spectator Mode +Android receives state but cannot modify the game. +Possible features: +Current game display +Scores +Puzzle progress +Mastermind guesses +Round history +Connection health +Match timer +Statistics +This should be the first Android operating mode because it has the lowest synchronization risk. +### 12.2 Player Mode +Android behaves as a participating client. +Possible actions: +Start a game +Join a game +Submit a move +Enter a code +Submit a guess +Advance a round +Exit a game +Player mode requires authorization and stricter conflict handling. +### 12.3 Controller Mode +Android controls the two-board system without representing a player. +Possible actions: +Select game +Reset match +Configure players +Manage settings +Export history +Controller mode may be introduced after spectator mode. + +## 13. Protocol Requirements +Every packet should contain a stable envelope. +Suggested logical packet envelope: +struct PacketHeader { + uint16_t magic; + uint8_t protocolVersion; + uint8_t packetType; + uint16_t payloadLength; + uint32_t messageId; + uint32_t senderId; + uint32_t epoch; + uint32_t revision; +}; +The actual wire representation must be serialized explicitly and must not depend on struct layout. +### 13.1 Required Fields +Protocol magic +Protocol version +Packet type +Payload length +Message identifier +Sender identifier +Game identifier where applicable +Epoch +Revision +Payload +Integrity check +### 13.2 Protocol Versioning +Receivers must reject unsupported major versions. +Minor versions may be accepted when backward compatible. +Example: +Major version change: +Breaking packet structure or behavior + +Minor version change: +New optional packet or capability +### 13.3 Capability Exchange +During connection, Android and the CYD should exchange capabilities. +Examples: +Supported protocol version +Supported games +Spectator support +Player support +Maximum packet size +Fragmentation support +Compression support +Firmware version +Native core version + +## 14. Synchronization Model +The CYD pair remains the authoritative game system in the first release. +Android receives synchronized snapshots and submits actions. +Android should not overwrite the entire game state unless explicitly participating in recovery. +### 14.1 Action-Based Writes +Preferred: +Android sends: +"Submit guess: red, blue, green, red" +Not preferred: +Android sends: +"Replace the entire Mastermind state with this object" +The authoritative core validates the action and produces the resulting state. +### 14.2 Snapshots +CYDs send snapshots: +On Android connection +After accepted actions +After reconciliation +After active game changes +After peer recovery +On explicit snapshot request +Periodically when appropriate +### 14.3 Conflict Resolution +Conflict resolution uses: +Game epoch +State revision +Packet validity +Active game identity +Deterministic reconciliation rules +Sender role where necessary +All platforms must use the shared C++ implementation for these decisions. + +## 15. Security +### 15.1 BLE Pairing +The initial implementation should require BLE bonding or an application-level pairing code before player or controller actions are accepted. +Spectator access may optionally be less restrictive. +### 15.2 Authorization Levels +Suggested roles: +Spectator +Player +Controller +Administrator +### 15.3 Sensitive Information +Mastermind secrets must not be exposed to an Android spectator before the round ends. +The protocol should provide a sanitized view for unauthorized clients. +The shared core should generate role-specific projections rather than relying only on the UI to hide sensitive fields. +### 15.4 Replay Protection +The system should reject: +Duplicate message identifiers +Old sequence numbers +Stale revisions +Packets from previous epochs +Invalid sender identifiers +Packets with bad integrity checks +### 15.5 Existing ESP-NOW Keys +Existing ESP-NOW PMK and LMK values remain outside the repository. +BLE credentials, bonds, or application keys must also not be committed. + +## 16. Reliability +### 16.1 Reconnection +When Android reconnects: +Discover the Flip7 BLE service. +Read device information. +Subscribe to event notifications. +Request the active state snapshot. +Validate the snapshot through the shared core. +Resume spectator or player mode. +### 16.2 Brownout Considerations +Bluetooth and display activity can increase peak current demand. +The firmware should: +Avoid unnecessary simultaneous radio operations. +Monitor resets and brownout causes. +Use a reliable USB power supply. +Avoid powering high-load peripherals from marginal USB ports. +Log Bluetooth initialization failures. +Recover cleanly after controller initialization errors. +### 16.3 Watchdogs and Timeouts +The BLE layer should define: +Connection timeout +Fragment reassembly timeout +Action acknowledgment timeout +Snapshot request timeout +Reconnection backoff +Maximum retry count + +## 17. Repository Structure +Recommended repository organization: +flip7-cyd-scorekeeper/ +├── core/ +│ ├── include/ +│ ├── src/ +│ └── tests/ +│ +├── firmware/ +│ ├── src/ +│ ├── include/ +│ └── transports/ +│ ├── espnow_transport.cpp +│ └── ble_transport.cpp +│ +├── android/ +│ ├── app/ +│ │ ├── src/main/java/ +│ │ ├── src/main/cpp/ +│ │ │ ├── CMakeLists.txt +│ │ │ └── flip7_jni.cpp +│ │ └── src/test/ +│ └── build.gradle.kts +│ +├── protocol/ +│ ├── specification.md +│ ├── packets.md +│ └── golden/ +│ +├── test/ +├── platformio.ini +└── README.md +The repository may retain its existing structure during migration. This represents the desired end state. + +## 18. Testing Strategy +### 18.1 Shared Core Unit Tests +Test: +Packet encoding +Packet decoding +State validation +Mastermind scoring +Puzzle move validation +Puzzle scrambling +Epoch transitions +Revision handling +Reconciliation +Duplicate detection +Role-specific projections +### 18.2 Golden Packet Tests +Store known protocol examples: +Input state +Expected exact packet bytes +Expected decoded state +Expected digest +The same golden data should be tested on: +Desktop C++ +ESP32 +Android JNI +### 18.3 BLE Tests +Test: +Discovery +Connection +Service discovery +Notification subscription +Write acknowledgment +MTU variation +Fragmentation +Reassembly +Disconnect during transfer +Duplicate fragment +Missing fragment +Reconnection +Multiple nearby CYDs +### 18.4 Integration Tests +Required scenarios: +Two CYDs continue playing with no Android device. +Android connects as spectator. +Android disconnects without interrupting the CYD game. +Android reconnects and receives current state. +Android sends a valid action. +Android sends an invalid action. +The BLE gateway CYD restarts. +The non-gateway CYD restarts. +One CYD recovers state from the other. +Android receives the reconciled state. +Android attempts to replay an old packet. +Android receives a Mastermind view without the hidden secret. +### 18.5 Fuzz and Malformed Input Tests +Test: +Truncated packets +Invalid lengths +Unsupported versions +Unknown packet types +Invalid enums +Oversized messages +Invalid digests +Invalid fragment counts +Impossible state combinations +Unexpected reserved bytes + +## 19. Delivery Phases +### Phase 1 — Core Extraction +Identify platform-neutral C++ logic. +Move it into a shared core. +Remove Arduino and ESP32 dependencies. +Preserve current firmware behavior. +Add desktop unit tests. +Exit criteria: Existing CYD firmware builds and behaves identically using the extracted core. +### Phase 2 — Protocol Hardening +Define explicit packet serialization. +Add packet versioning. +Add packet integrity checks. +Add message identifiers. +Add origin tracking. +Create golden packet tests. +Exit criteria: Packet encoding is deterministic across desktop and ESP32 builds. +### Phase 3 — BLE Spectator Transport +Add the Flip7 BLE service. +Add command and event characteristics. +Implement fragmentation and reassembly. +Send snapshots to Android. +Keep Android read-only. +Exit criteria: Android can connect, reconnect, and display live synchronized game state. +### Phase 4 — Android Native Core +Configure Android NDK and CMake. +Compile the shared core into the APK. +Add the JNI adapter. +Run golden protocol tests on Android. +Validate incoming packets natively. +Exit criteria: Android and ESP32 produce matching protocol results. +### Phase 5 — Android Player Actions +Add action packet types. +Add authentication and role assignment. +Validate Android actions. +Forward accepted changes through ESP-NOW. +Add conflict and retry handling. +Exit criteria: Android can participate without causing state divergence. +### Phase 6 — Persistence and History +Save match history on Android. +Add export. +Add optional CYD persistence. +Add statistics and dashboards. +Exit criteria: Completed games survive Android restarts and can be reviewed. + +## 20. Risks and Mitigations + +## 21. Architectural Decisions +### ADR-001: Retain ESP-NOW Between CYDs +Decision: ESP-NOW remains the primary CYD-to-CYD transport. +Reason: It is already implemented, tested, encrypted, and supports offline board synchronization. +### ADR-002: Use BLE for Android Communication +Decision: Android communicates with a CYD using BLE. +Reason: Both boards have verified BLE support, and Android provides native BLE APIs. +### ADR-003: Share the C++ Core Through JNI +Decision: The Android APK compiles and invokes the shared C++ core using NDK and JNI. +Reason: This avoids duplicated game and protocol implementations. +### ADR-004: Do Not Serialize Native Struct Memory +Decision: All protocol fields use explicit serialization. +Reason: Native struct layouts may differ across ESP32 and Android architectures. +### ADR-005: Start With Spectator Mode +Decision: The first Android release is read-only. +Reason: This validates BLE transport and JNI reuse before introducing multi-writer synchronization. +### ADR-006: Keep CYDs Authoritative +Decision: The CYD pair remains authoritative during the initial Android releases. +Reason: The boards must continue operating when the phone disconnects. +### ADR-007: Use BLE Rather Than Bluetooth Classic +Decision: BLE is the default Android transport. +Reason: BLE provides a structured service model, broad Android support, low-power operation, and clean notification-based state updates. + +## 22. Definition of Done +The architecture is considered successfully implemented when: +Both CYDs continue synchronizing over ESP-NOW. +Android discovers and connects to either CYD over BLE. +Android receives the current active game state. +Android reconnects after leaving BLE range. +Android and ESP32 compile the same shared C++ core. +Golden packet tests pass on Android and desktop. +Packets are serialized explicitly. +Android spectator mode does not expose protected game information. +BLE failure does not interrupt CYD-to-CYD play. +Duplicate and stale messages are rejected. +The firmware remains stable under simultaneous BLE and ESP-NOW activity. +Documentation explains the protocol, BLE service, and JNI interface. + +## 23. Recommended Initial Work Items +Extract Mastermind, puzzle, active-game, and reconciliation logic into core/. +Define a versioned packet envelope. +Replace raw struct transmission with explicit serialization. +Add golden packet fixtures. +Add a BLE device-information service. +Add command and event packet characteristics. +Implement a read-only Android BLE scanner and connection screen. +Build the shared core through Android CMake. +Add a minimal JNI packet validator. +Display the synchronized active game in spectator mode. + +## 24. Final Recommendation +Implement the Android companion using: +ESP-NOW for CYD-to-CYD synchronization. +BLE for Android-to-CYD communication. +A shared C++ core for protocol and game logic. +JNI and the Android NDK for native code reuse. +Kotlin and Jetpack Compose for Android-specific behavior. +Explicit, versioned packet serialization. +A spectator-first delivery strategy. +This architecture introduces Android support without weakening the existing offline CYD system and creates a foundation for future games, transports, dashboards, and client platforms. + +| Risk | Impact | Mitigation | +| --- | --- | --- | +| BLE and ESP-NOW compete for radio time | High | Keep packets small, queue transmissions, test under load | +| Native struct layouts differ | High | Use explicit serialization | +| Android and ESP32 behavior diverges | High | Compile the same C++ core on both | +| JNI becomes overly complex | Medium | Keep a narrow byte-oriented API | +| Packet forwarding causes loops | High | Add origin and message identifiers | +| Android sees hidden game information | High | Generate role-specific projections in the core | +| BLE disconnects during a game | Medium | CYDs remain authoritative and continue offline | +| Brownouts occur during radio use | Medium | Improve power delivery and log reset causes | +| Firmware size increases | Medium | Track binary size and disable unused Bluetooth Classic | +| BLE packet size is insufficient | Medium | Implement transport fragmentation | +| Multiple phones connect simultaneously | Medium | Limit initial release to one Android connection | +| Old clients send incompatible packets | Medium | Enforce protocol versions and capabilities | \ No newline at end of file diff --git a/include/countdown_wire.h b/include/countdown_wire.h index 319d5d0..17050e8 100644 --- a/include/countdown_wire.h +++ b/include/countdown_wire.h @@ -64,7 +64,7 @@ struct CountdownWireState { CountdownWirePhase phase; // offset 36 uint8_t roundType; // offset 37 uint8_t roundSubPhase; // offset 38 (CountdownRoundSubPhase cast) - uint8_t reserved; // offset 39 must be zero + uint8_t roundConfig; // offset 39 round-specific config (e.g. largeCount for Numbers) }; // Host-authority record — carried in transfer packets; every authoritative @@ -89,8 +89,7 @@ inline bool isValidCountdownWireState(const CountdownWireState& state) { state.chooserBoardId != state.hostBoardId && state.chooserBoardId != state.guestBoardId) || state.phase < CountdownWirePhase::Setup || - state.phase > CountdownWirePhase::Exited || - state.reserved != 0) { + state.phase > CountdownWirePhase::Exited) { return false; } return true; @@ -178,8 +177,10 @@ inline bool advanceCountdownRound(CountdownWireState& state, inline bool selectCountdownRoundType(CountdownWireState& state, uint32_t actorBoardId, uint8_t roundType) { - if (state.phase != CountdownWirePhase::BetweenRounds || - actorBoardId != state.chooserBoardId || // chooser picks, not host + const bool pickablePhase = state.phase == CountdownWirePhase::BetweenRounds || + state.phase == CountdownWirePhase::Setup; + if (!pickablePhase || + actorBoardId != state.chooserBoardId || roundType < 1 || roundType > 3) { return false; } diff --git a/platformio.ini b/platformio.ini index a5adcf4..9664418 100644 --- a/platformio.ini +++ b/platformio.ini @@ -12,6 +12,7 @@ lib_deps = https://github.com/PaulStoffregen/XPT2046_Touchscreen.git#v1.4 build_unflags = -std=gnu++11 -std=gnu++14 build_flags = + -std=gnu++17 -Iinclude -Icore/include -D USER_SETUP_LOADED=1 diff --git a/scripts/upload.py b/scripts/upload.py new file mode 100644 index 0000000..751a962 --- /dev/null +++ b/scripts/upload.py @@ -0,0 +1,201 @@ +#!/usr/bin/env python3 +""" +Build, upload, and monitor Flip7 CYD firmware. + +Loads ESP-NOW keys from ~/.config/flip7-cyd-scorekeeper/espnow.env, +sets FLIP7_ESPNOW_PMK and FLIP7_ESPNOW_LMK in the environment, then +delegates to PlatformIO. + +Usage: + python3 scripts/upload.py # build + upload to all boards + python3 scripts/upload.py --build # build only (no upload) + python3 scripts/upload.py --monitor # build + upload + monitor last port + python3 scripts/upload.py --monitor-only # open monitor on all detected ports + python3 scripts/upload.py --monitor-only --port /dev/ttyUSB0 # monitor one board + python3 scripts/upload.py --port /dev/ttyUSB0 # upload to a specific port only +""" + +import argparse +import glob +import os +import signal +import subprocess +import sys +import time +from pathlib import Path + + +ENV_FILE = Path.home() / ".config" / "flip7-cyd-scorekeeper" / "espnow.env" +REQUIRED_KEYS = ("FLIP7_ESPNOW_PMK", "FLIP7_ESPNOW_LMK") + +# Glob patterns for USB serial adapters in priority order. +PORT_PATTERNS = ["/dev/ttyUSB*", "/dev/ttyACM*"] + + +def load_env_file(path: Path) -> dict[str, str]: + """Parse `export KEY=VALUE` lines from a shell env file.""" + if not path.exists(): + print(f"ERROR: ESP-NOW key file not found: {path}", file=sys.stderr) + print( + "Run the one-time provisioning step from the README to create it.", + file=sys.stderr, + ) + sys.exit(1) + + env: dict[str, str] = {} + for line in path.read_text().splitlines(): + line = line.strip() + if not line or line.startswith("#"): + continue + if line.startswith("export "): + line = line[len("export "):] + if "=" not in line: + continue + key, _, value = line.partition("=") + env[key.strip()] = value.strip() + + for key in REQUIRED_KEYS: + if key not in env: + print(f"ERROR: {key} not found in {path}", file=sys.stderr) + sys.exit(1) + + return env + + +def detect_ports() -> list[str]: + """Return sorted list of all connected USB serial ports.""" + ports: list[str] = [] + for pattern in PORT_PATTERNS: + ports.extend(sorted(glob.glob(pattern))) + return ports + + +def run(cmd: list[str], env: dict[str, str]) -> int: + """Run a subprocess, streaming output, return exit code.""" + merged = {**os.environ, **env} + print(f"\n>>> {' '.join(cmd)}\n", flush=True) + result = subprocess.run(cmd, env=merged) + return result.returncode + + +def build(esp_env: dict[str, str]) -> None: + rc = run(["pio", "run"], esp_env) + if rc != 0: + print(f"\nBuild failed (exit {rc})", file=sys.stderr) + sys.exit(rc) + + +def release_port(port: str) -> None: + """Kill any process holding an exclusive lock on the port, then wait for it to release.""" + try: + result = subprocess.run( + ["fuser", port], capture_output=True, text=True + ) + pids = result.stdout.split() + if not pids: + return + print(f" Releasing {port} from PID(s): {', '.join(pids)}") + for pid in pids: + try: + os.kill(int(pid), signal.SIGTERM) + except (ProcessLookupError, ValueError): + pass + # Wait up to 2 seconds for the lock to drop + for _ in range(20): + time.sleep(0.1) + check = subprocess.run(["fuser", port], capture_output=True, text=True) + if not check.stdout.strip(): + break + except FileNotFoundError: + pass # fuser not available; proceed anyway + + +def upload_to(port: str, esp_env: dict[str, str]) -> bool: + """Upload to a single port. Returns True on success.""" + print(f"\n{'='*60}") + print(f" Uploading to {port}") + print(f"{'='*60}") + release_port(port) + rc = run(["pio", "run", "--target", "upload", "--upload-port", port], esp_env) + if rc != 0: + print(f"\nUpload to {port} failed (exit {rc})", file=sys.stderr) + return False + print(f"\nUpload to {port} complete.") + return True + + +def main() -> None: + parser = argparse.ArgumentParser(description="Build, upload, and monitor CYD firmware") + group = parser.add_mutually_exclusive_group() + group.add_argument("--build", action="store_true", help="Compile only; no upload") + group.add_argument( + "--monitor", action="store_true", + help="Build, upload to all boards, then open serial monitor on the last port", + ) + group.add_argument( + "--monitor-only", action="store_true", + help="Skip build/upload; open serial monitor on the target port(s)", + ) + parser.add_argument( + "--port", metavar="PORT", + help="Target a specific port (e.g. /dev/ttyUSB0)", + ) + args = parser.parse_args() + + project_root = Path(__file__).parent.parent + os.chdir(project_root) + + esp_env = load_env_file(ENV_FILE) + + # --monitor-only: skip build and upload entirely + if args.monitor_only: + port = args.port or _single_detected_port() + print(f"Opening monitor on {port}") + run(["pio", "device", "monitor", "--port", port], esp_env) + return + + build(esp_env) + + if args.build: + print("\nBuild complete.") + return + + # Determine target ports + if args.port: + ports = [args.port] + else: + ports = detect_ports() + if not ports: + print("ERROR: No serial ports found. Is a CYD board plugged in?", + file=sys.stderr) + sys.exit(1) + + print(f"\nFound {len(ports)} board(s): {', '.join(ports)}") + + failed: list[str] = [] + for port in ports: + if not upload_to(port, esp_env): + failed.append(port) + + if failed: + print(f"\nERROR: Upload failed for: {', '.join(failed)}", file=sys.stderr) + sys.exit(1) + + print(f"\nAll {len(ports)} board(s) flashed successfully.") + + if args.monitor: + # Monitor the last port + run(["pio", "device", "monitor", "--port", ports[-1]], esp_env) + + +def _single_detected_port() -> str: + """Return the first detected port, or exit with an error.""" + ports = detect_ports() + if not ports: + print("ERROR: No serial ports found.", file=sys.stderr) + sys.exit(1) + return ports[0] + + +if __name__ == "__main__": + main() diff --git a/src/main.cpp b/src/main.cpp index 61d3a1e..dad4b52 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -255,7 +255,9 @@ struct ConUiState { }; ConUiState conUi{}; -uint32_t roundPhaseStartMs = 0; // millis() when current sub-phase began +uint32_t roundPhaseStartMs = 0; // millis() when current sub-phase began +uint32_t lastAnimFrameMs = 0; // last millis() a timed animation redrawed +constexpr uint32_t kAnimFrameIntervalMs = 100; // ~10 fps for the arc timer bool touchWasDown = false; bool lastOnline = false; uint8_t selectedPeg = 0; @@ -955,28 +957,30 @@ static void renderCountdownNumpad(int32_t draftValue, bool submitted) { // Round intro screen (Intro sub-phase) \u2014 all round types // --------------------------------------------------------------------------- static void renderCountdownIntro(const CountdownWireState& state, - uint32_t elapsedMs) { - display.fillScreen(TFT_BLACK); - display.setTextDatum(MC_DATUM); - - const char* title = "COUNTDOWN"; - uint16_t titleColour = TFT_WHITE; - if (state.roundType == static_cast(flip7::countdown::RoundType::Numbers)) { - title = "NUMBERS ROUND"; - titleColour = TFT_CYAN; - } else if (state.roundType == static_cast(flip7::countdown::RoundType::Letters)) { - title = "LETTERS ROUND"; - titleColour = TFT_YELLOW; - } else if (state.roundType == static_cast(flip7::countdown::RoundType::Conundrum)) { - title = "CONUNDRUM ROUND"; - titleColour = TFT_MAGENTA; - } - - display.setTextColor(titleColour, TFT_BLACK); - display.drawString(title, 160, 75, 4); - display.setTextColor(TFT_LIGHTGREY, TFT_BLACK); - display.drawString("Get ready...", 160, 130, 2); + uint32_t elapsedMs, bool firstFrame) { + if (firstFrame) { + display.fillScreen(TFT_BLACK); + display.setTextDatum(MC_DATUM); + const char* title = "COUNTDOWN"; + uint16_t titleColour = TFT_WHITE; + if (state.roundType == static_cast(flip7::countdown::RoundType::Numbers)) { + title = "NUMBERS ROUND"; + titleColour = TFT_CYAN; + } else if (state.roundType == static_cast(flip7::countdown::RoundType::Letters)) { + title = "LETTERS ROUND"; + titleColour = TFT_YELLOW; + } else if (state.roundType == static_cast(flip7::countdown::RoundType::Conundrum)) { + title = "CONUNDRUM ROUND"; + titleColour = TFT_MAGENTA; + } + display.setTextColor(titleColour, TFT_BLACK); + display.drawString(title, 160, 75, 4); + display.setTextColor(TFT_LIGHTGREY, TFT_BLACK); + display.drawString("Get ready...", 160, 130, 2); + } + // Always update the timer circle region only + display.fillCircle(160, 175, 33, TFT_BLACK); renderCountdownAnimation(160, 175, 30, elapsedMs, 5000); } @@ -1019,20 +1023,23 @@ static void renderCountdownNumPicking(const CountdownWireState& state, static void renderCountdownNumThinking(const CountdownWireState& state, const flip7::countdown::NumbersRoundProjection& proj, - uint32_t elapsedMs) { - display.fillScreen(TFT_BLACK); - renderCountdownRoundHeader(state, false); - renderCountdownTileStrip(proj); - - display.setTextDatum(MC_DATUM); - display.setTextColor(TFT_CYAN, TFT_BLACK); - display.drawString("TARGET", 130, 100, 2); - display.setTextColor(TFT_WHITE, TFT_BLACK); - display.drawNumber(static_cast(proj.target), 130, 135, 6); + uint32_t elapsedMs, bool firstFrame) { + if (firstFrame) { + display.fillScreen(TFT_BLACK); + renderCountdownRoundHeader(state, false); + renderCountdownTileStrip(proj); - display.setTextColor(TFT_LIGHTGREY, TFT_BLACK); - display.drawString("Calculate on paper", 130, 190, 2); + display.setTextDatum(MC_DATUM); + display.setTextColor(TFT_CYAN, TFT_BLACK); + display.drawString("TARGET", 130, 100, 2); + display.setTextColor(TFT_WHITE, TFT_BLACK); + display.drawNumber(static_cast(proj.target), 130, 135, 6); + display.setTextColor(TFT_LIGHTGREY, TFT_BLACK); + display.drawString("Calculate on paper", 130, 190, 2); + } + // Only update the timer circle region to avoid full-screen flicker + display.fillCircle(kCdTimerCX, kCdTimerCY, kCdTimerR + 3, TFT_BLACK); renderCountdownAnimation(kCdTimerCX, kCdTimerCY, kCdTimerR, elapsedMs, 30000); } @@ -1225,7 +1232,8 @@ void renderCountdown(bool online, const CountdownWireState& state) { // Intro sub-phase \u2014 common to all round types if (subPhase == SubPhase::Intro) { - renderCountdownIntro(state, elapsed); + renderCountdownIntro(state, elapsed, + lastAnimFrameMs < roundPhaseStartMs); return; } @@ -1239,7 +1247,8 @@ void renderCountdown(bool online, const CountdownWireState& state) { portENTER_CRITICAL(&gameMux); proj = countdownEngine.numbersProjection(); portEXIT_CRITICAL(&gameMux); - renderCountdownNumThinking(state, proj, elapsed); + renderCountdownNumThinking(state, proj, elapsed, + lastAnimFrameMs < roundPhaseStartMs); return; } if (subPhase == SubPhase::NumClaimEntry) { @@ -1433,17 +1442,19 @@ static void renderCountdownLetPicking(const CountdownWireState& state, static void renderCountdownLetThinking(const CountdownWireState& state, const flip7::countdown::LettersRoundProjection& proj, - uint32_t elapsedMs) { - display.fillScreen(TFT_BLACK); - renderCountdownRoundHeader(state, false); - renderLetterStrip(proj.letters, proj.letterCount); - - display.setTextDatum(MC_DATUM); - display.setTextColor(TFT_YELLOW, TFT_BLACK); - display.drawString("Make the longest word you can", 160, 100, 2); - display.setTextColor(TFT_LIGHTGREY, TFT_BLACK); - display.drawString("Your time starts now", 160, 120, 2); + uint32_t elapsedMs, bool firstFrame) { + if (firstFrame) { + display.fillScreen(TFT_BLACK); + renderCountdownRoundHeader(state, false); + renderLetterStrip(proj.letters, proj.letterCount); + display.setTextDatum(MC_DATUM); + display.setTextColor(TFT_YELLOW, TFT_BLACK); + display.drawString("Make the longest word you can", 160, 100, 2); + display.setTextColor(TFT_LIGHTGREY, TFT_BLACK); + display.drawString("Your time starts now", 160, 120, 2); + } + display.fillCircle(kCdTimerCX, kCdTimerCY, kCdTimerR + 3, TFT_BLACK); renderCountdownAnimation(kCdTimerCX, kCdTimerCY, kCdTimerR, elapsedMs, 30000); } @@ -1804,7 +1815,8 @@ static void renderCountdownLetConPhases( // NOLINT portENTER_CRITICAL(&gameMux); proj = countdownEngine.lettersProjection(); portEXIT_CRITICAL(&gameMux); - renderCountdownLetThinking(state, proj, elapsed); + renderCountdownLetThinking(state, proj, elapsed, + lastAnimFrameMs < roundPhaseStartMs); return; } if (subPhase == SubPhase::LetClaimEntry) { @@ -1883,7 +1895,8 @@ static void renderCountdownLetConPhases( // NOLINT // Conundrum sub-phases if (subPhase == SubPhase::ConReady) { - renderCountdownIntro(state, elapsed); // reuse intro screen + renderCountdownIntro(state, elapsed, + lastAnimFrameMs < roundPhaseStartMs); // reuse intro screen return; } if (subPhase == SubPhase::ConActive) { @@ -2523,7 +2536,27 @@ void processCountdownStatePacket(const uint8_t* address, ? ScreenMode::Home : ScreenMode::Countdown; countdownReconciliationPending = false; + + // Sync the local engine round so projections (target, tiles, letters, + // conundrum) are available for rendering — even on the guest board. + // The host already called startNextRound directly; skip if already done. + if (countdownState.phase == CountdownWirePhase::InRound && + boardId != countdownState.hostBoardId) { + bool needsRoundStart = + countdownEngine.activeRound() == nullptr || + countdownEngine.roundNumber() != countdownState.roundNumber; + if (needsRoundStart) { + auto rt = static_cast( + countdownState.roundType); + auto rng = flip7::countdown::makeRoundRandom( + countdownState.gameId, countdownState.roundNumber); + uint8_t largeCnt = countdownState.roundConfig; + countdownEngine.startNextRound(rt, rng, largeCnt); + } + } + displayDirty = true; + lastAnimFrameMs = 0; // force first-frame full redraw on new state } if (accepted || duplicate) { queueCountdownAck(packet.header.senderId, packet.state, type); @@ -3202,6 +3235,9 @@ void handleTouch() { if (advanceCountdownSubPhase( advanced, boardId, CountdownRoundSubPhase::NumThinking)) { + // Store largeCount in roundConfig so the guest can + // reconstruct the same puzzle via makeRoundRandom. + advanced.roundConfig = numUi.largeCount; roundPhaseStartMs = millis(); portENTER_CRITICAL(&gameMux); if (sameCountdownWireState(countdownState, snap)) { @@ -4033,12 +4069,19 @@ void loop() { using SP = CountdownRoundSubPhase; auto sp = static_cast(snap.roundSubPhase); - // Force re-render during timed animation sub-phases so the arc updates. - bool animating = (sp == SP::Intro || - sp == SP::NumThinking || - sp == SP::LetThinking || - sp == SP::ConActive); - if (animating) { + // Rate-limited re-render during timed animation sub-phases (~10 fps). + // Without the limit every 10ms loop tick causes a full fillScreen + // redraw which flickers visibly on the TFT. + // ConActive uses a slower rate (500ms) since tiles still need full redraws. + bool animating = (sp == SP::Intro || + sp == SP::NumThinking || + sp == SP::LetThinking); + bool conAnimating = (sp == SP::ConActive); + if ((animating && (now - lastAnimFrameMs >= kAnimFrameIntervalMs || + lastAnimFrameMs < roundPhaseStartMs)) || + (conAnimating && (now - lastAnimFrameMs >= 500 || + lastAnimFrameMs < roundPhaseStartMs))) { + lastAnimFrameMs = now; portENTER_CRITICAL(&gameMux); displayDirty = true; portEXIT_CRITICAL(&gameMux); From 2ba51ffae4ec8f8ef7f4d170e87e696ca9661e09 Mon Sep 17 00:00:00 2001 From: Christopher Mayfield Date: Sat, 8 Aug 2026 14:01:33 -0500 Subject: [PATCH 09/12] fix: address Copilot PR #53 review comments - Header: remove misleading reducer/plugin wording - NumbersRound::submitClaim: validate value reachable from tiles - ConundrumRound: add buzz-window deadline with markExpired() - CMakeLists.txt: fix target link ordering - protocol.hpp: add Countdown packet definitions --- core/include/flip7/countdown_engine.hpp | 185 ++++++++++------ core/include/flip7/protocol.hpp | 81 +++++++ core/src/countdown_engine.cpp | 283 ++++++++++++++++++------ core/tests/CMakeLists.txt | 16 +- core/tests/countdown_test.cpp | 97 ++++++-- 5 files changed, 497 insertions(+), 165 deletions(-) diff --git a/core/include/flip7/countdown_engine.hpp b/core/include/flip7/countdown_engine.hpp index 922d2a2..484fb79 100644 --- a/core/include/flip7/countdown_engine.hpp +++ b/core/include/flip7/countdown_engine.hpp @@ -1,7 +1,7 @@ /** * Open-Source Countdown Engine - Core Header - * Platform-neutral C++ implementation of Countdown game rules, round plugins, - * command-event reducer pattern, and score management. + * Platform-neutral C++ implementation of Countdown game rules (Numbers, Letters, Conundrum) + * and match-level score management. */ #pragma once @@ -13,10 +13,8 @@ #include #include #include -#include -namespace flip7 { -namespace countdown { +namespace flip7::countdown { enum class RoundType : uint8_t { Numbers = 1, @@ -83,7 +81,7 @@ enum class MatchPhase : uint8_t { template class FixedVector { public: - FixedVector() : size_(0) {} + FixedVector() : data_{}, size_(0) {} bool push_back(const T& v) { if (size_ >= N) return false; data_[size_++] = v; @@ -95,17 +93,17 @@ class FixedVector { return true; } T& back() { return data_[size_ - 1]; } - const T& back() const { return data_[size_ - 1]; } + [[nodiscard]] const T& back() const { return data_[size_ - 1]; } T& operator[](size_t i) { return data_[i]; } const T& operator[](size_t i) const { return data_[i]; } - size_t size() const { return size_; } - bool empty() const { return size_ == 0; } - bool full() const { return size_ >= N; } + [[nodiscard]] size_t size() const { return size_; } + [[nodiscard]] bool empty() const { return size_ == 0; } + [[nodiscard]] bool full() const { return size_ >= N; } void clear() { size_ = 0; } T* begin() { return data_; } T* end() { return data_ + size_; } - const T* begin() const { return data_; } - const T* end() const { return data_ + size_; } + [[nodiscard]] const T* begin() const { return data_; } + [[nodiscard]] const T* end() const { return data_ + size_; } private: T data_[N]; size_t size_; @@ -200,9 +198,11 @@ struct LettersRoundProjection { }; struct ConundrumRoundProjection { + char solution[10]{0}; char scramble[10]{0}; char hint[48]{0}; bool solved{false}; + bool expired{false}; uint32_t winnerBoardId{0}; }; @@ -241,7 +241,7 @@ inline SeededRandomSource makeRoundRandom(uint32_t gameId, uint32_t roundNumber) class IRound { public: virtual ~IRound() = default; - virtual RoundType roundType() const = 0; + [[nodiscard]] virtual RoundType roundType() const = 0; virtual CommandResult applyCommand(const CommandContext& ctx, CommandType type, const std::vector& payload) = 0; @@ -256,16 +256,23 @@ class NumbersRound : public IRound { static NumbersRound createRandom(IRandomSource& random, uint8_t largeCount); - uint32_t target() const { return target_; } - const std::vector& tiles() const { return tiles_; } + [[nodiscard]] uint32_t target() const { return target_; } + [[nodiscard]] const std::vector& tiles() const { return tiles_; } // Score table: exact=10, within 5=7, within 10=5, else 0. static int32_t calculateScore(uint32_t target, uint32_t achieved); + // Returns true if claimedValue can be reached from tiles_ using + // +, -, *, / operations (each tile used at most once). + [[nodiscard]] static bool isValueReachable(int32_t target, + const std::vector& tiles); + // Private claim — stored by boardId; not revealed until both submitted. + // Validates that claimedValue is reachable from tiles_; rejects unreachable + // values with CLAIM_UNREACHABLE. CommandResult submitClaim(uint32_t boardId, int32_t claimedValue); - bool bothClaimsSubmitted() const; - int32_t claimFor(uint32_t boardId) const; + [[nodiscard]] bool bothClaimsSubmitted() const; + [[nodiscard]] int32_t claimFor(uint32_t boardId) const; // Presentation step — host validates; ContinueWithResult leaves result as // currentValue; BankResult returns it to the available pool for branching. @@ -273,13 +280,13 @@ class NumbersRound : public IRound { int32_t operandA, int32_t operandB, bool bankResult = false); CommandResult undoStep(uint32_t boardId); - CommandResult completePresentaton(uint32_t boardId); + CommandResult completePresentation(uint32_t boardId); - const NumberPlayerState* playerState(uint32_t boardId) const; - NumbersRoundProjection projection() const; + [[nodiscard]] const NumberPlayerState* playerState(uint32_t boardId) const; + [[nodiscard]] NumbersRoundProjection projection() const; // IRound - RoundType roundType() const override { return RoundType::Numbers; } + [[nodiscard]] RoundType roundType() const override { return RoundType::Numbers; } CommandResult applyCommand(const CommandContext& ctx, CommandType type, const std::vector& payload) override; std::optional tryFinalize( @@ -299,22 +306,24 @@ class LettersRound : public IRound { // Picking phase — called by host; enforces 3-5 vowel / 4-6 consonant constraints. bool drawVowel(IRandomSource& random); bool drawConsonant(IRandomSource& random); - bool canDrawVowel() const; - bool canDrawConsonant() const; + [[nodiscard]] bool canDrawVowel() const; + [[nodiscard]] bool canDrawConsonant() const; + + [[nodiscard]] const std::string& drawnLetters() const { return drawnLetters_; } + [[nodiscard]] size_t letterCount() const { return drawnLetters_.size(); } + [[nodiscard]] size_t vowelCount() const { return vowelCount_; } + [[nodiscard]] size_t consonantCount() const { return consonantCount_; } + [[nodiscard]] bool isSelectionComplete() const { return drawnLetters_.size() >= 9; } - const std::string& drawnLetters() const { return drawnLetters_; } - size_t letterCount() const { return drawnLetters_.size(); } - size_t vowelCount() const { return vowelCount_; } - size_t consonantCount() const { return consonantCount_; } - bool isSelectionComplete() const { return drawnLetters_.size() >= 9; } + bool syncDrawnLetters(const std::string& letters); static bool isValidSubset(const std::string& word, const std::string& availableLetters); // Claim phase — private word-length claim per player. CommandResult submitClaim(uint32_t boardId, uint8_t claimedLength); - bool bothClaimsSubmitted() const; - uint8_t claimFor(uint32_t boardId) const; + [[nodiscard]] bool bothClaimsSubmitted() const; + [[nodiscard]] uint8_t claimFor(uint32_t boardId) const; // Presentation phase — presenter taps tiles by letter index (0-8). CommandResult presentTileTap(uint32_t boardId, uint8_t tileIndex); @@ -322,17 +331,18 @@ class LettersRound : public IRound { CommandResult presentComplete(uint32_t boardId); CommandResult verifyWord(uint32_t verifierBoardId, bool accepted); - const std::string& presentedWordFor(uint32_t boardId) const; - LettersRoundProjection projection() const; + [[nodiscard]] const std::string& presentedWordFor(uint32_t boardId) const; + [[nodiscard]] bool verificationFor(uint32_t boardId) const; + [[nodiscard]] LettersRoundProjection projection() const; // IRound - RoundType roundType() const override { return RoundType::Letters; } + [[nodiscard]] RoundType roundType() const override { return RoundType::Letters; } CommandResult applyCommand(const CommandContext& ctx, CommandType type, const std::vector& payload) override; std::optional tryFinalize( const std::vector& playerBoardIds) override; - RoundResult evaluate(const std::vector& playerBoardIds) const; + [[nodiscard]] RoundResult evaluate(const std::vector& playerBoardIds) const; private: std::string drawnLetters_; @@ -345,29 +355,40 @@ class LettersRound : public IRound { }; // Conundrum Round State & Logic -// Both players attempt simultaneously — no buzz mechanic. +// Simultaneous attempt with bounded buzz-window; both players may attempt +// until the deadline expires or the conundrum is solved. class ConundrumRound : public IRound { public: - ConundrumRound(const std::string& solution, const std::string& scramble, - const std::string& hint); + ConundrumRound(std::string solution, std::string scramble, + std::string hint); // Pick a random conundrum from the built-in content table. static ConundrumRound createRandom(IRandomSource& random); - const std::string& solution() const { return solution_; } - const std::string& scramble() const { return scramble_; } - const std::string& hint() const { return hint_; } + [[nodiscard]] const std::string& solution() const { return solution_; } + [[nodiscard]] const std::string& scramble() const { return scramble_; } + [[nodiscard]] const std::string& hint() const { return hint_; } + + // Set the submission deadline (absolute ms timestamp). + void setDeadlineMs(uint32_t deadlineMs) { deadlineMs_ = deadlineMs; } + [[nodiscard]] uint32_t deadlineMs() const { return deadlineMs_; } + [[nodiscard]] bool hasDeadlineElapsed(uint32_t nowMs) const { + return deadlineMs_ > 0 && nowMs >= deadlineMs_; + } // Simultaneous attempt — checked immediately; no turn-based locking. // Returns accepted=true only when attempt matches solution. + // Returns DEADLINE_ELAPSED if the buzz-window has expired. CommandResult submitAttempt(uint32_t boardId, const std::string& attempt); - bool isSolved() const { return solved_; } - uint32_t winnerBoardId() const { return winnerBoardId_; } - ConundrumRoundProjection projection() const; + [[nodiscard]] bool isSolved() const { return solved_; } + [[nodiscard]] bool isExpired() const { return expired_; } + void markExpired(); + [[nodiscard]] uint32_t winnerBoardId() const { return winnerBoardId_; } + [[nodiscard]] ConundrumRoundProjection projection() const; // IRound - RoundType roundType() const override { return RoundType::Conundrum; } + [[nodiscard]] RoundType roundType() const override { return RoundType::Conundrum; } CommandResult applyCommand(const CommandContext& ctx, CommandType type, const std::vector& payload) override; std::optional tryFinalize( @@ -378,6 +399,8 @@ class ConundrumRound : public IRound { std::string scramble_; std::string hint_; bool solved_{false}; + bool expired_{false}; + uint32_t deadlineMs_{0}; uint32_t winnerBoardId_{0}; }; @@ -387,33 +410,33 @@ class CountdownMatchEngine { CountdownMatchEngine(uint32_t hostBoardId, uint32_t guestBoardId, uint32_t gameId); - uint32_t gameId() const { return gameId_; } - uint32_t hostBoardId() const { return hostBoardId_; } - uint32_t guestBoardId() const { return guestBoardId_; } + [[nodiscard]] uint32_t gameId() const { return gameId_; } + [[nodiscard]] uint32_t hostBoardId() const { return hostBoardId_; } + [[nodiscard]] uint32_t guestBoardId() const { return guestBoardId_; } // Score leader — owns authoritative game state. - uint32_t hostPlayerId() const { return hostBoardId_; } + [[nodiscard]] uint32_t hostPlayerId() const { return hostBoardId_; } // Previous round winner — picks the next round type. - uint32_t chooserBoardId() const { return chooserBoardId_; } - uint32_t hostTerm() const { return hostTerm_; } - MatchPhase phase() const { return phase_; } - uint32_t roundNumber() const { return roundNumber_; } + [[nodiscard]] uint32_t chooserBoardId() const { return chooserBoardId_; } + [[nodiscard]] uint32_t hostTerm() const { return hostTerm_; } + [[nodiscard]] MatchPhase phase() const { return phase_; } + [[nodiscard]] uint32_t roundNumber() const { return roundNumber_; } - const PlayerState& hostPlayer() const { return hostPlayer_; } - const PlayerState& guestPlayer() const { return guestPlayer_; } + [[nodiscard]] const PlayerState& hostPlayer() const { return hostPlayer_; } + [[nodiscard]] const PlayerState& guestPlayer() const { return guestPlayer_; } - bool isHost(uint32_t boardId) const { return boardId == hostBoardId_; } - bool isChooser(uint32_t boardId) const { return boardId == chooserBoardId_; } + [[nodiscard]] bool isHost(uint32_t boardId) const { return boardId == hostBoardId_; } + [[nodiscard]] bool isChooser(uint32_t boardId) const { return boardId == chooserBoardId_; } // Convenience snapshot for UI rendering. struct MatchState { PlayerState hostPlayer; PlayerState guestPlayer; - MatchPhase phase; - uint32_t roundNumber; - uint32_t chooserBoardId; - uint32_t hostTerm; + MatchPhase phase{MatchPhase::Setup}; + uint32_t roundNumber{0}; + uint32_t chooserBoardId{0}; + uint32_t hostTerm{1}; }; - MatchState matchState() const { + [[nodiscard]] MatchState matchState() const { return MatchState{hostPlayer_, guestPlayer_, phase_, roundNumber_, chooserBoardId_, hostTerm_}; } @@ -434,35 +457,54 @@ class CountdownMatchEngine { activeRound_.reset(); } + // Reconcile authority and match metadata received from the wire without + // discarding an already-synchronized active round. + void syncWireState(uint32_t hostBoardId, uint32_t guestBoardId, + uint32_t chooserBoardId, uint32_t hostTerm, + uint32_t roundNumber, int32_t hostScore, + int32_t guestScore) { + hostBoardId_ = hostBoardId; + guestBoardId_ = guestBoardId; + chooserBoardId_ = chooserBoardId; + hostTerm_ = hostTerm; + roundNumber_ = roundNumber; + hostPlayer_.boardId = hostBoardId; + hostPlayer_.isHost = true; + hostPlayer_.score = hostScore; + guestPlayer_.boardId = guestBoardId; + guestPlayer_.isHost = false; + guestPlayer_.score = guestScore; + } + // Create and store the active round object; advance to InRound. void startNextRound(RoundType type, IRandomSource& random, uint8_t largeCount = 2); // Active round access — nullptr when no round is in progress. IRound* activeRound() { return activeRound_.get(); } - const IRound* activeRound() const { return activeRound_.get(); } - RoundType activeRoundType() const { return activeRoundType_; } + [[nodiscard]] const IRound* activeRound() const { return activeRound_.get(); } + [[nodiscard]] RoundType activeRoundType() const { return activeRoundType_; } // Round-type-specific accessors — nullptr when a different round type is active. // Uses static_cast guarded by activeRoundType_ (no RTTI / dynamic_cast needed). NumbersRound* numbersRound(); - const NumbersRound* numbersRound() const; + [[nodiscard]] const NumbersRound* numbersRound() const; LettersRound* lettersRound(); - const LettersRound* lettersRound() const; + [[nodiscard]] const LettersRound* lettersRound() const; ConundrumRound* conundrumRound(); - const ConundrumRound* conundrumRound() const; + [[nodiscard]] const ConundrumRound* conundrumRound() const; // Display projections — safe to copy across tasks; return empty structs when // no round of that type is active. - NumbersRoundProjection numbersProjection() const; - LettersRoundProjection lettersProjection() const; - ConundrumRoundProjection conundrumProjection() const; + [[nodiscard]] NumbersRoundProjection numbersProjection() const; + [[nodiscard]] LettersRoundProjection lettersProjection() const; + [[nodiscard]] ConundrumRoundProjection conundrumProjection() const; // Apply score updates; update hostBoardId (score leader) and chooserBoardId // (round winner) independently per ADR-005. void recordRoundResult(const RoundResult& result); - bool isMatchComplete() const { return phase_ == MatchPhase::MatchComplete; } + [[nodiscard]] bool isMatchComplete() const { return phase_ == MatchPhase::MatchComplete; } private: uint32_t gameId_{0}; @@ -478,5 +520,4 @@ class CountdownMatchEngine { std::unique_ptr activeRound_; }; -} // namespace countdown -} // namespace flip7 +} // namespace flip7::countdown diff --git a/core/include/flip7/protocol.hpp b/core/include/flip7/protocol.hpp index 8ac1c9b..3e9cd32 100644 --- a/core/include/flip7/protocol.hpp +++ b/core/include/flip7/protocol.hpp @@ -24,6 +24,9 @@ constexpr size_t kStateRequestPacketSize = 32; constexpr size_t kGameStatePacketSize = 236; constexpr size_t kGameAckPacketSize = 40; constexpr size_t kMastermindStateRequestPacketSize = 32; +constexpr size_t kCountdownStatePacketSize = 128; +constexpr size_t kCountdownAckPacketSize = 40; +constexpr size_t kCountdownStateRequestPacketSize = 32; // Game limits constexpr uint8_t kPuzzleMaxTiles = 30; @@ -80,6 +83,11 @@ enum class MessageType : uint8_t { MastermindFullState = 8, MastermindAck = 9, MastermindRequestState = 10, + // Countdown + CountdownState = 11, + CountdownFullState = 12, + CountdownAck = 13, + CountdownRequestState = 14, // BLE-specific extensions (100+) BleCommand = 100, BleEvent = 101, @@ -232,6 +240,79 @@ struct MastermindStateRequestPacket { uint32_t stateDigest; }; +// Countdown types +constexpr uint8_t kCountdownMaxTiles = 6; +constexpr uint8_t kCountdownMaxSteps = 16; +constexpr uint8_t kCountdownMaxLetters = 9; + +enum class CountdownRoundType : uint8_t { + None = 0, + Numbers = 1, + Letters = 2, + Conundrum = 3, +}; + +enum class CountdownPhase : uint8_t { + Setup = 0, + InRound = 1, + BetweenRounds = 2, + MatchComplete = 3, +}; + +struct CountdownPlayerState { + int32_t score{0}; +}; + +struct CountdownMatchState { + uint32_t gameId{0}; + uint32_t hostBoardId{0}; + uint32_t guestBoardId{0}; + uint32_t chooserBoardId{0}; + uint32_t hostTerm{1}; + uint32_t roundNumber{0}; + CountdownPhase phase{CountdownPhase::Setup}; + CountdownRoundType activeRoundType{CountdownRoundType::None}; + CountdownPlayerState hostPlayer; + CountdownPlayerState guestPlayer; + uint8_t reserved[6]{0}; +}; + +struct CountdownRoundState { + CountdownRoundType type{CountdownRoundType::None}; + int32_t numbersTarget{0}; + int32_t numbersTiles[kCountdownMaxTiles]{0}; + uint8_t numbersTileCount{0}; + char letters[kCountdownMaxLetters]{0}; + uint8_t letterCount{0}; + char conundrumScramble[kCountdownMaxLetters + 1]{0}; + char conundrumHint[48]{0}; + bool conundrumSolved{false}; + uint8_t reserved[5]{0}; +}; + +struct CountdownStatePacket { + PacketHeader header; + CountdownMatchState matchState; + CountdownRoundState roundState; +}; + +struct CountdownAckPacket { + PacketHeader header; + uint32_t targetBoardId; + uint32_t gameId; + uint32_t revision; + uint32_t stateDigest; + MessageType acknowledgedType; + uint8_t reserved[3]; +}; + +struct CountdownStateRequestPacket { + PacketHeader header; + uint32_t gameId; + uint32_t revision; + uint32_t stateDigest; +}; + // Active game clock struct ActiveGameClock { uint32_t epoch; diff --git a/core/src/countdown_engine.cpp b/core/src/countdown_engine.cpp index 5fbb094..7946f64 100644 --- a/core/src/countdown_engine.cpp +++ b/core/src/countdown_engine.cpp @@ -3,12 +3,17 @@ */ #include "flip7/countdown_engine.hpp" +#include "flip7/conundrum_table.hpp" +#include #include #include +#include +#include #include +#include -namespace flip7 { -namespace countdown { + +namespace flip7::countdown { // -------------------------------------------------- // NumbersRound @@ -18,30 +23,32 @@ NumbersRound::NumbersRound(uint32_t target, const std::vector& tiles) : target_(target), tiles_(tiles) {} NumbersRound NumbersRound::createRandom(IRandomSource& random, uint8_t largeCount) { - uint32_t target = random.nextRange(101, 999); + const uint32_t target = random.nextRange(101, 999); std::vector largePool = {25, 50, 75, 100}; std::vector smallPool = {1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10}; - uint8_t largeToPick = std::min(largeCount, 4); - uint8_t smallToPick = static_cast(6 - largeToPick); + const uint8_t largeToPick = std::min(largeCount, 4); + const auto smallToPick = static_cast(6 - largeToPick); std::vector selectedTiles; for (uint8_t i = 0; i < largeToPick && !largePool.empty(); ++i) { - size_t idx = random.nextRange(0, static_cast(largePool.size() - 1)); + const size_t idx = + random.nextRange(0, static_cast(largePool.size() - 1)); selectedTiles.push_back(largePool[idx]); largePool.erase(largePool.begin() + static_cast(idx)); } for (uint8_t i = 0; i < smallToPick && !smallPool.empty(); ++i) { - size_t idx = random.nextRange(0, static_cast(smallPool.size() - 1)); + const size_t idx = + random.nextRange(0, static_cast(smallPool.size() - 1)); selectedTiles.push_back(smallPool[idx]); smallPool.erase(smallPool.begin() + static_cast(idx)); } - return NumbersRound(target, selectedTiles); + return {target, selectedTiles}; } int32_t NumbersRound::calculateScore(uint32_t target, uint32_t achieved) { @@ -52,11 +59,69 @@ int32_t NumbersRound::calculateScore(uint32_t target, uint32_t achieved) { return 0; } +bool NumbersRound::isValueReachable(int32_t target, + const std::vector& tiles) { + if (tiles.empty()) return false; + if (tiles.size() == 1) return tiles[0] == target; + + for (size_t i = 0; i < tiles.size(); ++i) { + for (size_t j = i + 1; j < tiles.size(); ++j) { + int32_t a = tiles[i]; + int32_t b = tiles[j]; + std::vector remaining; + for (size_t k = 0; k < tiles.size(); ++k) { + if (k != i && k != j) remaining.push_back(tiles[k]); + } + + // a + b + remaining.push_back(a + b); + if (isValueReachable(target, remaining)) return true; + remaining.pop_back(); + + // a - b (positive only) + if (a > b) { + remaining.push_back(a - b); + if (isValueReachable(target, remaining)) return true; + remaining.pop_back(); + } + // b - a (positive only) + if (b > a) { + remaining.push_back(b - a); + if (isValueReachable(target, remaining)) return true; + remaining.pop_back(); + } + + // a * b + remaining.push_back(a * b); + if (isValueReachable(target, remaining)) return true; + remaining.pop_back(); + + // a / b (exact division only) + if (b != 0 && a % b == 0) { + remaining.push_back(a / b); + if (isValueReachable(target, remaining)) return true; + remaining.pop_back(); + } + // b / a (exact division only) + if (a != 0 && b % a == 0) { + remaining.push_back(b / a); + if (isValueReachable(target, remaining)) return true; + remaining.pop_back(); + } + } + } + return false; +} + CommandResult NumbersRound::submitClaim(uint32_t boardId, int32_t claimedValue) { auto& ps = playerStates_[boardId]; if (ps.claimSubmitted) { return CommandResult{false, "ALREADY_CLAIMED", "Claim already submitted"}; } + if (!isValueReachable(claimedValue, tiles_)) { + return CommandResult{false, "CLAIM_UNREACHABLE", + "Claimed value is not achievable from the drawn tiles"}; + } ps.claimedValue = claimedValue; ps.claimSubmitted = true; ps.workspace.reset(tiles_); @@ -64,11 +129,11 @@ CommandResult NumbersRound::submitClaim(uint32_t boardId, int32_t claimedValue) } bool NumbersRound::bothClaimsSubmitted() const { - if (playerStates_.size() < 2) return false; - for (const auto& kv : playerStates_) { - if (!kv.second.claimSubmitted) return false; - } - return true; + return playerStates_.size() >= 2 && + std::all_of(playerStates_.begin(), playerStates_.end(), + [](const auto& entry) { + return entry.second.claimSubmitted; + }); } int32_t NumbersRound::claimFor(uint32_t boardId) const { @@ -90,14 +155,14 @@ CommandResult NumbersRound::applyStep(uint32_t boardId, ArithmeticOp op, if (ws.currentValue.has_value() && ws.currentValue.value() == operandA) { aFound = true; } else { - for (size_t i = 0; i < ws.availableValues.size(); ++i) { - if (ws.availableValues[i] == operandA) { aFound = true; break; } + for (const int32_t availableValue : ws.availableValues) { + if (availableValue == operandA) { aFound = true; break; } } } // Validate operand B is available (distinct from A) bool bFound = false; - for (size_t i = 0; i < ws.availableValues.size(); ++i) { - if (ws.availableValues[i] == operandB) { bFound = true; break; } + for (const int32_t availableValue : ws.availableValues) { + if (availableValue == operandB) { bFound = true; break; } } if (!aFound || !bFound) { return CommandResult{false, "OPERAND_UNAVAILABLE", @@ -184,7 +249,7 @@ CommandResult NumbersRound::undoStep(uint32_t boardId) { return CommandResult{true, "", ""}; } -CommandResult NumbersRound::completePresentaton(uint32_t boardId) { +CommandResult NumbersRound::completePresentation(uint32_t boardId) { auto it = playerStates_.find(boardId); if (it == playerStates_.end()) { return CommandResult{false, "UNKNOWN_PLAYER", "Unknown player"}; @@ -228,29 +293,34 @@ CommandResult NumbersRound::applyCommand(const CommandContext& ctx, switch (type) { case CommandType::NumSubmitClaim: { if (payload.size() < 4) return {false, "BAD_PAYLOAD", ""}; - int32_t v = static_cast( - (uint32_t(payload[0])) | (uint32_t(payload[1]) << 8) | - (uint32_t(payload[2]) << 16) | (uint32_t(payload[3]) << 24)); + auto v = static_cast( + (static_cast(payload[0])) | + (static_cast(payload[1]) << 8) | + (static_cast(payload[2]) << 16) | + (static_cast(payload[3]) << 24)); return submitClaim(ctx.senderBoardId, v); } case CommandType::NumPresentStepRequest: { if (payload.size() < 9) return {false, "BAD_PAYLOAD", ""}; auto op = static_cast(payload[0]); - int32_t a = static_cast( - (uint32_t(payload[1])) | (uint32_t(payload[2]) << 8) | - (uint32_t(payload[3]) << 16) | (uint32_t(payload[4]) << 24)); - int32_t b = static_cast( - (uint32_t(payload[5])) | (uint32_t(payload[6]) << 8) | - (uint32_t(payload[7]) << 16) | (uint32_t(payload[8]) << 24)); + auto a = static_cast( + (static_cast(payload[1])) | + (static_cast(payload[2]) << 8) | + (static_cast(payload[3]) << 16) | + (static_cast(payload[4]) << 24)); + auto b = static_cast( + (static_cast(payload[5])) | + (static_cast(payload[6]) << 8) | + (static_cast(payload[7]) << 16) | + (static_cast(payload[8]) << 24)); bool bank = (payload.size() > 9 && payload[9] != 0); return applyStep(ctx.senderBoardId, op, a, b, bank); } - case CommandType::NumPresentBankResult: - return undoStep(ctx.senderBoardId); // BankResult reuses undo path case CommandType::NumPresentUndo: + case CommandType::NumPresentBankResult: return undoStep(ctx.senderBoardId); case CommandType::NumPresentComplete: - return completePresentaton(ctx.senderBoardId); + return completePresentation(ctx.senderBoardId); default: return {false, "WRONG_ROUND", "Command not valid for Numbers round"}; } @@ -310,6 +380,23 @@ bool LettersRound::canDrawVowel() const { return true; } +bool LettersRound::syncDrawnLetters(const std::string& letters) { + if (letters.size() > 9) return false; + size_t vowels = 0; + for (char letter : letters) { + const char upper = static_cast(std::toupper(static_cast(letter))); + if (upper < 'A' || upper > 'Z') return false; + if (upper == 'A' || upper == 'E' || upper == 'I' || + upper == 'O' || upper == 'U') { + ++vowels; + } + } + drawnLetters_ = letters; + vowelCount_ = vowels; + consonantCount_ = letters.size() - vowels; + return true; +} + bool LettersRound::canDrawConsonant() const { size_t remaining = 9 - drawnLetters_.size(); if (remaining == 0) return false; @@ -322,8 +409,8 @@ bool LettersRound::canDrawConsonant() const { bool LettersRound::drawVowel(IRandomSource& random) { if (!canDrawVowel()) return false; - const char vowels[] = {'A', 'E', 'I', 'O', 'U'}; - char v = vowels[random.nextRange(0, 4)]; + constexpr char vowels[] = {'A', 'E', 'I', 'O', 'U'}; + const char v = vowels[random.nextRange(0, 4)]; drawnLetters_ += v; ++vowelCount_; return true; @@ -331,11 +418,11 @@ bool LettersRound::drawVowel(IRandomSource& random) { bool LettersRound::drawConsonant(IRandomSource& random) { if (!canDrawConsonant()) return false; - const char consonants[] = { + constexpr char consonants[] = { 'B', 'C', 'D', 'F', 'G', 'H', 'J', 'K', 'L', 'M', 'N', 'P', 'Q', 'R', 'S', 'T', 'V', 'W', 'X', 'Y', 'Z' }; - char c = consonants[random.nextRange(0, 20)]; + const char c = consonants[random.nextRange(0, 20)]; drawnLetters_ += c; ++consonantCount_; return true; @@ -427,6 +514,11 @@ const std::string& LettersRound::presentedWordFor(uint32_t boardId) const { return (it != presentedWords_.end()) ? it->second : empty; } +bool LettersRound::verificationFor(uint32_t boardId) const { + auto it = verifications_.find(boardId); + return it != verifications_.end() && it->second; +} + LettersRoundProjection LettersRound::projection() const { LettersRoundProjection p{}; p.letterCount = static_cast( @@ -524,33 +616,76 @@ RoundResult LettersRound::evaluate(const std::vector& playerBoardIds) // TODO: replace with content/conundrum/default_words.csv -> generated header // -------------------------------------------------- -namespace { -struct ConundrumEntry { const char* solution; const char* scramble; const char* hint; }; -static const ConundrumEntry kConundrumTable[] = { - {"BLUEPRINT", "TNPULEBRI", "A detailed plan or design"}, - {"COUNTDOWN", "DTWNOCNUO", "A backward sequence before an event"}, - {"STARTLING", "GTLNRSAIT", "Surprising or astonishing"}, - {"CARPETING", "TICNGPEAR", "Floor covering material"}, - {"GRENADIER", "AIGNRDERE", "A type of soldier"}, - {"PETROLEUM", "MOUERTPLE", "Crude oil and its derivatives"}, - {"RECTANGLE", "CTRELAGNE", "A four-sided flat shape"}, - {"IMPORTANT", "TNAMIPORT", "Of great significance"}, - {"TRAMPOLIN", "MILNPAOTR", "Bouncing apparatus"}, - {"SPECTATOR", "POSTCATER", "Someone watching an event"}, +const ConundrumEntry kConundrumTable[] = { + {.solution = "ADVENTURE", .scramble = "VENTUREAD", .hint = "An exciting or unusual experience"}, + {.solution = "BEAUTIFUL", .scramble = "TIFULBEAU", .hint = "Pleasant to look at"}, + {.solution = "BEGINNING", .scramble = "NINGBEGIN", .hint = "The start of something"}, + {.solution = "BLUEBERRY", .scramble = "BERRYBLUE", .hint = "A small blue edible fruit"}, + {.solution = "BLUEPRINT", .scramble = "TNPULEBRI", .hint = "A detailed plan or design"}, + {.solution = "BREAKFAST", .scramble = "FASTBREAK", .hint = "The first meal of the day"}, + {.solution = "CARPETING", .scramble = "TICNGPEAR", .hint = "Floor covering material"}, + {.solution = "CELEBRATE", .scramble = "BRATECELE", .hint = "To honor a special occasion"}, + {.solution = "CHAMPIONS", .scramble = "PIONSCHAM", .hint = "Winners of a competition"}, + {.solution = "CHOCOLATE", .scramble = "LATECHOCO", .hint = "Sweet treat made from cacao"}, + {.solution = "COMPUTERS", .scramble = "PUTERCOMS", .hint = "Electronic information machines"}, + {.solution = "COUNTDOWN", .scramble = "DTWNOCNUO", .hint = "A backward sequence before an event"}, + {.solution = "CREATURES", .scramble = "TURESCREA", .hint = "Living beings or animals"}, + {.solution = "CROSSWORD", .scramble = "WORDCROSS", .hint = "A word puzzle with intersecting answers"}, + {.solution = "DANGEROUS", .scramble = "GEROUSDAN", .hint = "Likely to cause harm"}, + {.solution = "DISCOVERY", .scramble = "COVERYDIS", .hint = "The finding of something new"}, + {.solution = "EDUCATION", .scramble = "CATIONEDU", .hint = "The process of learning"}, + {.solution = "ELEPHANTS", .scramble = "PHANTSELE", .hint = "Large animals with trunks"}, + {.solution = "EVERGREEN", .scramble = "GREENEVER", .hint = "A plant that stays green year-round"}, + {.solution = "FESTIVALS", .scramble = "TIVALSFES", .hint = "Large celebrations or events"}, + {.solution = "FIREPLACE", .scramble = "PLACEFIRE", .hint = "A hearth for a fire"}, + {.solution = "GARDENING", .scramble = "DENINGGAR", .hint = "The activity of growing plants"}, + {.solution = "GRENADIER", .scramble = "AIGNRDERE", .hint = "A type of soldier"}, + {.solution = "HAPPINESS", .scramble = "PINESSHAP", .hint = "A state of joy"}, + {.solution = "HEADLIGHT", .scramble = "LIGHTHEAD", .hint = "A forward-facing vehicle lamp"}, + {.solution = "IMPORTANT", .scramble = "TNAMIPORT", .hint = "Of great significance"}, + {.solution = "JELLYFISH", .scramble = "LYFISHJEL", .hint = "A drifting sea animal"}, + {.solution = "KEYBOARDS", .scramble = "BOARDKEYS", .hint = "Devices with keys for entering text"}, + {.solution = "KNOWLEDGE", .scramble = "LEDGEKNOW", .hint = "Information and understanding"}, + {.solution = "LANDSCAPE", .scramble = "SCAPELAND", .hint = "The visible features of an area"}, + {.solution = "LIGHTNING", .scramble = "NINGLIGHT", .hint = "A flash of electricity in a storm"}, + {.solution = "MAGNETISM", .scramble = "NETISMMAG", .hint = "A force that attracts iron"}, + {.solution = "MOUNTAINS", .scramble = "TAINSMOUN", .hint = "Very high natural elevations"}, + {.solution = "MUSICALLY", .scramble = "CALLYMUSI", .hint = "In a way related to music"}, + {.solution = "NOTEBOOKS", .scramble = "BOOKSNOTE", .hint = "Books used for writing notes"}, + {.solution = "ORCHESTRA", .scramble = "ESTRAORCH", .hint = "A large group of instrumentalists"}, + {.solution = "PAINTINGS", .scramble = "TINGSPAIN", .hint = "Pictures made with paint"}, + {.solution = "PAPERBACK", .scramble = "BACKPAPER", .hint = "A book with a flexible cover"}, + {.solution = "PETROLEUM", .scramble = "MOUERTPLE", .hint = "Crude oil and its derivatives"}, + {.solution = "PINEAPPLE", .scramble = "APPLEPINE", .hint = "A tropical fruit with spiky skin"}, + {.solution = "POLITICAL", .scramble = "TICALPOLI", .hint = "Relating to government"}, + {.solution = "RECTANGLE", .scramble = "CTRELAGNE", .hint = "A four-sided flat shape"}, + {.solution = "SOMETHING", .scramble = "THINGSOME", .hint = "An unspecified object or matter"}, + {.solution = "SPECTATOR", .scramble = "POSTCATER", .hint = "Someone watching an event"}, + {.solution = "STARTLING", .scramble = "GTLNRSAIT", .hint = "Surprising or astonishing"}, + {.solution = "SUNFLOWER", .scramble = "FLOWERSUN", .hint = "A tall plant with a yellow bloom"}, + {.solution = "TELESCOPE", .scramble = "SCOPETELE", .hint = "An instrument for viewing distant objects"}, + {.solution = "UNDERLINE", .scramble = "LINEUNDER", .hint = "A line drawn beneath text"}, + {.solution = "WONDERFUL", .scramble = "DERFULWON", .hint = "Extremely good"}, + {.solution = "WORKPLACE", .scramble = "PLACEWORK", .hint = "A location where people work"}, }; -static constexpr size_t kConundrumTableSize = - sizeof(kConundrumTable) / sizeof(kConundrumTable[0]); -} // namespace +const std::size_t kConundrumTableSize = + std::size(kConundrumTable); +static_assert(std::size(kConundrumTable) - 1 <= + std::numeric_limits::max(), + "Conundrum table indices must fit IRandomSource::nextRange"); -ConundrumRound::ConundrumRound(const std::string& solution, - const std::string& scramble, - const std::string& hint) - : solution_(solution), scramble_(scramble), hint_(hint) {} +ConundrumRound::ConundrumRound(std::string solution, + std::string scramble, + std::string hint) + : solution_(std::move(solution)), scramble_(std::move(scramble)), hint_(std::move(hint)) {} ConundrumRound ConundrumRound::createRandom(IRandomSource& random) { + if (kConundrumTableSize == 0) { + return ConundrumRound{"", "", ""}; + } size_t idx = random.nextRange(0, static_cast(kConundrumTableSize - 1)); const auto& entry = kConundrumTable[idx]; - return ConundrumRound(entry.solution, entry.scramble, entry.hint); + return {entry.solution, entry.scramble, entry.hint}; } CommandResult ConundrumRound::submitAttempt(uint32_t boardId, @@ -558,6 +693,9 @@ CommandResult ConundrumRound::submitAttempt(uint32_t boardId, if (solved_) { return CommandResult{false, "ALREADY_SOLVED", "Conundrum already solved"}; } + if (expired_) { + return CommandResult{false, "DEADLINE_ELAPSED", "Buzz-window has expired"}; + } std::string clean = attempt; std::string correct = solution_; for (auto& c : clean) c = static_cast(std::toupper(static_cast(c))); @@ -574,9 +712,11 @@ CommandResult ConundrumRound::submitAttempt(uint32_t boardId, ConundrumRoundProjection ConundrumRound::projection() const { ConundrumRoundProjection p{}; + std::strncpy(p.solution, solution_.c_str(), sizeof(p.solution) - 1); std::strncpy(p.scramble, scramble_.c_str(), sizeof(p.scramble) - 1); std::strncpy(p.hint, hint_.c_str(), sizeof(p.hint) - 1); p.solved = solved_; + p.expired = expired_; p.winnerBoardId = winnerBoardId_; return p; } @@ -591,9 +731,19 @@ CommandResult ConundrumRound::applyCommand(const CommandContext& ctx, return submitAttempt(ctx.senderBoardId, attempt); } +void ConundrumRound::markExpired() { + expired_ = true; +} + std::optional ConundrumRound::tryFinalize( const std::vector& /*playerBoardIds*/) { - if (!solved_) return std::nullopt; + if (!solved_ && !expired_) return std::nullopt; + if (expired_ && !solved_) { + RoundResult res; + res.roundType = RoundType::Conundrum; + res.isTie = true; + return res; + } RoundResult res; res.roundType = RoundType::Conundrum; @@ -643,27 +793,33 @@ void CountdownMatchEngine::startNextRound(RoundType type, IRandomSource& random, NumbersRound* CountdownMatchEngine::numbersRound() { return (activeRound_ && activeRoundType_ == RoundType::Numbers) - ? static_cast(activeRound_.get()) : nullptr; + ? static_cast(activeRound_.get()) // NOLINT(cppcoreguidelines-pro-type-static-cast-downcast) + : nullptr; } const NumbersRound* CountdownMatchEngine::numbersRound() const { return (activeRound_ && activeRoundType_ == RoundType::Numbers) - ? static_cast(activeRound_.get()) : nullptr; + ? static_cast(activeRound_.get()) // NOLINT(cppcoreguidelines-pro-type-static-cast-downcast) + : nullptr; } LettersRound* CountdownMatchEngine::lettersRound() { return (activeRound_ && activeRoundType_ == RoundType::Letters) - ? static_cast(activeRound_.get()) : nullptr; + ? static_cast(activeRound_.get()) // NOLINT(cppcoreguidelines-pro-type-static-cast-downcast) + : nullptr; } const LettersRound* CountdownMatchEngine::lettersRound() const { return (activeRound_ && activeRoundType_ == RoundType::Letters) - ? static_cast(activeRound_.get()) : nullptr; + ? static_cast(activeRound_.get()) // NOLINT(cppcoreguidelines-pro-type-static-cast-downcast) + : nullptr; } ConundrumRound* CountdownMatchEngine::conundrumRound() { return (activeRound_ && activeRoundType_ == RoundType::Conundrum) - ? static_cast(activeRound_.get()) : nullptr; + ? static_cast(activeRound_.get()) // NOLINT(cppcoreguidelines-pro-type-static-cast-downcast) + : nullptr; } const ConundrumRound* CountdownMatchEngine::conundrumRound() const { return (activeRound_ && activeRoundType_ == RoundType::Conundrum) - ? static_cast(activeRound_.get()) : nullptr; + ? static_cast(activeRound_.get()) // NOLINT(cppcoreguidelines-pro-type-static-cast-downcast) + : nullptr; } NumbersRoundProjection CountdownMatchEngine::numbersProjection() const { @@ -709,5 +865,4 @@ void CountdownMatchEngine::recordRoundResult(const RoundResult& result) { } } -} // namespace countdown -} // namespace flip7 +} // namespace flip7::countdown diff --git a/core/tests/CMakeLists.txt b/core/tests/CMakeLists.txt index d9aec9c..92b6a63 100644 --- a/core/tests/CMakeLists.txt +++ b/core/tests/CMakeLists.txt @@ -6,14 +6,16 @@ project(flip7core_tests LANGUAGES CXX) find_package(GTest REQUIRED) -# Test executable +# Build the core library first (defines flip7core target) +set(BUILD_TESTS OFF) +add_subdirectory(..) + +# Test executable - only include test files that actually exist add_executable(flip7core_tests protocol_test.cpp serialization_test.cpp reconciliation_test.cpp - mastermind_test.cpp - puzzle_test.cpp - golden_packet_test.cpp + countdown_test.cpp ) target_link_libraries(flip7core_tests PRIVATE @@ -22,8 +24,4 @@ target_link_libraries(flip7core_tests PRIVATE GTest::Main ) -add_test(NAME flip7core_tests COMMAND flip7core_tests) - -# Also build the main library with tests enabled -set(BUILD_TESTS ON) -add_subdirectory(..) \ No newline at end of file +add_test(NAME flip7core_tests COMMAND flip7core_tests) \ No newline at end of file diff --git a/core/tests/countdown_test.cpp b/core/tests/countdown_test.cpp index a9aca8d..a8de72e 100644 --- a/core/tests/countdown_test.cpp +++ b/core/tests/countdown_test.cpp @@ -3,10 +3,67 @@ */ #include +#include +#include +#include +#include +#include +#include + +#include "flip7/conundrum_table.hpp" #include "flip7/countdown_engine.hpp" using namespace flip7::countdown; +namespace { + +std::array letterHistogram(std::string_view word) { + std::array counts{}; + for (char c : word) { + EXPECT_GE(c, 'A'); + EXPECT_LE(c, 'Z'); + ++counts[c - 'A']; + } + return counts; +} + +} // namespace + +TEST(ConundrumTable, AllEntriesAreValid) { + std::set uniqueSolutions; + + for (std::size_t i = 0; i < kConundrumTableSize; ++i) { + const auto& entry = kConundrumTable[i]; + SCOPED_TRACE(::testing::Message() << "Conundrum entry " << i); + + ASSERT_NE(entry.solution, nullptr); + ASSERT_NE(entry.scramble, nullptr); + ASSERT_NE(entry.hint, nullptr); + SCOPED_TRACE(entry.solution); + + ASSERT_EQ(std::strlen(entry.solution), 9u) + << "Solution must be exactly 9 letters."; + ASSERT_FALSE(std::string_view(entry.solution).empty()); + + for (char c : std::string_view(entry.solution)) { + EXPECT_TRUE(std::isupper(static_cast(c))) + << "Solution contains lowercase characters."; + } + + ASSERT_EQ(std::strlen(entry.scramble), 9u) + << "Scramble must be exactly 9 letters."; + EXPECT_NE(std::string(entry.solution), std::string(entry.scramble)) + << "Scramble is identical to solution."; + EXPECT_EQ(letterHistogram(entry.solution), letterHistogram(entry.scramble)) + << "Scramble is not an exact anagram."; + + EXPECT_FALSE(std::string_view(entry.hint).empty()) + << "Hint may not be empty."; + EXPECT_TRUE(uniqueSolutions.insert(entry.solution).second) + << "Duplicate solution found."; + } +} + // --------------------------------------------------------------------------- // makeRoundRandom — determinism // --------------------------------------------------------------------------- @@ -76,7 +133,7 @@ TEST(CountdownTest, NumbersRoundArithmeticStep) { // 75 + 25 would fail — 25 is consumed auto r2 = round.applyStep(0x01, ArithmeticOp::Add, 75, 25); - EXPECT_FALSE(r2.accepted); // 25 no longer available + EXPECT_FALSE(r2.accepted); // 25 no longer available // 75 + 4 = 79 auto r3 = round.applyStep(0x01, ArithmeticOp::Add, 75, 4); @@ -96,7 +153,8 @@ TEST(CountdownTest, NumbersRoundDivisionValidation) { EXPECT_TRUE(r1.accepted); // Non-integer division rejected: 25 / 4 - round.submitClaim(0x02, 0); + auto claim2 = round.submitClaim(0x02, 100); + EXPECT_TRUE(claim2.accepted); auto r2 = round.applyStep(0x02, ArithmeticOp::Divide, 25, 4); EXPECT_FALSE(r2.accepted); EXPECT_EQ(r2.errorCode, "INVALID_DIVISION"); @@ -154,7 +212,7 @@ TEST(CountdownTest, LettersRoundValidSubset) { TEST(CountdownTest, LettersRoundClaimsAndPresentation) { LettersRound round; // Manually set letters for determinism - for (char c : std::string("AEIORCNTS")) { + for (char c: std::string("AEIORCNTS")) { // Use applyCommand to add each letter CommandContext ctx{0x10, 0}; std::vector payload = {static_cast(c)}; @@ -182,7 +240,7 @@ TEST(CountdownTest, ConundrumRoundSimultaneousAttempts) { ConundrumRound round("BLUEPRINT", "TNPULEBRI", "A detailed plan or design"); EXPECT_EQ(round.solution(), "BLUEPRINT"); - EXPECT_EQ(round.hint(), "A detailed plan or design"); + EXPECT_EQ(round.hint(), "A detailed plan or design"); EXPECT_FALSE(round.isSolved()); // Player 1 incorrect — no lockout @@ -237,14 +295,14 @@ TEST(CountdownTest, MatchEngineStartNextRoundCreatesRound) { } TEST(CountdownTest, MatchEngineLeaderHostMigrationAndChooserUpdate) { - uint32_t hostId = 0xA1; - uint32_t guestId = 0xB2; - uint32_t gameId = 999; + uint32_t hostId = 0xA1; + uint32_t guestId = 0xB2; + uint32_t gameId = 999; CountdownMatchEngine engine(hostId, guestId, gameId); - EXPECT_EQ(engine.hostBoardId(), hostId); - EXPECT_EQ(engine.guestBoardId(), guestId); - EXPECT_EQ(engine.chooserBoardId(), hostId); // host chooses first + EXPECT_EQ(engine.hostBoardId(), hostId); + EXPECT_EQ(engine.guestBoardId(), guestId); + EXPECT_EQ(engine.chooserBoardId(), hostId); // host chooses first EXPECT_EQ(engine.hostTerm(), 1u); auto random = makeRoundRandom(gameId, 1u); @@ -252,16 +310,16 @@ TEST(CountdownTest, MatchEngineLeaderHostMigrationAndChooserUpdate) { // Guest wins the round — becomes chooser AND takes score lead → becomes host RoundResult rr; - rr.roundType = RoundType::Numbers; + rr.roundType = RoundType::Numbers; rr.winnerBoardId = guestId; rr.scoreAwards[guestId] = 10; engine.recordRoundResult(rr); // ADR-005: guest took score lead → guest becomes new host - EXPECT_EQ(engine.hostBoardId(), guestId); + EXPECT_EQ(engine.hostBoardId(), guestId); EXPECT_EQ(engine.guestBoardId(), hostId); - EXPECT_EQ(engine.hostTerm(), 2u); // incremented on transfer + EXPECT_EQ(engine.hostTerm(), 2u); // incremented on transfer // chooser = round winner = guestId EXPECT_EQ(engine.chooserBoardId(), guestId); @@ -275,7 +333,7 @@ TEST(CountdownTest, MatchEngineChooserRetainedOnTie) { // Tie round — no winner RoundResult rr; rr.roundType = RoundType::Letters; - rr.isTie = true; + rr.isTie = true; rr.scoreAwards[0xA1] = 5; rr.scoreAwards[0xB2] = 5; @@ -284,7 +342,7 @@ TEST(CountdownTest, MatchEngineChooserRetainedOnTie) { // Tie: previous chooser retained; no host migration (scores equal) EXPECT_EQ(engine.chooserBoardId(), prevChooser); - EXPECT_EQ(engine.hostTerm(), 1u); // no transfer + EXPECT_EQ(engine.hostTerm(), 1u); // no transfer } // --------------------------------------------------------------------------- @@ -297,7 +355,7 @@ TEST(CountdownTest, IRoundNumbersApplyCommandClaim) { CommandContext ctx{0x01, 0}; // Encode claimed value 350 as little-endian uint32 - std::vector payload = {0x5E, 0x01, 0x00, 0x00}; // 0x15E = 350 + std::vector payload = {0x5E, 0x01, 0x00, 0x00}; // 0x15E = 350 auto result = round.applyCommand(ctx, CommandType::NumSubmitClaim, payload); EXPECT_TRUE(result.accepted); EXPECT_EQ(round.claimFor(0x01), 350); @@ -306,7 +364,7 @@ TEST(CountdownTest, IRoundNumbersApplyCommandClaim) { TEST(CountdownTest, IRoundConundrumApplyCommand) { ConundrumRound round("STARTLING", "GTLNRSAIT", "Surprising"); CommandContext ctx{0x10, 0}; - std::vector attempt{'S','T','A','R','T','L','I','N','G'}; + std::vector attempt{'S', 'T', 'A', 'R', 'T', 'L', 'I', 'N', 'G'}; auto result = round.applyCommand(ctx, CommandType::ConSubmitAttempt, attempt); EXPECT_TRUE(result.accepted); EXPECT_TRUE(round.isSolved()); @@ -317,17 +375,16 @@ TEST(CountdownTest, IRoundConundrumApplyCommand) { // --------------------------------------------------------------------------- TEST(CountdownTest, FixedVectorCapacity) { - FixedVector v; + FixedVector < int, 3 > v; EXPECT_TRUE(v.empty()); EXPECT_TRUE(v.push_back(1)); EXPECT_TRUE(v.push_back(2)); EXPECT_TRUE(v.push_back(3)); EXPECT_TRUE(v.full()); - EXPECT_FALSE(v.push_back(4)); // over capacity + EXPECT_FALSE(v.push_back(4)); // over capacity EXPECT_EQ(v.size(), 3u); EXPECT_EQ(v[0], 1); EXPECT_TRUE(v.pop_back()); EXPECT_EQ(v.size(), 2u); EXPECT_FALSE(v.full()); } - From 20eaaa813ffa81ed9cc55799f41efdf3c0d9ed3a Mon Sep 17 00:00:00 2001 From: Christopher Mayfield Date: Sat, 8 Aug 2026 14:06:01 -0500 Subject: [PATCH 10/12] fix: add missing conundrum_table.hpp from merge --- core/include/flip7/conundrum_table.hpp | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 core/include/flip7/conundrum_table.hpp diff --git a/core/include/flip7/conundrum_table.hpp b/core/include/flip7/conundrum_table.hpp new file mode 100644 index 0000000..cf25a66 --- /dev/null +++ b/core/include/flip7/conundrum_table.hpp @@ -0,0 +1,16 @@ +#pragma once + +#include + +namespace flip7::countdown { + +struct ConundrumEntry { + const char* solution; + const char* scramble; + const char* hint; +}; + +extern const ConundrumEntry kConundrumTable[]; +extern const std::size_t kConundrumTableSize; + +} // namespace flip7::countdown From f5b82149981f784a403207a8d06c08571dca8ff8 Mon Sep 17 00:00:00 2001 From: Christopher Mayfield Date: Sat, 8 Aug 2026 14:08:33 -0500 Subject: [PATCH 11/12] fix: add -Icore/include to CI test compilation --- .github/workflows/build.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 6c93626..b5fa925 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -20,7 +20,7 @@ jobs: run: pio run - name: Test game logic run: | - g++ -std=c++17 -Wall -Wextra -Werror -Iinclude test/puzzle_logic_test.cpp -o /tmp/puzzle_logic_test + g++ -std=c++17 -Wall -Wextra -Werror -Iinclude -Icore/include test/puzzle_logic_test.cpp -o /tmp/puzzle_logic_test /tmp/puzzle_logic_test - g++ -std=c++17 -Wall -Wextra -Werror -Iinclude test/mastermind_logic_test.cpp -o /tmp/mastermind_logic_test + g++ -std=c++17 -Wall -Wextra -Werror -Iinclude -Icore/include test/mastermind_logic_test.cpp -o /tmp/mastermind_logic_test /tmp/mastermind_logic_test From 2d5aee4e6a20d5b2039cac931308ca1b9d0eb555 Mon Sep 17 00:00:00 2001 From: Christopher Mayfield Date: Sat, 8 Aug 2026 14:10:30 -0500 Subject: [PATCH 12/12] chore: bump actions/checkout@v4->v5, actions/setup-python@v6->v7 for Node 24 --- .github/workflows/build.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b5fa925..4905c31 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -6,8 +6,8 @@ jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 - - uses: actions/setup-python@v6 + - uses: actions/checkout@v5 + - uses: actions/setup-python@v7 with: python-version: "3.13" - name: Install PlatformIO