forked from retoor/devplacepy
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:
@@ -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)
|
||||
Reference in New Issue
Block a user