feat: add Code Farm cooperative idle game with plot-based crop system and pub/sub notifications

Implement a Farmville-style cooperative idle game mounted at `/game` with member-only play and public farm viewing. The data layer is pure and timestamp-driven with no background tick: plot states are derived from `ready_at` vs current time, never stored. Key features include plantable software projects (shell script through kernel) that build over real time, harvest for coins and XP, CI tier upgrades for faster builds, plot purchasing with doubling cost, watering mechanics for friends' builds, daily quests, and prestige system. All game endpoints negotiate HTML or JSON and return full farm state for single-request client refresh. Pub/sub notifications broadcast farm updates on `public.game.farm.{username}` topics. Database schema adds `game_farms`, `game_plots`, and `game_quests` tables with appropriate indexes.
This commit is contained in:
2026-06-22 16:41:53 +00:00
parent 743efbf61f
commit ebf6bf7f82
40 changed files with 3860 additions and 5 deletions
+100
View File
@@ -0,0 +1,100 @@
# retoor <retoor@molodetz.nl>
from devplacepy.services.game import economy
def test_level_thresholds():
assert economy.level_for_xp(0) == 1
assert economy.level_for_xp(economy.xp_threshold(2)) == 2
assert economy.level_for_xp(economy.xp_threshold(5)) == 5
assert economy.level_for_xp(economy.xp_threshold(economy.MAX_LEVEL) + 9999) == economy.MAX_LEVEL
def test_level_progress_span():
progress = economy.level_progress(economy.xp_threshold(3))
assert progress["level"] == 3
assert progress["into_level"] == 0
assert progress["is_max"] is False
def test_plot_cost_doubles():
assert economy.plot_cost(economy.STARTING_PLOTS) == economy.PLOT_BASE_COST
assert economy.plot_cost(economy.STARTING_PLOTS + 1) == economy.PLOT_BASE_COST * 2
assert economy.plot_cost(economy.STARTING_PLOTS + 2) == economy.PLOT_BASE_COST * 4
def test_perk_cost_grows():
perk = economy.perk_for("yield")
assert economy.perk_cost(perk, 0) == perk.base_cost
assert economy.perk_cost(perk, 1) == round(perk.base_cost * perk.cost_growth)
assert economy.perk_cost(perk, 2) > economy.perk_cost(perk, 1)
def test_daily_reward_streak_and_cap():
assert economy.daily_reward(1) == economy.DAILY_BASE
assert economy.daily_reward(2) == economy.DAILY_BASE + economy.DAILY_STREAK_STEP
assert economy.daily_reward(99) == economy.daily_reward(economy.DAILY_STREAK_CAP)
def test_effective_plant_cost_discount():
crop = economy.crop_for("python")
assert economy.effective_plant_cost(crop, 0) == crop.cost
assert economy.effective_plant_cost(crop, 3) < crop.cost
assert economy.effective_plant_cost(crop, 3) >= 1
def test_effective_reward_coins_yield_and_prestige():
crop = economy.crop_for("shell")
assert economy.effective_reward_coins(crop, 0, 0) == crop.reward_coins
assert economy.effective_reward_coins(crop, 4, 0) > crop.reward_coins
assert economy.effective_reward_coins(crop, 0, 2) > crop.reward_coins
def test_effective_reward_xp():
crop = economy.crop_for("shell")
assert economy.effective_reward_xp(crop, 0) == crop.reward_xp
assert economy.effective_reward_xp(crop, 10) > crop.reward_xp
def test_growth_perk_speeds_build():
crop = economy.crop_for("python")
assert economy.grow_seconds_for(crop, 1, 5) < economy.grow_seconds_for(crop, 1, 0)
def test_ci_tier_speeds_build():
crop = economy.crop_for("python")
assert economy.grow_seconds_for(crop, 2, 0) < economy.grow_seconds_for(crop, 1, 0)
def test_prestige_multiplier():
assert economy.prestige_multiplier(0) == 1.0
assert economy.prestige_multiplier(2) == 1 + 2 * economy.PRESTIGE_BONUS
def test_fertilize_cost_has_floor():
assert economy.fertilize_cost(1) == economy.FERTILIZE_MIN_COST
assert economy.fertilize_cost(10000) > economy.FERTILIZE_MIN_COST
def test_crop_payload_locked_flag():
rust = economy.crop_for("rust")
assert economy.crop_payload(rust, 1, 1)["locked"] is True
assert economy.crop_payload(rust, 1, rust.min_level)["locked"] is False
def test_crop_payload_reflects_perks():
crop = economy.crop_for("python")
base = economy.crop_payload(crop, 1, 5)
boosted = economy.crop_payload(crop, 1, 5, growth_level=5, discount_level=3, yield_level=4, prestige=1)
assert boosted["cost"] < base["cost"]
assert boosted["grow_seconds"] < base["grow_seconds"]
assert boosted["reward_coins"] > base["reward_coins"]
def test_daily_quests_deterministic_count_and_kinds():
first = economy.daily_quests("user-xyz", "2026-06-22")
second = economy.daily_quests("user-xyz", "2026-06-22")
assert first == second
assert len(first) == economy.DAILY_QUEST_COUNT
assert all(quest["kind"] in economy.QUEST_DEFS for quest in first)
assert all(quest["goal"] > 0 for quest in first)