starting on round logic
This commit is contained in:
20
src/Creature.cpp
Normal file
20
src/Creature.cpp
Normal file
@@ -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;
|
||||||
|
}
|
||||||
20
src/Creature.h
Normal file
20
src/Creature.h
Normal file
@@ -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);
|
||||||
36
src/Round.h
Normal file
36
src/Round.h
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "Creature.h"
|
||||||
|
#include <vector>
|
||||||
|
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<Creature> 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);
|
||||||
137
src/round.cpp
Normal file
137
src/round.cpp
Normal file
@@ -0,0 +1,137 @@
|
|||||||
|
#include "Round.h"
|
||||||
|
#include "Creature.h"
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cmath>
|
||||||
|
#include <raylib.h>
|
||||||
|
|
||||||
|
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<Species>(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; }
|
||||||
Reference in New Issue
Block a user