From 8f167778797e79f1891d0ecf2b74342f66ea28ac Mon Sep 17 00:00:00 2001 From: chris bell Date: Tue, 25 Aug 2026 21:28:37 -0500 Subject: [PATCH] starting on round logic --- src/Creature.cpp | 20 +++++++ src/Creature.h | 20 +++++++ src/Round.h | 36 +++++++++++++ src/round.cpp | 137 +++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 213 insertions(+) create mode 100644 src/Creature.cpp create mode 100644 src/Creature.h create mode 100644 src/Round.h create mode 100644 src/round.cpp diff --git a/src/Creature.cpp b/src/Creature.cpp new file mode 100644 index 0000000..425db35 --- /dev/null +++ b/src/Creature.cpp @@ -0,0 +1,20 @@ +#include "Creature.h" + +bool SpeciesEatsWhenNormal(Species species) { + switch (species) { + + case Species::Raccoon: + case Species::Opossum: + return true; + case Species::Fox: + case Species::Deer: + return false; + } + + return true; +} + +bool WouldEatMarshmallow(const Creature &c) { + bool n = SpeciesEatsWhenNormal(c.species); + return c.behaves_normally ? n : !n; +} diff --git a/src/Creature.h b/src/Creature.h new file mode 100644 index 0000000..079f204 --- /dev/null +++ b/src/Creature.h @@ -0,0 +1,20 @@ +#pragma once + +enum class Species { Raccoon, Opossum, Deer, Fox }; +enum class AppearanceTier { Standard, Subtle1, Subtle2, Obvious }; + +struct Creature { + Species species; + bool is_cryptid = false; + AppearanceTier appearance = AppearanceTier::Standard; + bool behaves_normally = true; + + bool has_been_fed = false; + bool observed_reaction = false; + + bool captured = false; + bool resolved = false; +}; + +bool SpeciesEatsWhenNormal(Species species); +bool WouldEatMarshmallow(const Creature &c); \ No newline at end of file diff --git a/src/Round.h b/src/Round.h new file mode 100644 index 0000000..6294d57 --- /dev/null +++ b/src/Round.h @@ -0,0 +1,36 @@ +#pragma once + +#include "Creature.h" +#include +struct RoundConfig { + int creature_count = 5; + int marshmallow_supply = 2; + float cryptid_ratio = 0.35f; + + // Appearance weights + float w_standard = 0.35f; + float w_subtle1 = 0.30f; + float w_subtle2 = 0.20f; + float w_obvious = 0.15f; +}; + +RoundConfig MakeRoundConfig(int roundIndex); + +struct Round { + RoundConfig conf; + std::vector creatures; + int currentIndex = 0; + int marshmallowsRemaining = 0; + int miscaptures = 0; + int missedCryptids = 0; + + bool IsComplete() const; + Creature *CurrentCreature(); + float SuccessRate() const; // (1 - (miscaptures / creature_count)) + bool Passed() const; // SuccessRate >= 0.8 +}; + +Round GenerateRound(const RoundConfig &config); + +bool FeedCurrentCreature(Round &round); +bool ResolveCurrentCreature(Round &round, bool captured); \ No newline at end of file diff --git a/src/round.cpp b/src/round.cpp new file mode 100644 index 0000000..18d7937 --- /dev/null +++ b/src/round.cpp @@ -0,0 +1,137 @@ +#include "Round.h" +#include "Creature.h" +#include +#include +#include + +namespace { +AppearanceTier RollAppearanceTier(const RoundConfig &cfg) { + float roll = (float)GetRandomValue(0, 10000) / 10000.0f; + float t1 = cfg.w_standard; + float t2 = t1 + cfg.w_subtle1; + float t3 = t2 + cfg.w_subtle2; + + if (roll < t1) + return AppearanceTier::Standard; + if (roll < t2) + return AppearanceTier::Subtle1; + if (roll < t3) + return AppearanceTier::Subtle2; + return AppearanceTier::Obvious; +} + +float IncorrectBehaviourChance(AppearanceTier tier) { + switch (tier) { + case AppearanceTier::Standard: + return 1.0f; + case AppearanceTier::Subtle1: + return 0.5f; + case AppearanceTier::Subtle2: + return 0.5f; + case AppearanceTier::Obvious: + return 0.3f; + } + return 1.0f; +} + +Species RandomSpecies() { return static_cast(GetRandomValue(0, 3)); } +} // namespace + +RoundConfig MakeRoundConfig(int roundIndex) { + RoundConfig cfg; + cfg.creature_count = std::min(5 + roundIndex * 2, 20); + cfg.marshmallow_supply = + std::max(2, (int)std::round(cfg.creature_count * 0.4f)); + + float rampedStandard = std::min(0.35f + roundIndex * 0.03f, 0.70f); + float remaining = 1.0f - rampedStandard; + float baseRemaining = 0.30f + 0.20f + 0.15f; + cfg.w_standard = rampedStandard; + cfg.w_subtle1 = remaining * (0.30f / baseRemaining); + cfg.w_subtle2 = remaining * (0.20f / baseRemaining); + cfg.w_obvious = remaining * (0.15f / baseRemaining); + + return cfg; +} + +Round GenerateRound(const RoundConfig &config) { + Round round; + round.conf = config; + round.marshmallowsRemaining = config.marshmallow_supply; + + int cryptidCount = + std::clamp((int)std::round(config.creature_count * config.cryptid_ratio), + 1, config.creature_count - 1); + + for (int i = 0; i < config.creature_count; ++i) { + Creature c; + c.species = RandomSpecies(); + c.is_cryptid = i < cryptidCount; + + if (!c.is_cryptid) { + c.appearance = AppearanceTier::Standard; + c.behaves_normally = true; + } else { + c.appearance = RollAppearanceTier(config); + float lieChance = IncorrectBehaviourChance(c.appearance); + float roll = (float)GetRandomValue(0, 10000) / 10000.0f; + c.behaves_normally = !(roll < lieChance); + } + + round.creatures.push_back(c); + } + + // Shuffle so cryptids aren't all front-loaded. + for (int i = (int)round.creatures.size() - 1; i > 0; --i) { + int j = GetRandomValue(0, i); + std::swap(round.creatures[i], round.creatures[j]); + } + + return round; +} + +bool FeedCurrentCreature(Round &round) { + Creature *c = round.CurrentCreature(); + if (!c || c->has_been_fed || round.marshmallowsRemaining <= 0) + return false; + + c->has_been_fed = true; + c->observed_reaction = true; + round.marshmallowsRemaining--; + return true; +} + +bool ResolveCurrentCreature(Round &round, bool captured) { + Creature *c = round.CurrentCreature(); + if (!c) + return false; + + c->captured = captured; + c->resolved = true; + + bool died = false; + if (!captured && c->is_cryptid) { + died = true; // let a real cryptid go — run over + } else if (captured && !c->is_cryptid) { + round.miscaptures++; + } + + round.currentIndex++; + return died; +} + +bool Round::IsComplete() const { return currentIndex >= (int)creatures.size(); } + +Creature *Round::CurrentCreature() { + if (IsComplete()) + return nullptr; + return &creatures[currentIndex]; +} + +float Round::SuccessRate() const { + if (conf.creature_count == 0) + return 1.0f; + return 1.0f - (float)miscaptures / (float)conf.creature_count; +} + +bool Round::Passed() const { return SuccessRate() >= 0.8f; } \ No newline at end of file