Building funds safe from raids; settlement tiers; population and food on the town square

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
rope 2026-10-18 19:20:52 +01:00
parent e3b70c1e3a
commit 3716df7493
6 changed files with 358 additions and 91 deletions

View file

@ -125,24 +125,112 @@ class GiftTest(unittest.TestCase):
class CityTest(unittest.TestCase):
def test_build_needs_stockpile_then_takes_time(self):
def test_fund_from_bank_and_stores_then_builds(self):
world = world_with(("fred", "warrior"))
p = world["players"]["fred"]
self.assertIn("short", game.commission(world, p, "walls", T0))
world["city"]["stockpile"].update({"stone": 1200, "bricks": 500})
world["city"]["treasury"] = 300
self.assertIsNone(game.commission(world, p, "walls", T0))
self.assertEqual(world["city"]["treasury"], 0)
self.assertNotIn("stone", world["city"]["stockpile"])
city = world["city"]
p["bank"] = {"stone": 1500}
city["stockpile"] = {"bricks": 600}
city["treasury"] = 300
self.assertIsNone(game.fund(world, p, "walls", "stone", "all", "bank", T0))
self.assertEqual(p["bank"], {"stone": 300}) # only what the fund needed
self.assertEqual(p["favour"], 1200)
self.assertIsNone(game.fund(world, p, "walls", "bricks", 500, "city", T0))
self.assertEqual(city["stockpile"], {"bricks": 100})
self.assertIsNone(city["construction"])
self.assertEqual(game.fund(world, p, "walls", "stone", 1, "bank", T0), "The fund doesn't need that")
self.assertIsNone(game.fund(world, p, "walls", "gold", "all", "city", T0))
self.assertEqual(city["construction"]["building"], "walls")
self.assertEqual(city["buildings"]["walls"]["fund"], {})
game.tick(world, T0 + 3 * game.HOUR, random.Random(1))
self.assertEqual(world["city"]["buildings"]["walls"]["level"], 1)
self.assertEqual(city["buildings"]["walls"]["level"], 1)
self.assertEqual(game.defence(world)["parts"]["walls"], 20)
def test_funded_buildings_queue_behind_the_builders(self):
world = world_with(("fred", "warrior"))
p = world["players"]["fred"]
city = world["city"]
for key in ("walls", "houses"):
city["buildings"][key]["fund"] = {k: v for k, v in game.build_cost(key, 1).items() if k != "gold"}
city["treasury"] += game.build_cost(key, 1)["gold"]
self.assertIsNone(game.fund(world, p, key, "gold", "all", "city", T0))
self.assertEqual(city["construction"]["building"], "walls")
self.assertEqual(city["queue"], ["houses"])
game.tick(world, T0 + 3 * game.HOUR, random.Random(1))
self.assertEqual(city["construction"]["building"], "houses")
self.assertEqual(city["queue"], [])
def test_raids_never_touch_funds(self):
world = world_with(("fred", "warrior"))
city = world["city"]
city["buildings"]["walls"]["fund"] = {"stone": 1000}
city["stockpile"] = {"stone": 1000}
world["next_event_at"] = T0
game.tick(world, T0, random.Random(2))
raid = game.upcoming_raid(world)
raid["strength"] = 500
game.tick(world, raid["fire_at"], random.Random(1))
self.assertLess(city["stockpile"]["stone"], 1000)
self.assertEqual(city["buildings"]["walls"]["fund"], {"stone": 1000})
def test_tier_caps_levels_and_promotes_for_good(self):
world = world_with(("fred", "warrior"))
city = world["city"]
city["buildings"]["walls"]["level"] = 1
self.assertIsNone(game.fund_target(world, "walls")) # hamlet: level 1 only
self.assertIsNotNone(game.fund_target(world, "houses")) # houses go to 2
slots = game.bank_slots(world, world["players"]["fred"])
city["population"] = 41
world["next_event_at"] = None
game.tick(world, T0 + 1, random.Random(1))
self.assertEqual(city["tier"], 1)
self.assertIsNotNone(game.fund_target(world, "walls"))
self.assertEqual(game.bank_slots(world, world["players"]["fred"]), slots + game.TIER_SLOTS)
city["population"] = 10
game.tick(world, T0 + 2, random.Random(1))
self.assertEqual(city["tier"], 1)
def test_damage_turns_the_fund_towards_repairs_and_keeps_the_rest(self):
world = world_with(("fred", "warrior"))
p = world["players"]["fred"]
city = world["city"]
city["buildings"]["walls"].update(level=1, damaged=True, fund={"stone": 2000})
repair = game.repair_cost("walls", 1)
self.assertEqual(game.fund_target(world, "walls"), repair)
for item, qty in repair.items():
if item == "gold":
city["treasury"] = qty
game.fund(world, p, "walls", "gold", "all", "city", T0)
elif item != "stone":
city["stockpile"][item] = qty
game.fund(world, p, "walls", item, "all", "city", T0)
self.assertTrue(city["construction"]["repair"])
self.assertEqual(city["buildings"]["walls"]["fund"], {"stone": 2000 - repair["stone"]})
def test_old_worlds_migrate(self):
world = game.new_world(T0)
world["version"] = 1
for building in world["city"]["buildings"].values():
del building["fund"]
del world["city"]["queue"], world["city"]["tier"]
game.migrate(world)
self.assertEqual(world["version"], game.WORLD_VERSION)
self.assertEqual(world["city"]["queue"], [])
self.assertEqual(world["city"]["buildings"]["walls"]["fund"], {})
def test_population_view_explains_growth(self):
world = world_with()
view = game.population_view(world)
self.assertEqual(view["tier_name"], "Hamlet")
self.assertEqual(view["next_tier"], {"name": "Village", "pop": 40})
self.assertGreater(view["growth"], 0)
self.assertEqual(view["food_days"], 4.0) # 40 bread for 20 people eating 0.5 a day
def test_people_eat_and_go_hungry(self):
world = world_with()
city = world["city"]
city["stockpile"] = {"bread": 5}
game.tick(world, T0 + 2 * game.DAY, random.Random(1))
game.tick(world, T0 + 4 * game.DAY, random.Random(1))
self.assertTrue(city["starving"])
self.assertNotIn("bread", city["stockpile"])
self.assertLess(city["population"], 20)