Construction: buildings rise by labour; planks and hammers

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
rope 2026-10-19 09:24:45 +01:00
parent 4093245b8e
commit a4e4bd4385
6 changed files with 180 additions and 47 deletions

View file

@ -189,7 +189,9 @@ class CityTest(unittest.TestCase):
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(city["construction"]["needed"], game.LABOUR_PER_LEVEL)
city["construction"]["labour"] = game.LABOUR_PER_LEVEL
game.tick(world, T0 + 1, random.Random(1))
self.assertEqual(city["buildings"]["walls"]["level"], 1)
self.assertEqual(game.defence(world)["parts"]["walls"], 20)
@ -203,7 +205,8 @@ class CityTest(unittest.TestCase):
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))
city["construction"]["labour"] = city["construction"]["needed"]
game.tick(world, T0 + 1, random.Random(1))
self.assertEqual(city["construction"]["building"], "houses")
self.assertEqual(city["queue"], [])
@ -505,15 +508,15 @@ class FavourTest(unittest.TestCase):
self.p = self.world["players"]["fred"]
self.p["favour"] = 5000
def test_builders_halve_the_time_left(self):
def test_builders_do_half_the_work_left(self):
world, p = self.world, self.p
self.assertEqual(game.spend_favour(world, p, "builders", T0), "Nothing is being built")
world["city"]["construction"] = {"building": "walls", "repair": False, "start": T0, "done_at": T0 + 4 * 3600}
world["city"]["construction"] = {"building": "walls", "repair": False, "start": T0, "labour": 60.0, "needed": 360}
cost = game.favours(world, T0)["builders"]["cost"]
self.assertEqual(cost, 160)
self.assertEqual(cost, 101) # 150 labour at 0.67 favour each
self.assertIsNone(game.spend_favour(world, p, "builders", T0))
self.assertEqual(world["city"]["construction"]["done_at"], T0 + 2 * 3600)
self.assertEqual(p["favour"], 5000 - 160)
self.assertEqual(world["city"]["construction"]["labour"], 210)
self.assertEqual(p["favour"], 5000 - 101)
def test_informant_raises_intel(self):
world, p = self.world, self.p
@ -543,8 +546,8 @@ class FavourTest(unittest.TestCase):
def test_not_enough_favour(self):
self.p["favour"] = 10
self.world["city"]["construction"] = {"building": "walls", "repair": False, "start": T0, "done_at": T0 + 3600}
self.assertEqual(game.spend_favour(self.world, self.p, "builders", T0), "Needs 40 favour")
self.world["city"]["construction"] = {"building": "walls", "repair": False, "start": T0, "labour": 0.0, "needed": 360}
self.assertEqual(game.spend_favour(self.world, self.p, "builders", T0), "Needs 121 favour")
@ -761,5 +764,69 @@ class HerbloreTest(unittest.TestCase):
self.assertEqual((p["potions"], p["xp"]["herblore"]), ({}, 0))
class ConstructionTest(unittest.TestCase):
def site(self):
world = world_with(("bob", "pioneer"), ("ann", "warrior"))
world["city"]["construction"] = {"building": "walls", "repair": False, "start": T0, "labour": 0.0, "needed": 360}
world["next_event_at"] = None
return world
def test_players_build_with_their_hands(self):
world = self.site()
p = world["players"]["ann"]
game.start_action(world, p, "labour", T0)
summary = game.process_player(world, p, T0 + 30 * 100, random.Random(1))
self.assertEqual(summary["labour"], 100)
self.assertEqual(world["city"]["construction"]["labour"], 100)
self.assertGreater(p["xp"]["construction"], 0)
def test_finishing_the_site_raises_the_building_and_stops_the_work(self):
world = self.site()
world["city"]["construction"]["labour"] = 355
p = world["players"]["ann"]
game.start_action(world, p, "labour", T0)
summary = game.process_player(world, p, T0 + 30 * 10, random.Random(1))
self.assertEqual(world["city"]["buildings"]["walls"]["level"], 1)
self.assertIsNone(world["city"]["construction"])
self.assertEqual(summary["stopped"], "Nothing is being built")
def test_townsfolk_trickle_and_levels_help(self):
world = self.site()
game.tick(world, T0 + 10 * game.HOUR, random.Random(1))
self.assertAlmostEqual(world["city"]["construction"]["labour"], 10 * 20 * game.TOWNSFOLK_LABOUR, delta=5)
p = world["players"]["bob"]
self.assertEqual(game.labour_per_action(p), 1)
p["xp"]["construction"] = game.XP_TABLE[40]
self.assertEqual(game.labour_per_action(p), 3)
plain = game.action_time(world, p, game.ACTION_BY_ID["labour"])
p["equip"]["tool_construction"] = "iron_hammer"
self.assertAlmostEqual(game.action_time(world, p, game.ACTION_BY_ID["labour"]), plain * 0.7)
def test_no_site_no_work(self):
world = world_with(("ann", "warrior"))
self.assertEqual(game.start_action(world, world["players"]["ann"], "labour", T0), "Nothing is being built")
def test_planks_and_hammers_exist_for_every_tier(self):
for plank, _name in game.PLANKS:
self.assertIn(f"saw_{plank}", game.ACTION_BY_ID)
for key, *_ in game.METALS:
self.assertIn(f"smith_{key}_hammer", game.ACTION_BY_ID)
self.assertIn("oak_plank", game.build_cost("houses", 3))
def test_v6_timer_becomes_labour(self):
world = world_with(("fred", "pioneer"))
world["version"] = 6
p = world["players"]["fred"]
del p["xp"]["construction"], p["equip"]["tool_construction"]
world["city"]["construction"] = {"building": "houses", "repair": False, "start": T0, "done_at": T0 + 3 * 3600}
game.migrate(world, T0 + 3600)
work = world["city"]["construction"]
self.assertEqual(work["needed"], game.LABOUR_PER_LEVEL)
self.assertAlmostEqual(work["labour"], game.LABOUR_PER_LEVEL / 3)
self.assertNotIn("done_at", work)
self.assertIsNone(p["equip"]["tool_construction"])
if __name__ == "__main__":
unittest.main()