mapwright 0.21.0__tar.gz → 0.22.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (128) hide show
  1. {mapwright-0.21.0 → mapwright-0.22.0}/CHANGELOG.md +23 -0
  2. {mapwright-0.21.0 → mapwright-0.22.0}/PKG-INFO +14 -7
  3. {mapwright-0.21.0 → mapwright-0.22.0}/README.md +13 -6
  4. mapwright-0.22.0/docs/gallery/fortress-town.png +0 -0
  5. mapwright-0.22.0/docs/gallery/fortress-town.svg +1 -0
  6. {mapwright-0.21.0 → mapwright-0.22.0}/examples/gallery.py +1 -0
  7. {mapwright-0.21.0 → mapwright-0.22.0}/pyproject.toml +1 -1
  8. {mapwright-0.21.0 → mapwright-0.22.0}/src/mapwright/__init__.py +3 -1
  9. {mapwright-0.21.0 → mapwright-0.22.0}/src/mapwright/settlement.py +117 -19
  10. {mapwright-0.21.0 → mapwright-0.22.0}/src/mapwright/settlement_renderer.py +22 -0
  11. {mapwright-0.21.0 → mapwright-0.22.0}/tests/test_api_contract.py +2 -0
  12. {mapwright-0.21.0 → mapwright-0.22.0}/tests/test_settlement.py +81 -5
  13. {mapwright-0.21.0 → mapwright-0.22.0}/.github/workflows/ci.yml +0 -0
  14. {mapwright-0.21.0 → mapwright-0.22.0}/.github/workflows/publish.yml +0 -0
  15. {mapwright-0.21.0 → mapwright-0.22.0}/.gitignore +0 -0
  16. {mapwright-0.21.0 → mapwright-0.22.0}/LICENSE +0 -0
  17. {mapwright-0.21.0 → mapwright-0.22.0}/NOTICE +0 -0
  18. {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/age-old.png +0 -0
  19. {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/age-old.svg +0 -0
  20. {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/age-young.png +0 -0
  21. {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/age-young.svg +0 -0
  22. {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/archipelago.png +0 -0
  23. {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/archipelago.svg +0 -0
  24. {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/arctic.png +0 -0
  25. {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/arctic.svg +0 -0
  26. {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/atlas.png +0 -0
  27. {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/atlas_pack/README.md +0 -0
  28. {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/atlas_pack/city_castle_1.png +0 -0
  29. {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/atlas_pack/city_large_1.png +0 -0
  30. {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/atlas_pack/city_town_1.png +0 -0
  31. {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/atlas_pack/city_village_1.png +0 -0
  32. {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/atlas_pack/decoration_compass_1.png +0 -0
  33. {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/atlas_pack/decoration_creature_1.png +0 -0
  34. {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/atlas_pack/decoration_ship_1.png +0 -0
  35. {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/atlas_pack/dune_1.png +0 -0
  36. {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/atlas_pack/hill_1.png +0 -0
  37. {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/atlas_pack/hill_2.png +0 -0
  38. {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/atlas_pack/manifest.json +0 -0
  39. {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/atlas_pack/mountain_mid_1.png +0 -0
  40. {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/atlas_pack/mountain_old_1.png +0 -0
  41. {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/atlas_pack/mountain_old_2.png +0 -0
  42. {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/atlas_pack/mountain_young_1.png +0 -0
  43. {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/atlas_pack/mountain_young_2.png +0 -0
  44. {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/atlas_pack/tree_cactus_1.png +0 -0
  45. {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/atlas_pack/tree_deciduous_1.png +0 -0
  46. {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/atlas_pack/tree_deciduous_2.png +0 -0
  47. {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/atlas_pack/tree_pine_1.png +0 -0
  48. {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/atlas_pack/tree_pine_2.png +0 -0
  49. {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/citadel.png +0 -0
  50. {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/citadel.svg +0 -0
  51. {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/continent.png +0 -0
  52. {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/continent.svg +0 -0
  53. {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/desert.png +0 -0
  54. {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/desert.svg +0 -0
  55. {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/dungeon.png +0 -0
  56. {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/dungeon.svg +0 -0
  57. {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/grid-city.png +0 -0
  58. {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/grid-city.svg +0 -0
  59. {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/highlands.png +0 -0
  60. {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/highlands.svg +0 -0
  61. {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/hint.png +0 -0
  62. {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/hint.svg +0 -0
  63. {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/islands.png +0 -0
  64. {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/islands.svg +0 -0
  65. {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/metropolis.png +0 -0
  66. {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/metropolis.svg +0 -0
  67. {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/pangaea.png +0 -0
  68. {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/pangaea.svg +0 -0
  69. {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/port.png +0 -0
  70. {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/port.svg +0 -0
  71. {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/regions.png +0 -0
  72. {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/regions.svg +0 -0
  73. {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/roads.png +0 -0
  74. {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/roads.svg +0 -0
  75. {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/shantytown.png +0 -0
  76. {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/shantytown.svg +0 -0
  77. {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/template-atoll.png +0 -0
  78. {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/template-atoll.svg +0 -0
  79. {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/template-isthmus.png +0 -0
  80. {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/template-isthmus.svg +0 -0
  81. {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/theme-blueprint.png +0 -0
  82. {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/theme-blueprint.svg +0 -0
  83. {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/theme-citadel-neon.png +0 -0
  84. {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/theme-citadel-neon.svg +0 -0
  85. {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/theme-dune.png +0 -0
  86. {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/theme-dune.svg +0 -0
  87. {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/theme-dungeon-blueprint.png +0 -0
  88. {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/theme-dungeon-blueprint.svg +0 -0
  89. {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/theme-neon.png +0 -0
  90. {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/theme-neon.svg +0 -0
  91. {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/theme-parchment.png +0 -0
  92. {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/theme-parchment.svg +0 -0
  93. {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/town.png +0 -0
  94. {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/town.svg +0 -0
  95. {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/tropical.png +0 -0
  96. {mapwright-0.21.0 → mapwright-0.22.0}/docs/gallery/tropical.svg +0 -0
  97. {mapwright-0.21.0 → mapwright-0.22.0}/examples/benchmark.py +0 -0
  98. {mapwright-0.21.0 → mapwright-0.22.0}/src/mapwright/_geometry.py +0 -0
  99. {mapwright-0.21.0 → mapwright-0.22.0}/src/mapwright/_graph.py +0 -0
  100. {mapwright-0.21.0 → mapwright-0.22.0}/src/mapwright/_serde.py +0 -0
  101. {mapwright-0.21.0 → mapwright-0.22.0}/src/mapwright/affordances.py +0 -0
  102. {mapwright-0.21.0 → mapwright-0.22.0}/src/mapwright/atlas_renderer.py +0 -0
  103. {mapwright-0.21.0 → mapwright-0.22.0}/src/mapwright/config.py +0 -0
  104. {mapwright-0.21.0 → mapwright-0.22.0}/src/mapwright/dungeon.py +0 -0
  105. {mapwright-0.21.0 → mapwright-0.22.0}/src/mapwright/dungeon_renderer.py +0 -0
  106. {mapwright-0.21.0 → mapwright-0.22.0}/src/mapwright/names.py +0 -0
  107. {mapwright-0.21.0 → mapwright-0.22.0}/src/mapwright/regions.py +0 -0
  108. {mapwright-0.21.0 → mapwright-0.22.0}/src/mapwright/rng.py +0 -0
  109. {mapwright-0.21.0 → mapwright-0.22.0}/src/mapwright/roads.py +0 -0
  110. {mapwright-0.21.0 → mapwright-0.22.0}/src/mapwright/svg_renderer.py +0 -0
  111. {mapwright-0.21.0 → mapwright-0.22.0}/src/mapwright/terrain.py +0 -0
  112. {mapwright-0.21.0 → mapwright-0.22.0}/src/mapwright/themes.py +0 -0
  113. {mapwright-0.21.0 → mapwright-0.22.0}/tests/test_affordances.py +0 -0
  114. {mapwright-0.21.0 → mapwright-0.22.0}/tests/test_atlas_renderer.py +0 -0
  115. {mapwright-0.21.0 → mapwright-0.22.0}/tests/test_config.py +0 -0
  116. {mapwright-0.21.0 → mapwright-0.22.0}/tests/test_dungeon.py +0 -0
  117. {mapwright-0.21.0 → mapwright-0.22.0}/tests/test_dungeon_renderer.py +0 -0
  118. {mapwright-0.21.0 → mapwright-0.22.0}/tests/test_geometry.py +0 -0
  119. {mapwright-0.21.0 → mapwright-0.22.0}/tests/test_graph.py +0 -0
  120. {mapwright-0.21.0 → mapwright-0.22.0}/tests/test_names.py +0 -0
  121. {mapwright-0.21.0 → mapwright-0.22.0}/tests/test_properties.py +0 -0
  122. {mapwright-0.21.0 → mapwright-0.22.0}/tests/test_regions.py +0 -0
  123. {mapwright-0.21.0 → mapwright-0.22.0}/tests/test_rng.py +0 -0
  124. {mapwright-0.21.0 → mapwright-0.22.0}/tests/test_roads.py +0 -0
  125. {mapwright-0.21.0 → mapwright-0.22.0}/tests/test_serialize.py +0 -0
  126. {mapwright-0.21.0 → mapwright-0.22.0}/tests/test_svg_renderer.py +0 -0
  127. {mapwright-0.21.0 → mapwright-0.22.0}/tests/test_terrain.py +0 -0
  128. {mapwright-0.21.0 → mapwright-0.22.0}/tests/test_themes.py +0 -0
@@ -8,6 +8,29 @@ All notable changes to mapwright are documented here. The format follows
8
8
  `tests/test_api_contract.py`). While the version is `0.x`, minor versions may
9
9
  make breaking changes; these will always be noted here.
10
10
 
11
+ ## [0.22.0] — 2026-06-02
12
+
13
+ ### Added
14
+ - **Settlement `purpose` + landmarks — what a town exists for.** A new
15
+ `SettlementConfig.purpose` field (`"general"` default, or `"trade"`,
16
+ `"fortress"`, `"religious"`, `"harbor"`, `"extraction"`, `"transit"`). Anything
17
+ but `"general"`:
18
+ - promotes the central ward to a purpose-specific **landmark** kind
19
+ (fortress→`citadel`, religious→`temple`, trade→`market`, harbor→`docks`,
20
+ extraction→`mine`, transit→`plaza`), recorded on the new
21
+ `Settlement.landmark` field (a new public `Landmark` type);
22
+ - focuses the **main roads** on that landmark (they radiate from it to the
23
+ gates);
24
+ - **biases the ward-kind mix** toward the purpose (e.g. fortress → more
25
+ garrison wards).
26
+ - The renderer draws a star glyph over the landmark ward.
27
+ - New presets **`fortress_town`**, **`pilgrimage_site`**, **`mining_camp`**;
28
+ gallery `fortress-town` showcase.
29
+ - `"general"` output is **byte-identical** to before (no landmark, unchanged
30
+ ward bag and roads). New `Landmark` public type; `Settlement` serialisation
31
+ bumps to `mapwright/settlement@5` (back-compatible — older payloads load with
32
+ no landmark and `purpose="general"`).
33
+
11
34
  ## [0.21.0] — 2026-06-02
12
35
 
13
36
  ### Added
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mapwright
3
- Version: 0.21.0
3
+ Version: 0.22.0
4
4
  Summary: Domain-neutral procedural fantasy map & world generation: Voronoi terrain, hydraulic erosion, biomes, rivers, Markov place-names, and shaded-relief SVG.
5
5
  Project-URL: Homepage, https://github.com/sligara7/mapwright
6
6
  Project-URL: Repository, https://github.com/sligara7/mapwright
@@ -100,6 +100,10 @@ produced by [`examples/gallery.py`](examples/gallery.py):
100
100
  <td align="center"><img width="240" src="https://raw.githubusercontent.com/sligara7/mapwright/main/docs/gallery/grid-city.png" alt="planned town with a geometric street grid"><br><sub><code>layout="grid"</code> (grid_city)</sub></td>
101
101
  </tr>
102
102
  <tr>
103
+ <td align="center"><img width="240" src="https://raw.githubusercontent.com/sligara7/mapwright/main/docs/gallery/fortress-town.png" alt="walled fortress town with a central citadel landmark"><br><sub><code>purpose="fortress"</code> (fortress_town)</sub></td>
104
+ <td align="center" colspan="2"><sub>A <code>purpose</code> seeds a central <b>landmark</b> (★) the main roads focus on — citadel, temple, mine, … — and biases the ward mix toward what the town is for.</sub></td>
105
+ </tr>
106
+ <tr>
103
107
  <td align="center"><img width="240" src="https://raw.githubusercontent.com/sligara7/mapwright/main/docs/gallery/roads.png" alt="settlements linked by terrain-routed roads"><br><sub><code>RegionalRoadGenerator</code></sub></td>
104
108
  <td align="center"><img width="240" src="https://raw.githubusercontent.com/sligara7/mapwright/main/docs/gallery/regions.png" alt="land partitioned into named territories"><br><sub><code>RegionGenerator</code></sub></td>
105
109
  <td align="center"><img width="240" src="https://raw.githubusercontent.com/sligara7/mapwright/main/docs/gallery/template-isthmus.png" alt="isthmus heightmap template"><br><sub><code>template="isthmus"</code></sub></td>
@@ -226,11 +230,14 @@ open("town.svg", "w").write(SettlementSVGRenderer().render(town))
226
230
  ```
227
231
 
228
232
  Settlement presets: `hamlet`, `village`, `town`, `city`, `port`, `citadel`,
229
- `shantytown`, `metropolis`, `grid_city`. The `wealth` (poor ⇄ rich) and `era`
230
- (ancientmodern) knobs drive the shanty↔skyscraper axis plot size, ward-kind
231
- mix, and block regularity. The `layout` knob picks the street pattern: `"organic"`
232
- (winding ward-to-ward roads, the default) or `"grid"` (a geometric street grid
233
- aligned to the town's long axis, with grid-aligned lots).
233
+ `shantytown`, `metropolis`, `grid_city`, `fortress_town`, `pilgrimage_site`,
234
+ `mining_camp`. The `wealth` (poorrich) and `era` (ancient modern) knobs drive
235
+ the shanty↔skyscraper axis — plot size, ward-kind mix, and block regularity. The
236
+ `layout` knob picks the street pattern: `"organic"` (winding ward-to-ward roads,
237
+ the default) or `"grid"` (a geometric street grid aligned to the town's long axis,
238
+ with grid-aligned lots). The `purpose` knob (`"fortress"`, `"religious"`,
239
+ `"extraction"`, …) gives the town a central **landmark** the main roads focus on
240
+ and biases its ward mix toward what it's for.
234
241
 
235
242
  ## What's inside
236
243
 
@@ -248,7 +255,7 @@ aligned to the town's long axis, with grid-aligned lots).
248
255
  | `RegionGenerator` | Partitions land into named factions/territories: spread capitals seed a flood fill over the land graph (sea divides them); each `Region` is Markov-named. |
249
256
  | `DungeonGenerator` | BSP-partitioned rooms + minimum-spanning-tree corridors → rooms, corridor cells, and a walkable grid (with `Dungeon.ascii()`). |
250
257
  | `DungeonSVGRenderer` | Renders a `Dungeon` to SVG: walls, carved floor, room outlines, optional tile grid and per-room labels. Takes a `theme=`. |
251
- | `SettlementGenerator` | Self-contained town layout: an organic footprint divided into named Voronoi **wards** (market, docks, …), each subdivided into building **lots**, a **street** network (`layout="organic"` → MST over ward adjacency + gate-to-market roads; `layout="grid"` → a geometric street grid + grid-aligned lots), an optional defensive **wall** (towers + gate gaps, opened at the harbour when coastal), and optional coastline. |
258
+ | `SettlementGenerator` | Self-contained town layout: an organic footprint divided into named Voronoi **wards** (market, docks, …), each subdivided into building **lots**, a **street** network (`layout="organic"` → MST over ward adjacency + gate-to-hub roads; `layout="grid"` → a geometric street grid + grid-aligned lots), an optional defensive **wall** (towers + gate gaps, opened at the harbour when coastal), an optional **`purpose`** that places a central **landmark** (citadel/temple/mine/…) the main roads focus on, and optional coastline. |
252
259
  | `SettlementSVGRenderer` | Renders a `Settlement` to SVG: sea, footprint, kind-coloured wards, building lots, streets, wall with towers/gatehouses, labels. Takes a `theme=`. |
253
260
 
254
261
  Everything is neutral: `RegionalTerrainGenerator` returns a `TerrainResult` of `TerrainCell`s
@@ -73,6 +73,10 @@ produced by [`examples/gallery.py`](examples/gallery.py):
73
73
  <td align="center"><img width="240" src="https://raw.githubusercontent.com/sligara7/mapwright/main/docs/gallery/grid-city.png" alt="planned town with a geometric street grid"><br><sub><code>layout="grid"</code> (grid_city)</sub></td>
74
74
  </tr>
75
75
  <tr>
76
+ <td align="center"><img width="240" src="https://raw.githubusercontent.com/sligara7/mapwright/main/docs/gallery/fortress-town.png" alt="walled fortress town with a central citadel landmark"><br><sub><code>purpose="fortress"</code> (fortress_town)</sub></td>
77
+ <td align="center" colspan="2"><sub>A <code>purpose</code> seeds a central <b>landmark</b> (★) the main roads focus on — citadel, temple, mine, … — and biases the ward mix toward what the town is for.</sub></td>
78
+ </tr>
79
+ <tr>
76
80
  <td align="center"><img width="240" src="https://raw.githubusercontent.com/sligara7/mapwright/main/docs/gallery/roads.png" alt="settlements linked by terrain-routed roads"><br><sub><code>RegionalRoadGenerator</code></sub></td>
77
81
  <td align="center"><img width="240" src="https://raw.githubusercontent.com/sligara7/mapwright/main/docs/gallery/regions.png" alt="land partitioned into named territories"><br><sub><code>RegionGenerator</code></sub></td>
78
82
  <td align="center"><img width="240" src="https://raw.githubusercontent.com/sligara7/mapwright/main/docs/gallery/template-isthmus.png" alt="isthmus heightmap template"><br><sub><code>template="isthmus"</code></sub></td>
@@ -199,11 +203,14 @@ open("town.svg", "w").write(SettlementSVGRenderer().render(town))
199
203
  ```
200
204
 
201
205
  Settlement presets: `hamlet`, `village`, `town`, `city`, `port`, `citadel`,
202
- `shantytown`, `metropolis`, `grid_city`. The `wealth` (poor ⇄ rich) and `era`
203
- (ancientmodern) knobs drive the shanty↔skyscraper axis plot size, ward-kind
204
- mix, and block regularity. The `layout` knob picks the street pattern: `"organic"`
205
- (winding ward-to-ward roads, the default) or `"grid"` (a geometric street grid
206
- aligned to the town's long axis, with grid-aligned lots).
206
+ `shantytown`, `metropolis`, `grid_city`, `fortress_town`, `pilgrimage_site`,
207
+ `mining_camp`. The `wealth` (poorrich) and `era` (ancient modern) knobs drive
208
+ the shanty↔skyscraper axis — plot size, ward-kind mix, and block regularity. The
209
+ `layout` knob picks the street pattern: `"organic"` (winding ward-to-ward roads,
210
+ the default) or `"grid"` (a geometric street grid aligned to the town's long axis,
211
+ with grid-aligned lots). The `purpose` knob (`"fortress"`, `"religious"`,
212
+ `"extraction"`, …) gives the town a central **landmark** the main roads focus on
213
+ and biases its ward mix toward what it's for.
207
214
 
208
215
  ## What's inside
209
216
 
@@ -221,7 +228,7 @@ aligned to the town's long axis, with grid-aligned lots).
221
228
  | `RegionGenerator` | Partitions land into named factions/territories: spread capitals seed a flood fill over the land graph (sea divides them); each `Region` is Markov-named. |
222
229
  | `DungeonGenerator` | BSP-partitioned rooms + minimum-spanning-tree corridors → rooms, corridor cells, and a walkable grid (with `Dungeon.ascii()`). |
223
230
  | `DungeonSVGRenderer` | Renders a `Dungeon` to SVG: walls, carved floor, room outlines, optional tile grid and per-room labels. Takes a `theme=`. |
224
- | `SettlementGenerator` | Self-contained town layout: an organic footprint divided into named Voronoi **wards** (market, docks, …), each subdivided into building **lots**, a **street** network (`layout="organic"` → MST over ward adjacency + gate-to-market roads; `layout="grid"` → a geometric street grid + grid-aligned lots), an optional defensive **wall** (towers + gate gaps, opened at the harbour when coastal), and optional coastline. |
231
+ | `SettlementGenerator` | Self-contained town layout: an organic footprint divided into named Voronoi **wards** (market, docks, …), each subdivided into building **lots**, a **street** network (`layout="organic"` → MST over ward adjacency + gate-to-hub roads; `layout="grid"` → a geometric street grid + grid-aligned lots), an optional defensive **wall** (towers + gate gaps, opened at the harbour when coastal), an optional **`purpose`** that places a central **landmark** (citadel/temple/mine/…) the main roads focus on, and optional coastline. |
225
232
  | `SettlementSVGRenderer` | Renders a `Settlement` to SVG: sea, footprint, kind-coloured wards, building lots, streets, wall with towers/gatehouses, labels. Takes a `theme=`. |
226
233
 
227
234
  Everything is neutral: `RegionalTerrainGenerator` returns a `TerrainResult` of `TerrainCell`s
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="630" height="630" viewBox="0 0 630 630"><rect width="630" height="630" fill="#c9d2bb"/><polygon points="112.4,390.5 112.4,361.1 117.4,330.8 127.1,300.4 141.2,270.5 159.2,241.7 180.9,214.6 205.7,189.8 233.2,167.8 262.9,149.2 294.0,134.5 326.0,124.2 357.9,118.7 388.8,118.3 417.8,123.1 444.0,133.0 466.6,147.7 484.9,166.7 498.5,189.2 507.1,214.6 510.8,242.1 509.6,270.7 503.8,299.9 493.9,328.9 480.0,357.3 462.7,384.5 442.2,410.2 418.8,433.9 392.9,455.1 364.9,473.5 335.1,488.5 304.4,499.5 273.2,506.1 242.6,507.9 213.4,504.7 186.5,496.3 162.7,483.1 143.0,465.2 127.8,443.3 117.5,418.2" fill="#e6dcc0"/><g stroke="#4a4230" stroke-width="1" stroke-linejoin="round"><polygon points="133.8,286.2 141.2,270.5 159.2,241.7 180.9,214.6 205.7,189.8 212.6,184.3 240.8,218.7 215.1,296.2 163.9,301.6" fill="#b3a684"/><polygon points="360.3,475.8 335.1,488.5 304.4,499.5 294.4,501.6 285.4,464.7 307.5,438.2 343.2,445.0" fill="#b3a684"/><polygon points="492.7,179.7 498.5,189.2 500.5,195.2 438.6,261.5 413.2,261.6 395.8,245.4 388.9,204.9" fill="#b3a684"/><polygon points="215.1,296.2 240.8,218.7 285.3,232.4 273.6,299.8 230.6,307.4" fill="#b9a98c"/><polygon points="480.9,355.5 480.0,357.3 462.7,384.5 442.2,410.2 420.8,431.8 390.6,401.4 421.6,345.9" fill="#c2b48f"/><polygon points="420.8,431.8 418.8,433.9 392.9,455.1 364.9,473.5 360.3,475.8 343.2,445.0 368.3,400.1 390.6,401.4" fill="#c2b48f"/><polygon points="386.9,202.0 388.9,204.9 395.8,245.4 337.2,270.5 293.7,231.0 334.1,182.5" fill="#c2b48f"/><polygon points="230.1,481.5 189.2,452.7 189.0,451.2 192.4,439.8 239.4,422.6 254.0,455.7" fill="#d8cdb0"/><polygon points="332.5,298.8 366.1,329.3 343.2,374.1 322.1,374.4 296.7,344.9 298.9,316.8" fill="#cdbf9e"/><polygon points="229.5,506.5 213.4,504.7 186.5,496.3 173.4,489.0 189.2,452.7 230.1,481.5" fill="#c2b48f"/><polygon points="343.2,445.0 307.5,438.2 296.9,407.4 322.1,374.4 343.2,374.1 368.3,400.1" fill="#c2b48f"/><polygon points="337.2,270.5 395.8,245.4 413.2,261.6 400.2,323.2 366.1,329.3 332.5,298.8" fill="#c2b48f"/><polygon points="192.4,439.8 189.0,451.2 131.4,448.6 127.8,443.3 117.5,418.2 114.5,402.1 167.3,392.4" fill="#b9a98c"/><polygon points="296.9,407.4 307.5,438.2 285.4,464.7 254.0,455.7 239.4,422.6 247.9,398.8" fill="#b3a684"/><polygon points="415.9,122.8 417.8,123.1 444.0,133.0 466.6,147.7 484.9,166.7 492.7,179.7 388.9,204.9 386.9,202.0" fill="#b3a684"/><polygon points="294.4,501.6 273.2,506.1 242.6,507.9 229.5,506.5 230.1,481.5 254.0,455.7 285.4,464.7" fill="#dfd6c0"/><polygon points="499.4,312.9 493.9,328.9 480.9,355.5 421.6,345.9 400.2,323.2 413.2,261.6 438.6,261.5" fill="#b9a98c"/><polygon points="421.6,345.9 390.6,401.4 368.3,400.1 343.2,374.1 366.1,329.3 400.2,323.2" fill="#c2b48f"/><polygon points="212.6,184.3 233.2,167.8 262.9,149.2 294.0,134.5 315.2,127.7 334.1,182.5 293.7,231.0 285.3,232.4 240.8,218.7" fill="#cdbf9e"/><polygon points="296.7,344.9 322.1,374.4 296.9,407.4 247.9,398.8 237.5,374.7 241.1,366.7" fill="#cdbf9e"/><polygon points="163.9,301.6 215.1,296.2 230.6,307.4 241.1,366.7 237.5,374.7 181.2,375.8" fill="#b3a684"/><polygon points="315.2,127.7 326.0,124.2 357.9,118.7 388.8,118.3 415.9,122.8 386.9,202.0 334.1,182.5" fill="#b9a98c"/><polygon points="230.6,307.4 273.6,299.8 298.9,316.8 296.7,344.9 241.1,366.7" fill="#cdbf9e"/><polygon points="112.4,390.5 112.4,361.1 117.4,330.8 127.1,300.4 133.8,286.2 163.9,301.6 181.2,375.8 167.3,392.4 114.5,402.1" fill="#b9a98c"/><polygon points="500.5,195.2 507.1,214.6 510.8,242.1 509.6,270.7 503.8,299.9 499.4,312.9 438.6,261.5" fill="#d8cdb0"/><polygon points="273.6,299.8 285.3,232.4 293.7,231.0 337.2,270.5 332.5,298.8 298.9,316.8" fill="#dfd6c0"/><polygon points="237.5,374.7 247.9,398.8 239.4,422.6 192.4,439.8 167.3,392.4 181.2,375.8" fill="#cdbf9e"/><polygon points="189.0,451.2 189.2,452.7 173.4,489.0 162.7,483.1 143.0,465.2 131.4,448.6" fill="#d8cdb0"/></g><g fill="#7d6c52" stroke="#4a3e2e" stroke-width="0.5" stroke-linejoin="round"><polygon points="238.4,219.9 236.0,227.2 228.0,224.6 230.4,217.3"/><polygon points="234.9,230.7 232.6,237.6 224.5,234.9 226.8,228.1"/><polygon points="210.1,214.4 211.2,211.0 226.8,216.2 225.7,219.5"/><polygon points="213.6,230.9 205.7,228.3 209.0,218.4 216.9,221.0"/><polygon points="221.1,233.9 217.1,232.6 220.8,221.7 224.7,223.1"/><polygon points="231.8,240.9 229.1,249.0 224.7,247.5 227.4,239.4"/><polygon points="221.7,246.3 215.7,244.4 218.3,236.6 224.3,238.6"/><polygon points="212.2,242.8 202.0,239.4 204.3,232.4 214.5,235.8"/><polygon points="223.5,207.7 220.2,210.4 211.8,207.6 212.9,204.3"/><polygon points="217.2,193.0 226.7,204.5 226.1,205.0 214.5,201.2"/><polygon points="229.2,207.1 236.0,215.5 223.9,211.4"/><polygon points="205.5,192.2 206.7,191.0 212.4,186.4 214.7,189.3 213.0,194.7"/><polygon points="197.2,201.3 203.2,195.3 211.2,197.9 208.9,205.2"/><polygon points="201.8,206.8 208.1,208.9 205.8,215.9 199.5,213.8"/><polygon points="188.0,210.0 193.7,204.2 198.6,205.8 196.3,212.7"/><polygon points="199.2,234.7 198.0,238.1 181.9,232.8 183.0,229.3"/><polygon points="202.7,224.8 200.6,231.0 191.9,228.2 193.9,221.9"/><polygon points="195.5,215.7 205.1,218.9 204.1,221.9 194.5,218.7"/><polygon points="188.0,213.7 192.3,215.1 188.4,227.0 184.1,225.6"/><polygon points="171.7,229.4 182.4,216.0 183.4,215.1 178.0,231.4"/><polygon points="190.6,239.6 196.2,241.5 193.3,250.0 187.8,248.2"/><polygon points="199.3,242.3 203.3,243.6 200.4,252.6 196.4,251.2"/><polygon points="199.4,255.5 198.5,258.0 185.8,253.7 186.6,251.3"/><polygon points="197.1,261.2 195.5,265.8 183.5,261.8 185.0,257.2"/><polygon points="184.2,249.0 183.2,251.8 172.6,248.3 173.5,245.4"/><polygon points="181.8,254.8 179.8,260.7 170.0,257.4 171.9,251.6"/><polygon points="177.4,235.6 187.0,238.7 184.7,245.5 175.1,242.3"/><polygon points="161.6,242.0 168.9,232.8 173.5,234.3 170.0,244.7"/><polygon points="155.1,252.2 159.3,245.6 168.6,248.7 166.3,255.9"/><polygon points="174.7,270.6 172.1,278.4 167.3,276.8 169.9,268.9"/><polygon points="164.2,275.5 157.7,273.4 160.1,265.9 166.7,268.1"/><polygon points="162.6,258.9 176.7,263.6 175.5,267.2 161.4,262.5"/><polygon points="144.3,268.9 148.1,262.8 155.8,267.7 154.3,272.2"/><polygon points="149.8,259.8 152.6,255.4 159.3,257.6 157.1,264.4"/><polygon points="163.2,278.8 171.0,281.4 169.3,286.6 160.7,283.8"/><polygon points="167.6,290.1 164.5,299.4 164.3,299.4 155.6,294.9 159.4,287.4"/><polygon points="146.7,273.5 152.8,275.5 149.8,284.5 143.7,282.5"/><polygon points="137.6,281.0 142.1,271.4 143.9,272.0 140.6,282.0"/><polygon points="135.9,285.4 136.7,283.7 149.0,287.8 147.8,291.5"/><polygon points="156.3,276.6 159.9,277.8 151.9,293.4 150.9,292.9"/><polygon points="185.5,295.4 184.9,297.2 169.2,298.9 171.9,290.9"/><polygon points="180.4,265.0 194.1,269.6 192.6,274.3 178.8,269.7"/><polygon points="181.2,289.6 173.0,286.9 175.6,279.1 183.8,281.8"/><polygon points="176.1,275.7 177.1,272.8 186.4,275.9 185.4,278.8"/><polygon points="191.8,278.0 187.2,291.9 184.6,291.0 189.2,277.1"/><polygon points="205.6,295.2 189.4,296.9 191.5,290.5"/><polygon points="196.3,274.5 197.4,271.0 207.1,274.2 205.9,277.6"/><polygon points="201.3,289.5 192.9,286.7 195.7,278.1 204.2,280.9"/><polygon points="216.4,285.9 213.8,293.7 205.2,290.9 207.8,283.1"/><polygon points="219.3,278.3 217.9,282.6 208.6,279.5 210.0,275.2"/><polygon points="206.2,244.6 212.7,246.8 210.7,252.9 204.2,250.7"/><polygon points="215.9,248.3 227.6,252.2 225.9,257.4 214.2,253.6"/><polygon points="222.8,267.0 220.2,274.9 212.7,272.5 215.4,264.5"/><polygon points="225.4,260.8 224.3,264.1 215.9,261.3 217.0,258.0"/><polygon points="209.1,271.1 199.0,267.8 201.2,261.3 211.2,264.7"/><polygon points="202.0,257.7 203.3,253.8 214.0,257.4 212.7,261.3"/><polygon points="300.3,498.8 295.6,499.8 293.5,491.2 298.2,490.0"/><polygon points="312.2,494.6 303.8,497.6 303.7,497.6 301.7,489.6 310.5,487.4"/><polygon points="292.8,488.0 290.6,478.9 295.5,477.7 297.7,486.8"/><polygon points="299.2,477.2 307.5,475.2 309.5,483.6 301.2,485.6"/><polygon points="327.6,489.1 321.9,491.1 318.2,480.7 325.1,479.0"/><polygon points="318.8,492.8 315.6,493.9 312.6,481.6 314.6,481.1"/><polygon points="312.2,478.3 311.2,474.1 323.4,471.1 324.4,475.3"/><polygon points="309.0,459.4 311.1,458.9 313.8,470.2 311.8,470.7"/><polygon points="314.3,458.7 320.0,457.4 322.5,467.5 316.8,468.9"/><polygon points="295.8,463.1 306.2,460.6 307.3,465.4 296.9,467.9"/><polygon points="308.4,468.4 309.1,471.2 298.1,473.9 297.4,471.1"/><polygon points="289.9,475.6 287.4,465.2 292.4,464.0 294.9,474.3"/><polygon points="345.6,481.5 334.5,487.1 331.0,488.3 330.3,485.2"/><polygon points="335.2,470.2 342.4,468.4 344.7,478.0 337.6,479.8"/><polygon points="329.7,482.1 327.2,471.8 331.6,470.7 334.1,481.0"/><polygon points="357.4,474.9 349.2,479.0 346.5,467.6 352.5,466.1"/><polygon points="338.5,453.2 344.4,451.8 350.2,462.1 341.2,464.3"/><polygon points="326.6,468.2 325.1,462.2 335.7,459.7 337.2,465.6"/><polygon points="323.9,459.0 323.2,456.2 334.7,453.4 335.3,456.2"/><polygon points="321.2,442.3 326.5,443.3 324.6,452.9 323.9,453.0"/><polygon points="329.6,444.4 342.0,446.7 342.7,448.0 328.2,451.5"/><polygon points="318.3,445.5 320.2,453.1 309.2,455.8 307.3,448.2"/><polygon points="305.3,443.1 308.1,439.8 318.2,441.7 318.2,441.8 305.7,444.8"/><polygon points="299.5,450.3 302.9,446.2 305.6,457.3 301.4,458.3"/><polygon points="290.1,461.3 297.0,452.9 298.6,459.2"/><polygon points="415.3,200.4 419.6,199.4 422.8,212.6 418.5,213.6"/><polygon points="423.0,198.3 433.3,195.8 434.4,200.1 424.0,202.7"/><polygon points="435.0,203.6 436.4,209.3 426.4,211.7 425.0,206.0"/><polygon points="437.3,212.9 439.5,222.0 433.4,223.5 431.1,214.4"/><polygon points="429.6,224.2 421.7,226.1 419.6,217.4 427.5,215.4"/><polygon points="416.4,221.1 417.8,227.1 407.0,229.7 405.6,223.7"/><polygon points="403.3,215.1 414.6,213.2 415.6,217.4 404.5,220.1"/><polygon points="395.3,233.0 394.4,227.3 402.4,226.0 403.7,231.0"/><polygon points="393.9,224.0 392.7,216.9 399.8,215.7 401.5,222.7"/><polygon points="402.5,203.7 411.5,201.5 413.5,209.5 403.8,211.2"/><polygon points="392.2,213.4 391.0,206.3 398.7,204.4 400.0,212.0"/><polygon points="400.5,247.0 397.6,244.4 396.3,236.4 402.2,235.0 404.8,246.0"/><polygon points="405.6,233.8 409.0,233.0 411.9,244.6 408.4,245.5"/><polygon points="415.2,259.7 414.0,259.7 403.8,250.2 412.4,248.1"/><polygon points="414.0,239.3 412.3,232.2 419.3,230.5 421.0,237.6"/><polygon points="416.3,248.9 414.7,242.6 421.9,240.8 423.4,247.2"/><polygon points="426.3,259.7 419.1,259.8 417.3,252.2 424.1,250.6"/><polygon points="436.7,259.8 430.0,259.8 428.3,252.9 434.7,251.4"/><polygon points="446.7,250.5 439.7,258.0 437.9,250.5 446.2,248.5"/><polygon points="427.8,249.0 426.8,245.0 443.8,240.9 444.8,244.9"/><polygon points="425.7,240.9 423.0,230.0 429.3,228.5 431.9,239.4"/><polygon points="433.3,227.6 440.2,225.9 442.8,236.7 435.9,238.4"/><polygon points="458.7,205.5 457.5,204.5 462.8,198.8 467.7,203.3"/><polygon points="465.5,196.7 472.6,189.2 475.4,200.8 471.1,201.9"/><polygon points="479.4,215.5 474.9,220.5 471.4,217.2 478.1,210.0"/><polygon points="469.1,214.9 466.2,212.1 472.5,205.3 476.5,204.3 477.1,206.4"/><polygon points="463.7,210.7 460.4,207.6 468.5,205.6"/><polygon points="449.8,197.8 444.1,192.5 448.3,191.5"/><polygon points="455.5,201.7 453.3,199.7 451.4,191.8 460.0,189.7 461.4,195.3"/><polygon points="463.0,188.2 471.0,186.2 464.3,193.4"/><polygon points="490.0,204.3 481.9,212.9 480.4,206.6"/><polygon points="497.8,194.2 498.0,194.7 493.3,199.7 480.2,202.9 479.2,198.8"/><polygon points="491.7,182.0 496.6,190.1 496.6,190.2 487.3,192.4 485.2,183.6"/><polygon points="483.7,193.6 477.8,195.0 475.6,185.7 481.4,184.3"/><polygon points="465.8,229.3 456.1,239.6 449.4,233.3"/><polygon points="454.0,242.7 449.3,247.7 446.4,235.6"/><polygon points="457.4,223.1 454.4,210.7 458.2,209.7 464.4,215.6"/><polygon points="472.4,222.7 471.3,223.9 459.0,226.9 467.4,218.0"/><polygon points="454.3,227.7 445.5,229.8 443.4,221.4 452.3,219.3"/><polygon points="442.1,217.9 441.0,213.5 450.8,211.1 451.9,215.6"/><polygon points="440.3,208.5 437.2,195.4 441.5,194.3 447.9,200.3"/><polygon points="451.0,202.3 455.7,206.7 444.3,209.5"/><polygon points="219.2,294.9 224.2,279.8 235.5,283.6 229.3,302.2"/><polygon points="241.7,285.0 249.4,287.6 244.6,302.0 235.5,303.6"/><polygon points="251.9,260.9 257.7,262.8 251.3,282.2 245.5,280.3"/><polygon points="226.7,273.1 232.6,255.5 245.4,259.7 239.6,277.3"/><polygon points="272.4,285.3 270.4,296.6 252.3,299.8 258.6,280.7"/><polygon points="263.0,264.6 276.1,268.9 274.3,279.0 259.9,274.2"/><polygon points="274.6,233.1 281.0,235.1 276.3,262.0 266.2,258.6"/><polygon points="240.7,230.2 243.1,223.1 267.7,230.7 265.2,238.3"/><polygon points="235.3,248.2 238.8,237.5 262.3,245.3 258.8,256.0"/><polygon points="397.0,394.5 404.3,381.4 409.4,384.3 402.1,397.4"/><polygon points="413.5,386.8 420.0,390.4 412.9,403.1 406.4,399.5"/><polygon points="406.8,414.2 393.6,401.0 394.8,398.7 410.6,407.5"/><polygon points="414.8,422.5 410.2,417.9 416.8,406.1 422.3,409.2"/><polygon points="433.4,415.6 420.8,428.4 418.4,425.9 426.4,411.6"/><polygon points="428.6,407.2 419.3,402.0 424.4,392.9 433.7,398.1"/><polygon points="444.7,403.7 440.6,408.8 437.1,412.4 432.4,409.8 437.9,399.9"/><polygon points="415.2,363.7 423.2,349.4 431.5,350.7 429.0,365.9"/><polygon points="425.9,387.8 415.9,382.2 422.3,370.7 428.5,371.7"/><polygon points="411.5,380.5 406.2,377.5 411.4,368.3 417.7,369.3"/><polygon points="439.1,395.2 430.9,390.6 432.9,378.1 441.7,379.5"/><polygon points="454.0,390.9 447.1,399.5 444.2,397.9 447.0,380.5 455.5,381.8"/><polygon points="458.1,365.7 456.4,376.4 443.9,374.4 445.7,363.7"/><polygon points="447.0,352.3 460.4,354.5 459.4,360.8 446.0,358.6"/><polygon points="433.7,372.8 437.2,351.2 442.1,351.9 438.6,373.6"/><polygon points="475.9,357.9 461.7,380.2 465.6,356.2"/><polygon points="347.9,442.8 352.8,433.9 366.4,441.4 361.4,450.3"/><polygon points="355.4,462.8 347.4,448.3 359.7,455.2"/><polygon points="367.8,459.5 364.7,454.8 371.2,443.2 375.5,445.6"/><polygon points="389.5,454.1 372.3,465.3 371.1,463.5 379.5,448.5"/><polygon points="368.6,468.5 363.8,471.7 361.2,473.0 357.9,467.0 362.4,459.0"/><polygon points="403.2,444.2 394.4,451.4 390.7,449.3 395.9,440.1"/><polygon points="416.6,431.8 406.3,440.3 399.1,436.3 407.0,422.2"/><polygon points="387.0,446.6 381.7,443.6 390.5,427.7 395.9,430.7"/><polygon points="377.1,440.8 370.9,437.4 379.6,421.8 385.8,425.2"/><polygon points="399.3,413.1 403.8,417.6 398.7,426.9 393.3,423.9"/><polygon points="382.1,417.0 389.4,403.9 389.4,403.9 395.7,410.2 389.5,421.2"/><polygon points="368.2,404.7 369.5,402.3 384.3,403.2 379.9,411.2"/><polygon points="366.1,434.8 355.4,428.8 360.5,419.5 371.3,425.5"/><polygon points="362.6,414.9 366.1,408.7 377.6,415.1 374.1,421.3"/><polygon points="369.4,253.4 360.0,257.5 353.8,243.2 363.3,239.1"/><polygon points="381.9,248.4 374.8,251.5 368.4,236.6 375.6,233.5"/><polygon points="390.0,227.4 392.7,243.7 386.9,246.2 380.6,231.4"/><polygon points="384.4,223.7 375.6,227.5 369.6,213.4 378.4,209.7"/><polygon points="386.7,205.0 387.1,205.7 390.0,222.4 389.2,222.7 382.4,206.9"/><polygon points="381.5,202.6 367.1,208.8 363.8,201.1 370.1,198.4"/><polygon points="370.5,229.8 351.3,238.0 349.0,232.6 368.2,224.4"/><polygon points="347.1,227.1 340.5,211.8 350.2,207.7 356.7,222.9"/><polygon points="354.9,204.8 359.4,202.9 366.5,219.6 362.1,221.5"/><polygon points="338.0,206.3 331.2,190.6 335.0,186.0 345.1,189.7 339.1,205.9"/><polygon points="350.3,191.2 363.3,196.0 345.8,203.5"/><polygon points="297.3,229.4 305.7,219.3 310.8,223.6"/><polygon points="309.6,216.5 318.6,205.7 329.2,214.5 314.7,220.7"/><polygon points="321.4,201.3 327.2,194.4 334.9,212.5"/><polygon points="326.5,251.7 343.1,233.3 347.2,242.8"/><polygon points="338.1,268.0 337.6,268.2 325.7,257.4 332.4,254.5"/><polygon points="354.4,259.8 342.3,265.0 337.4,253.5 349.5,248.3"/><polygon points="319.6,251.1 311.8,243.9 320.9,233.9 325.5,244.6"/><polygon points="307.9,240.4 299.9,233.1 317.3,225.7 318.5,228.7"/><polygon points="339.9,224.7 341.1,227.5 329.3,240.5 325.2,231.0"/><polygon points="321.4,223.3 336.9,216.6 338.5,220.3 323.0,226.9"/><polygon points="225.2,472.9 193.5,450.6 195.8,443.0 212.1,437.1"/><polygon points="243.7,442.8 249.1,454.8 233.6,471.5 225.5,449.4"/><polygon points="218.9,433.7 237.6,426.8 241.5,435.5 222.1,442.6"/><polygon points="354.4,346.0 347.9,358.6 339.0,354.0 345.4,341.4"/><polygon points="334.1,351.9 327.4,348.5 334.2,335.3 340.8,338.7"/><polygon points="323.2,346.8 319.3,344.8 326.5,330.8 330.3,332.8"/><polygon points="338.9,308.7 346.9,316.0 334.8,329.4 329.6,326.7"/><polygon points="356.1,323.8 362.8,329.9 357.0,341.2 349.2,337.2"/><polygon points="351.4,318.9 352.6,320.0 344.8,335.4 339.0,332.5"/><polygon points="325.6,322.2 324.9,323.7 307.8,315.0 318.7,309.2"/><polygon points="332.2,301.6 335.1,304.3 328.5,317.2 322.8,306.6"/><polygon points="301.7,335.5 299.8,334.6 301.1,318.3 302.2,317.6 309.0,321.1"/><polygon points="322.1,328.5 315.3,341.8 306.5,337.4 313.4,324.0"/><polygon points="307.7,354.2 299.1,344.2 299.4,339.5 311.9,345.9"/><polygon points="321.9,369.3 312.0,357.8 316.4,349.2 328.9,355.6"/><polygon points="345.3,363.6 341.4,371.2 327.6,371.4 334.4,358.1"/><polygon points="181.9,491.0 176.4,488.0 182.4,474.3 188.1,476.8"/><polygon points="188.8,494.7 187.3,494.3 185.8,493.4 192.4,478.5 198.4,481.1"/><polygon points="184.6,469.8 190.3,456.6 198.8,462.6 191.6,472.8"/><polygon points="202.9,464.8 207.7,468.2 201.1,477.6 195.6,475.2"/><polygon points="215.1,502.4 213.9,502.2 193.7,496.0 197.8,490.1"/><polygon points="227.5,504.1 225.2,503.9 219.7,500.0 227.8,488.5"/><polygon points="216.3,497.4 212.9,495.0 223.7,479.8 227.0,482.1"/><polygon points="209.0,491.3 201.4,485.9 211.3,471.9 218.9,477.3"/><polygon points="358.7,410.8 351.4,423.8 341.0,418.0 348.3,404.9"/><polygon points="335.6,415.3 327.7,410.9 335.3,397.3 343.2,401.7"/><polygon points="345.0,380.3 356.2,391.9 349.4,398.5 338.3,392.3"/><polygon points="360.4,394.9 365.8,400.4 362.2,406.7 353.3,401.8"/><polygon points="321.7,379.0 323.3,376.9 340.7,376.6 335.2,386.5"/><polygon points="305.7,399.4 308.5,395.7 323.9,407.4 323.0,409.0"/><polygon points="312.6,392.0 318.8,383.9 331.8,391.1 325.7,402.1"/><polygon points="306.2,425.3 300.2,407.9 303.3,403.9 314.6,410.3"/><polygon points="319.0,438.5 308.9,436.6 307.6,432.8 309.4,429.6 320.5,435.9"/><polygon points="312.6,426.0 319.8,413.1 329.1,418.3 321.9,431.1"/><polygon points="339.1,442.4 322.8,439.3 325.4,434.7"/><polygon points="329.0,431.0 334.3,421.4 348.4,429.3 343.0,438.9"/><polygon points="363.6,270.7 367.0,278.8 353.7,284.5 350.3,276.5"/><polygon points="346.2,268.8 360.8,262.6 362.4,266.2 347.8,272.4"/><polygon points="339.8,272.5 342.1,271.4 348.6,286.6 336.5,291.8"/><polygon points="362.7,309.9 352.8,314.2 349.2,310.9 358.6,300.5"/><polygon points="345.7,306.9 336.2,298.2 353.7,290.7 355.9,295.7"/><polygon points="373.7,294.0 377.7,303.2 367.0,307.8 363.1,298.5"/><polygon points="369.4,283.1 372.1,289.5 360.9,294.3 358.1,288.0"/><polygon points="369.0,326.7 366.7,327.1 356.1,317.5 363.6,314.3"/><polygon points="379.4,308.6 385.5,322.8 373.7,324.9 368.7,313.2"/><polygon points="408.1,271.6 405.3,284.6 386.8,280.7"/><polygon points="401.0,306.8 398.0,320.9 391.4,322.1 387.3,312.6"/><polygon points="386.8,306.6 385.5,307.1 376.2,285.4 377.3,285.0 390.8,287.8"/><polygon points="404.6,290.2 402.4,300.5 393.1,304.5 396.5,288.4"/><polygon points="407.3,259.5 410.5,262.5 410.0,264.8 393.8,271.7 391.5,266.3"/><polygon points="385.7,252.6 395.3,248.5 403.0,255.7 389.5,261.5"/><polygon points="365.2,261.3 380.9,254.6 383.4,260.5 367.8,267.2"/><polygon points="385.3,265.6 388.7,273.5 373.6,280.0 370.2,272.1"/><polygon points="188.2,440.2 186.1,447.2 157.1,445.9 157.5,437.9 187.7,439.3"/><polygon points="158.0,430.0 158.6,416.8 176.0,417.6 183.2,431.1"/><polygon points="157.9,409.7 158.5,397.1 165.7,395.8 173.4,410.4"/><polygon points="120.6,411.8 119.4,405.5 151.2,399.7 150.5,413.1"/><polygon points="127.6,436.6 120.2,418.3 128.4,418.7"/><polygon points="149.1,445.2 133.8,444.5 134.9,420.8 150.2,421.5"/><polygon points="267.0,404.1 275.8,405.6 274.5,412.9 265.7,411.4"/><polygon points="254.6,401.9 263.2,403.4 261.9,410.7 253.3,409.2"/><polygon points="245.9,408.5 248.8,400.3 251.5,400.8 250.1,409.2"/><polygon points="244.2,414.6 245.3,411.6 259.9,414.1 259.4,417.3"/><polygon points="244.3,429.7 241.1,422.6 242.8,417.8 246.9,418.5 244.9,429.8"/><polygon points="250.4,419.7 258.3,421.1 256.6,431.3 248.6,429.9"/><polygon points="263.6,414.9 273.8,416.7 272.6,423.3 262.5,421.5"/><polygon points="271.9,427.2 270.7,433.9 260.6,432.2 261.8,425.4"/><polygon points="256.2,454.3 255.4,454.1 250.6,443.1 258.8,439.5"/><polygon points="248.9,439.9 245.9,433.3 258.9,435.5"/><polygon points="261.9,442.6 263.0,436.3 270.4,437.6 269.3,443.9"/><polygon points="266.7,457.3 259.9,455.3 261.5,446.2 268.4,447.4"/><polygon points="303.9,434.3 305.1,437.8 298.6,445.6 292.7,440.6 298.8,433.4"/><polygon points="289.5,438.7 284.1,434.1 286.9,430.8 294.9,432.2"/><polygon points="281.7,432.9 276.2,428.3 284.4,429.7"/><polygon points="273.2,440.6 273.3,439.7 284.9,441.8 280.7,446.9"/><polygon points="270.6,458.6 270.2,458.5 272.7,444.5 278.4,449.2"/><polygon points="273.8,436.7 274.9,430.6 284.5,438.6"/><polygon points="285.3,462.5 284.9,463.0 273.7,459.8 277.2,455.7"/><polygon points="296.3,448.8 292.4,453.5 284.8,447.2 288.7,442.5"/><polygon points="290.2,456.3 287.2,460.0 279.3,453.5 282.4,449.8"/><polygon points="295.7,408.7 297.8,415.0 290.6,417.5 292.2,408.1"/><polygon points="298.5,418.8 302.2,429.6 289.5,427.3 290.5,421.5"/><polygon points="278.4,412.3 279.5,406.2 288.8,407.8 287.8,413.9"/><polygon points="285.2,426.7 276.4,425.2 278.0,416.2 286.8,417.7"/><polygon points="487.4,174.6 489.6,178.4 475.1,181.9 474.1,177.9"/><polygon points="479.2,163.9 483.1,168.0 484.9,171.0 473.3,173.8 471.4,165.8"/><polygon points="470.6,154.1 476.9,160.7 469.9,162.4 468.0,154.7"/><polygon points="472.0,183.3 469.6,183.9 466.9,172.6 469.2,172.0"/><polygon points="466.4,184.1 460.8,185.5 458.3,175.2 463.9,173.8"/><polygon points="457.4,171.6 456.2,166.5 466.8,163.9 468.0,169.0"/><polygon points="455.2,163.0 454.1,158.3 464.9,155.7 466.0,160.4"/><polygon points="447.9,188.6 439.7,190.6 437.8,182.9 446.1,180.9"/><polygon points="457.4,186.5 451.5,187.9 449.5,179.9 455.5,178.4"/><polygon points="441.2,161.4 450.7,159.1 451.9,164.0 442.4,166.3"/><polygon points="452.5,167.6 454.3,174.9 445.3,177.1 443.5,169.8"/><polygon points="437.0,179.0 433.3,163.8 437.6,162.7 441.4,178.0"/><polygon points="455.8,143.0 465.3,149.2 467.4,151.4 458.4,153.6"/><polygon points="453.3,148.3 454.9,154.5 446.3,156.6 444.8,150.4"/><polygon points="447.4,137.6 451.2,140.0 452.4,144.8 444.0,146.8 442.1,138.9"/><polygon points="442.5,157.2 432.3,159.7 430.6,152.7 440.8,150.2"/><polygon points="429.6,148.7 428.0,142.4 438.3,139.9 439.9,146.2"/><polygon points="423.7,126.9 429.6,129.1 426.3,137.8"/><polygon points="432.5,130.6 443.2,134.6 443.4,134.8 429.6,138.1"/><polygon points="408.2,193.4 406.2,185.2 411.7,183.8"/><polygon points="406.1,181.8 403.4,170.9 414.2,174.8 412.2,180.3"/><polygon points="433.5,181.9 435.8,191.4 430.1,192.8 426.8,179.5"/><polygon points="426.3,193.9 419.2,195.6 416.6,185.2 424.9,188.2"/><polygon points="415.9,196.8 409.6,198.3 409.5,198.0 413.5,187.0"/><polygon points="415.7,181.2 417.8,175.6 422.6,177.4 424.3,184.3"/><polygon points="406.5,198.9 401.0,200.3 398.6,190.3 405.0,192.7"/><polygon points="397.4,200.9 389.7,202.7 389.0,201.7 393.8,188.8 394.6,189.1"/><polygon points="395.2,185.2 399.7,172.8 403.5,188.2"/><polygon points="419.9,154.6 405.0,158.2 408.0,150.2"/><polygon points="417.0,124.7 417.4,124.7 420.3,125.8 421.8,132.3 413.5,134.3"/><polygon points="422.4,135.7 423.5,140.3 410.3,143.6 412.3,138.2"/><polygon points="424.3,144.1 426.4,152.8 411.4,147.3"/><polygon points="428.0,159.9 432.1,176.9 423.0,173.5"/><polygon points="412.7,170.2 402.0,166.2 403.3,162.5 410.5,160.8"/><polygon points="418.9,172.1 417.1,171.5 414.4,160.1 424.2,157.7"/><polygon points="268.3,473.7 264.9,485.4 259.7,486.7 257.2,476.4"/><polygon points="253.7,460.1 254.9,458.8 270.7,463.3 269.3,468.3 256.5,471.4"/><polygon points="276.2,465.2 282.9,467.1 285.9,479.5 271.1,483.1"/><polygon points="238.6,475.3 249.6,463.5 251.7,472.1"/><polygon points="240.3,479.8 252.2,476.9 254.7,487.4 242.9,490.3"/><polygon points="231.8,493.8 232.0,482.3 233.5,480.7 235.6,480.1 238.5,492.1"/><polygon points="239.6,505.8 231.4,504.9 231.5,497.7 237.3,496.3"/><polygon points="259.4,504.0 244.2,504.9 242.1,496.3 256.6,492.8"/><polygon points="278.6,501.8 272.8,503.0 265.5,503.5 262.5,491.5 275.4,488.4"/><polygon points="291.3,499.7 284.3,501.2 280.7,486.5 287.7,484.8"/><polygon points="416.4,266.9 416.7,265.8 437.0,265.7 450.0,276.7 440.8,287.5"/><polygon points="412.3,283.5 414.1,275.2 436.2,293.9 431.4,299.6"/><polygon points="408.4,325.8 404.8,321.9 411.0,292.6 425.9,305.2"/><polygon points="422.2,340.5 414.2,332.0 430.8,312.4 439.6,319.9"/><polygon points="440.3,345.4 429.7,343.6 445.9,324.4 453.0,330.3"/><polygon points="477.8,351.4 448.6,346.6 458.4,335.0"/><polygon points="486.4,337.1 482.0,346.1 467.7,334.1 474.0,326.6"/><polygon points="495.5,314.0 490.8,327.6 489.1,331.0 478.3,321.9 489.3,308.8"/><polygon points="449.5,289.3 456.3,281.2 472.4,294.9 465.6,302.9"/><polygon points="451.4,319.1 435.9,306.0 444.9,295.3 460.4,308.4"/><polygon points="462.9,328.9 457.4,324.3 478.0,299.9 483.4,304.6"/><polygon points="418.4,346.3 409.7,362.0 404.9,359.4 414.5,342.2"/><polygon points="400.6,357.1 386.3,349.2 389.0,344.3 403.3,352.3"/><polygon points="407.9,334.3 411.6,338.2 405.7,348.7 401.2,346.2"/><polygon points="391.5,340.2 399.4,326.0 404.4,331.3 397.6,343.6"/><polygon points="382.0,346.0 365.5,336.9 368.0,332.0 379.8,329.9 382.5,345.1"/><polygon points="384.5,328.0 394.1,326.3 386.6,339.8"/><polygon points="350.0,366.0 356.5,353.2 362.4,356.2 355.8,369.2"/><polygon points="366.8,359.1 376.6,364.0 369.7,376.4 360.6,371.3"/><polygon points="359.6,348.7 362.9,342.3 382.4,353.2 379.3,358.8"/><polygon points="362.2,389.6 346.7,373.5 348.1,370.9 366.8,381.4"/><polygon points="376.6,398.8 369.1,398.3 365.1,394.2 367.0,390.7 377.8,396.7"/><polygon points="370.2,387.2 377.0,374.9 386.0,379.9 379.1,392.2"/><polygon points="396.5,385.5 389.1,398.7 381.9,398.3 390.8,382.3"/><polygon points="406.4,366.7 398.8,380.3 389.1,374.9 396.7,361.2"/><polygon points="383.9,372.8 379.2,370.2 387.5,355.3 392.2,357.9"/><polygon points="293.7,190.2 288.3,185.7 297.7,174.5 303.0,178.9"/><polygon points="300.3,170.8 306.6,163.3 312.5,168.2 306.2,175.7"/><polygon points="313.5,186.1 307.5,181.1 315.7,171.3 321.7,176.3"/><polygon points="330.4,183.4 322.1,193.5 316.9,189.2 325.3,179.2"/><polygon points="318.0,197.0 311.8,204.4 298.3,193.2 304.5,185.8"/><polygon points="319.7,148.2 323.6,159.5 313.3,163.0 309.7,160.1"/><polygon points="325.4,163.7 330.1,177.4 317.1,166.6"/><polygon points="312.1,149.5 305.1,157.8 296.9,151.0 303.9,142.6"/><polygon points="313.5,130.6 313.8,130.5 318.1,142.8 315.4,146.0 306.7,138.8"/><polygon points="285.5,141.4 295.0,136.9 304.6,133.9 293.0,147.7"/><polygon points="292.2,153.4 302.2,161.7 297.5,167.3 287.5,159.0"/><polygon points="293.6,171.0 284.5,181.8 275.6,174.4 284.7,163.5"/><polygon points="271.2,148.1 280.4,143.8 288.3,150.4 282.4,157.5"/><polygon points="270.9,154.7 279.0,161.5 271.0,171.0 262.9,164.2"/><polygon points="253.6,157.4 263.8,151.0 266.2,149.9 267.7,151.2 258.9,161.8"/><polygon points="259.2,168.2 268.0,175.6 260.2,185.0 251.4,177.6"/><polygon points="238.7,167.4 250.1,160.3 255.3,164.6 247.2,174.4"/><polygon points="250.1,183.9 256.6,189.2 247.2,200.5 238.6,193.3"/><polygon points="244.1,204.8 238.3,211.8 229.1,200.6 234.1,196.5"/><polygon points="230.4,173.1 234.3,170.1 246.3,180.2 240.3,185.1"/><polygon points="217.0,184.8 226.6,177.1 235.6,188.1 226.1,195.9"/><polygon points="250.0,218.8 242.3,216.4 241.7,215.6 251.8,203.5 258.3,208.8"/><polygon points="260.9,222.8 254.3,220.8 262.2,211.3 267.1,215.4"/><polygon points="283.8,229.6 265.4,224.0 270.2,218.3"/><polygon points="272.2,177.9 279.5,184.0 275.1,189.3 267.8,183.2"/><polygon points="271.6,192.4 261.4,204.5 255.3,199.4 265.4,187.3"/><polygon points="282.5,187.2 294.1,196.8 289.7,202.0 278.2,192.4"/><polygon points="287.2,205.7 284.6,208.8 272.5,198.7 275.1,195.6"/><polygon points="281.3,212.0 276.7,217.5 265.3,208.0 269.9,202.5"/><polygon points="295.9,224.7 292.4,228.8 290.4,229.2 280.4,220.8 284.8,215.4"/><polygon points="297.9,201.0 307.8,209.2 298.8,219.9 288.9,211.7"/><polygon points="251.8,364.9 258.2,362.4 262.9,374.5 256.5,377.0"/><polygon points="243.3,382.0 240.2,374.7 242.9,368.6 247.5,366.8 252.1,378.5"/><polygon points="257.8,397.7 249.8,396.3 245.7,386.6 255.5,382.8 260.9,396.5"/><polygon points="259.9,380.5 264.6,378.7 270.3,393.4 265.7,395.2"/><polygon points="274.5,402.0 263.4,400.0 272.4,396.5"/><polygon points="274.7,392.9 272.7,387.7 284.5,383.1 286.5,388.3"/><polygon points="292.2,404.1 278.6,401.7 276.7,396.9 287.8,392.6"/><polygon points="299.9,399.4 297.1,403.0 288.8,381.9 292.5,380.5"/><polygon points="316.3,370.4 319.8,374.5 316.0,379.4 308.3,373.5"/><polygon points="312.4,382.4 303.4,394.2 297.5,378.9 304.3,376.2"/><polygon points="296.1,347.4 299.4,351.2 290.3,359.0 287.1,350.9"/><polygon points="301.9,355.7 311.7,367.0 296.8,372.8 293.1,363.3"/><polygon points="292.0,375.4 284.3,378.4 279.7,366.5 287.3,363.5"/><polygon points="279.7,380.1 271.5,383.3 266.9,371.6 275.1,368.4"/><polygon points="265.2,366.5 263.1,361.1 282.9,353.4 285.0,358.8"/><polygon points="209.8,350.4 215.2,349.1 218.0,361.3 212.6,362.6"/><polygon points="201.0,365.2 200.7,352.6 205.8,351.5 208.5,363.4"/><polygon points="220.9,373.1 211.3,373.3 211.2,366.9 219.0,365.1"/><polygon points="207.9,373.7 200.5,373.9 200.4,369.0 207.8,367.3"/><polygon points="179.2,358.5 178.9,357.5 196.7,353.4 196.8,358.2"/><polygon points="185.3,374.0 182.5,374.0 179.8,362.2 185.1,362.1"/><polygon points="196.8,373.3 189.2,373.4 189.0,362.4 196.6,362.3"/><polygon points="233.7,372.6 225.0,372.8 223.0,364.1 231.2,362.2"/><polygon points="238.4,359.8 239.6,366.5 237.2,371.8 234.6,360.7"/><polygon points="228.1,358.6 222.0,360.0 219.2,348.3 225.4,346.9"/><polygon points="235.2,344.5 237.3,356.5 232.1,357.7 229.3,345.8"/><polygon points="222.0,343.8 214.4,345.6 213.0,339.5 220.6,337.7"/><polygon points="212.2,336.3 210.9,330.7 218.6,328.9 219.9,334.5"/><polygon points="232.0,326.1 233.2,332.6 223.4,334.3 222.1,328.4"/><polygon points="234.0,336.1 234.8,340.8 225.3,343.0 224.1,337.8"/><polygon points="229.7,314.7 231.0,322.1 220.5,324.6 218.8,317.2"/><polygon points="228.9,308.1 229.2,308.3 229.7,310.9 217.3,313.8 216.7,310.9"/><polygon points="216.3,325.6 210.6,326.9 207.5,313.7 213.2,312.4"/><polygon points="218.0,299.9 226.2,305.8 219.7,307.3"/><polygon points="216.5,305.5 217.1,307.8 205.9,310.4 205.4,308.1"/><polygon points="203.8,299.2 214.4,298.1 215.4,302.5 205.1,304.9"/><polygon points="206.5,327.2 207.9,333.1 198.1,335.4 196.7,329.5"/><polygon points="205.6,321.3 206.3,324.0 195.5,326.5 194.9,323.8"/><polygon points="194.6,336.3 190.3,337.3 187.7,325.9 191.9,324.9"/><polygon points="208.7,336.7 210.9,346.2 204.8,347.6 202.6,338.1"/><polygon points="201.0,348.3 193.6,350.1 191.4,340.9 198.9,339.1"/><polygon points="189.5,350.9 178.2,353.5 176.6,347.0 188.0,344.4"/><polygon points="175.7,342.9 174.1,336.0 185.4,333.4 187.0,340.2"/><polygon points="172.7,332.4 172.0,329.4 184.4,326.5 185.1,329.6"/><polygon points="169.5,318.0 168.1,312.1 176.7,310.1 178.0,316.0"/><polygon points="180.1,324.2 171.3,326.2 170.2,321.3 178.9,319.2"/><polygon points="185.4,308.3 186.9,322.3 183.5,323.1 180.3,309.5"/><polygon points="166.5,303.5 184.5,301.6 184.8,304.2 167.6,308.2"/><polygon points="203.2,313.9 204.2,318.1 190.8,321.3 190.1,315.3"/><polygon points="198.8,299.3 200.5,299.1 203.1,310.4 200.0,310.8"/><polygon points="188.6,301.0 195.5,300.3 196.5,310.5 189.7,311.2"/><polygon points="403.1,124.1 411.4,125.4 403.6,146.8 395.8,144.0"/><polygon points="384.4,121.9 388.5,121.8 396.2,123.1 389.5,141.5 378.6,137.5"/><polygon points="400.7,153.3 393.9,171.9 381.7,167.4 388.5,148.9"/><polygon points="375.1,165.8 368.6,163.5 376.0,143.4 382.4,145.8"/><polygon points="352.7,122.7 358.2,121.7 377.7,121.5 374.3,130.6"/><polygon points="352.1,129.5 371.5,136.6 367.8,146.8 348.4,139.7"/><polygon points="365.7,153.3 362.9,161.1 342.9,153.8 345.7,145.9"/><polygon points="320.6,130.4 327.0,128.3 340.7,126.0 344.6,127.4 336.2,150.3 326.2,146.6"/><polygon points="336.4,179.3 328.1,155.2 343.2,160.8"/><polygon points="391.0,179.1 384.5,196.9 370.1,191.5 376.6,173.8"/><polygon points="362.4,188.5 343.5,181.5 349.8,164.2 368.8,171.2"/><polygon points="263.4,355.5 256.2,358.3 252.0,347.7 261.7,346.0"/><polygon points="251.7,359.9 243.0,363.3 240.6,349.9 247.3,348.7"/><polygon points="240.2,344.4 238.9,337.2 258.8,333.7 260.1,340.9"/><polygon points="286.2,345.8 268.3,352.8 266.5,342.9 285.1,339.6"/><polygon points="265.1,337.5 264.2,332.3 283.7,328.9 284.6,334.0"/><polygon points="295.8,326.5 294.5,343.3 291.4,344.5 288.5,327.8"/><polygon points="275.6,304.4 288.1,312.9 280.6,324.0 279.1,324.3"/><polygon points="292.4,314.6 297.0,317.7 296.7,322.1 286.1,324.0"/><polygon points="248.2,317.2 250.3,329.2 237.8,331.4 235.7,319.4"/><polygon points="233.1,309.1 247.1,306.7 248.1,312.1 234.1,314.6"/><polygon points="271.7,315.8 273.3,325.0 256.1,328.0 254.5,318.8"/><polygon points="252.0,306.4 269.8,303.3 271.0,310.0 253.2,313.2"/><polygon points="161.2,308.4 165.4,326.7 147.4,330.8 143.2,312.6"/><polygon points="137.0,291.2 157.8,301.8 140.4,305.9"/><polygon points="126.6,308.7 129.0,301.1 132.4,293.9 135.4,306.7"/><polygon points="120.5,337.3 121.4,331.8 126.7,315.0 134.8,313.1 139.4,332.8"/><polygon points="116.9,354.1 118.3,345.3 137.7,340.8 139.6,348.8"/><polygon points="124.5,359.6 140.6,355.8 144.3,371.4 128.2,375.1"/><polygon points="114.9,379.6 114.9,361.3 115.1,360.4 118.1,359.7 122.3,377.9"/><polygon points="138.8,379.4 146.8,377.5 150.3,392.7 141.6,394.4"/><polygon points="116.0,390.2 116.0,385.6 132.8,381.7 135.2,394.7 117.4,397.9"/><polygon points="174.8,378.9 165.8,389.7 155.9,391.6 154.0,383.7"/><polygon points="172.5,356.9 175.8,371.3 153.7,376.5 150.3,362.0"/><polygon points="145.2,340.0 167.3,334.8 170.6,348.7 148.4,353.8"/><polygon points="503.7,278.6 499.8,298.8 497.4,305.7 475.4,287.2 491.9,267.6"/><polygon points="469.0,281.7 459.8,274.0 477.0,253.7 485.8,261.8"/><polygon points="453.4,268.6 444.6,261.2 463.5,241.0 470.9,247.9"/><polygon points="504.4,226.0 506.5,242.2 505.5,268.7 483.5,248.3"/><polygon points="498.8,203.2 502.9,215.3 477.4,242.5 469.2,234.9"/><polygon points="296.6,265.2 313.2,268.1 311.7,276.4 295.2,273.6"/><polygon points="310.8,282.1 309.4,290.1 292.8,287.3 294.2,279.2"/><polygon points="279.5,284.6 283.2,263.2 290.7,264.5 287.0,285.9"/><polygon points="289.8,292.2 294.9,293.1 292.1,309.2 282.7,302.8"/><polygon points="275.7,299.0 277.4,289.4 285.4,290.8 278.6,300.9"/><polygon points="300.2,294.3 308.4,295.7 306.0,309.8 299.0,313.5 297.1,312.2"/><polygon points="325.6,300.1 311.3,307.8 313.0,298.0"/><polygon points="331.6,286.2 330.0,295.8 314.7,293.2 316.4,283.6"/><polygon points="318.8,269.0 334.2,271.6 332.7,280.6 317.3,277.9"/><polygon points="284.2,257.0 286.1,245.9 304.0,249.0 295.0,258.9"/><polygon points="286.3,240.4 287.3,234.5 292.9,233.6 303.7,243.4"/><polygon points="310.8,250.6 325.8,264.2 302.1,260.2"/><polygon points="189.9,378.6 203.7,378.3 203.9,389.6 190.1,389.9"/><polygon points="204.4,395.1 204.6,402.6 189.9,402.8 189.7,395.4"/><polygon points="174.8,386.3 182.1,377.6 185.0,377.6 185.2,386.2"/><polygon points="175.9,402.9 170.6,392.8 172.1,391.0 184.4,390.8 184.6,402.7"/><polygon points="205.8,433.0 193.2,437.6 192.0,435.2 205.1,428.3 205.8,429.6"/><polygon points="190.7,430.6 185.3,420.3 196.5,414.4 202.0,424.7"/><polygon points="200.0,411.2 205.5,408.3 205.8,422.1"/><polygon points="182.4,415.8 178.3,408.0 198.0,407.6"/><polygon points="225.3,424.5 210.6,429.9 210.4,416.2 225.1,415.9"/><polygon points="240.2,414.5 237.9,421.1 230.3,423.9 230.1,414.7"/><polygon points="227.3,409.8 210.2,410.1 210.1,400.9 227.1,400.6"/><polygon points="244.9,399.8 241.3,410.1 232.8,410.2 232.6,400.0"/><polygon points="235.9,377.3 243.5,394.8 234.4,395.0 234.1,377.3"/><polygon points="229.5,395.2 224.5,395.3 224.2,377.4 229.2,377.3"/><polygon points="219.2,394.8 209.9,395.0 209.6,378.2 218.9,378.0"/><polygon points="153.9,470.6 145.5,463.0 137.9,452.1 154.7,452.9"/><polygon points="183.5,455.1 171.4,483.1 165.2,479.6 161.1,475.9 162.1,454.2"/></g><g fill="none" stroke-linecap="round" stroke-linejoin="round"><path d="M192.3,251.5 L227.9,257.4 L251.7,265.7" stroke="#6c604a" stroke-width="3.4"/><path d="M251.7,265.7 L279.5,266.1 L303.2,277.0" stroke="#6c604a" stroke-width="3.4"/><path d="M251.7,265.7 L252.1,303.6 L263.7,330.1" stroke="#6c604a" stroke-width="3.4"/><path d="M263.7,330.1 L268.9,355.8 L280.0,377.8" stroke="#6c604a" stroke-width="3.4"/><path d="M280.0,377.8 L272.4,403.1 L272.9,430.3" stroke="#6c604a" stroke-width="3.4"/><path d="M272.9,430.3 L246.7,439.1 L223.7,450.8" stroke="#6c604a" stroke-width="3.4"/><path d="M223.7,450.8 L209.6,467.1 L203.0,483.2" stroke="#6c604a" stroke-width="3.4"/><path d="M203.0,483.2 L181.3,470.8 L163.3,463.8" stroke="#6c604a" stroke-width="3.4"/><path d="M163.3,463.8 L160.2,449.9 L153.0,423.9" stroke="#6c604a" stroke-width="3.4"/><path d="M223.7,450.8 L215.9,431.2 L208.6,402.5" stroke="#6c604a" stroke-width="3.4"/><path d="M223.7,450.8 L242.0,468.6 L260.9,485.1" stroke="#6c604a" stroke-width="3.4"/><path d="M280.0,377.8 L309.5,390.9 L331.5,409.0" stroke="#6c604a" stroke-width="3.4"/><path d="M331.5,409.0 L355.7,422.6 L378.3,435.1" stroke="#6c604a" stroke-width="3.4"/><path d="M263.7,330.1 L235.9,337.0 L203.8,336.8" stroke="#6c604a" stroke-width="3.4"/><path d="M272.9,430.3 L296.5,451.5 L319.3,468.9" stroke="#6c604a" stroke-width="3.4"/><path d="M203.8,336.8 L172.5,338.7 L143.9,349.9" stroke="#6c604a" stroke-width="3.4"/><path d="M280.0,377.8 L309.4,359.7 L329.2,337.4" stroke="#6c604a" stroke-width="3.4"/><path d="M329.2,337.4 L354.6,351.7 L382.1,361.5" stroke="#6c604a" stroke-width="3.4"/><path d="M382.1,361.5 L406.1,373.6 L432.1,383.6" stroke="#6c604a" stroke-width="3.4"/><path d="M303.2,277.0 L315.4,250.8 L347.7,226.8" stroke="#6c604a" stroke-width="3.4"/><path d="M329.2,337.4 L349.3,314.1 L375.1,288.2" stroke="#6c604a" stroke-width="3.4"/><path d="M432.1,383.6 L451.2,350.7 L446.2,311.2" stroke="#6c604a" stroke-width="3.4"/><path d="M446.2,311.2 L469.0,287.2 L484.1,255.9" stroke="#6c604a" stroke-width="3.4"/><path d="M484.1,255.9 L469.5,228.4 L439.5,219.8" stroke="#6c604a" stroke-width="3.4"/><path d="M439.5,219.8 L440.8,192.3 L434.9,166.6" stroke="#6c604a" stroke-width="3.4"/><path d="M434.9,166.6 L401.4,162.4 L366.0,153.1" stroke="#6c604a" stroke-width="3.4"/><path d="M347.7,226.8 L313.9,206.8 L279.1,182.6" stroke="#6c604a" stroke-width="3.4"/><path d="M192.3,251.5 L148.8,293.9 L143.9,349.9" stroke="#6c604a" stroke-width="3.4"/><path d="M319.3,468.9 L289.9,483.2 L260.9,485.1" stroke="#6c604a" stroke-width="3.4"/><path d="M439.5,219.8 L392.4,225.1 L347.7,226.8" stroke="#6c604a" stroke-width="3.4"/><path d="M439.5,219.8 L404.5,253.5 L375.1,288.2" stroke="#6c604a" stroke-width="3.4"/><path d="M439.5,219.8 L425.9,261.6 L446.2,311.2" stroke="#6c604a" stroke-width="3.4"/><path d="M251.7,265.7 L263.0,225.5 L279.1,182.6" stroke="#6c604a" stroke-width="3.4"/><path d="M347.7,226.8 L366.5,257.9 L375.1,288.2" stroke="#6c604a" stroke-width="3.4"/><path d="M347.7,226.8 L387.9,203.5 L434.9,166.6" stroke="#6c604a" stroke-width="3.4"/><path d="M329.2,337.4 L315.7,307.8 L303.2,277.0" stroke="#6c604a" stroke-width="3.4"/><path d="M203.0,483.2 L229.8,494.0 L260.9,485.1" stroke="#6c604a" stroke-width="3.4"/><path d="M329.2,337.4 L127.1,300.4" stroke="#6c604a" stroke-width="4.8"/><path d="M329.2,337.4 L466.6,147.7" stroke="#6c604a" stroke-width="4.8"/><path d="M329.2,337.4 L364.9,473.5" stroke="#6c604a" stroke-width="4.8"/><path d="M192.3,251.5 L227.9,257.4 L251.7,265.7" stroke="#e4d9bc" stroke-width="1.8"/><path d="M251.7,265.7 L279.5,266.1 L303.2,277.0" stroke="#e4d9bc" stroke-width="1.8"/><path d="M251.7,265.7 L252.1,303.6 L263.7,330.1" stroke="#e4d9bc" stroke-width="1.8"/><path d="M263.7,330.1 L268.9,355.8 L280.0,377.8" stroke="#e4d9bc" stroke-width="1.8"/><path d="M280.0,377.8 L272.4,403.1 L272.9,430.3" stroke="#e4d9bc" stroke-width="1.8"/><path d="M272.9,430.3 L246.7,439.1 L223.7,450.8" stroke="#e4d9bc" stroke-width="1.8"/><path d="M223.7,450.8 L209.6,467.1 L203.0,483.2" stroke="#e4d9bc" stroke-width="1.8"/><path d="M203.0,483.2 L181.3,470.8 L163.3,463.8" stroke="#e4d9bc" stroke-width="1.8"/><path d="M163.3,463.8 L160.2,449.9 L153.0,423.9" stroke="#e4d9bc" stroke-width="1.8"/><path d="M223.7,450.8 L215.9,431.2 L208.6,402.5" stroke="#e4d9bc" stroke-width="1.8"/><path d="M223.7,450.8 L242.0,468.6 L260.9,485.1" stroke="#e4d9bc" stroke-width="1.8"/><path d="M280.0,377.8 L309.5,390.9 L331.5,409.0" stroke="#e4d9bc" stroke-width="1.8"/><path d="M331.5,409.0 L355.7,422.6 L378.3,435.1" stroke="#e4d9bc" stroke-width="1.8"/><path d="M263.7,330.1 L235.9,337.0 L203.8,336.8" stroke="#e4d9bc" stroke-width="1.8"/><path d="M272.9,430.3 L296.5,451.5 L319.3,468.9" stroke="#e4d9bc" stroke-width="1.8"/><path d="M203.8,336.8 L172.5,338.7 L143.9,349.9" stroke="#e4d9bc" stroke-width="1.8"/><path d="M280.0,377.8 L309.4,359.7 L329.2,337.4" stroke="#e4d9bc" stroke-width="1.8"/><path d="M329.2,337.4 L354.6,351.7 L382.1,361.5" stroke="#e4d9bc" stroke-width="1.8"/><path d="M382.1,361.5 L406.1,373.6 L432.1,383.6" stroke="#e4d9bc" stroke-width="1.8"/><path d="M303.2,277.0 L315.4,250.8 L347.7,226.8" stroke="#e4d9bc" stroke-width="1.8"/><path d="M329.2,337.4 L349.3,314.1 L375.1,288.2" stroke="#e4d9bc" stroke-width="1.8"/><path d="M432.1,383.6 L451.2,350.7 L446.2,311.2" stroke="#e4d9bc" stroke-width="1.8"/><path d="M446.2,311.2 L469.0,287.2 L484.1,255.9" stroke="#e4d9bc" stroke-width="1.8"/><path d="M484.1,255.9 L469.5,228.4 L439.5,219.8" stroke="#e4d9bc" stroke-width="1.8"/><path d="M439.5,219.8 L440.8,192.3 L434.9,166.6" stroke="#e4d9bc" stroke-width="1.8"/><path d="M434.9,166.6 L401.4,162.4 L366.0,153.1" stroke="#e4d9bc" stroke-width="1.8"/><path d="M347.7,226.8 L313.9,206.8 L279.1,182.6" stroke="#e4d9bc" stroke-width="1.8"/><path d="M192.3,251.5 L148.8,293.9 L143.9,349.9" stroke="#e4d9bc" stroke-width="1.8"/><path d="M319.3,468.9 L289.9,483.2 L260.9,485.1" stroke="#e4d9bc" stroke-width="1.8"/><path d="M439.5,219.8 L392.4,225.1 L347.7,226.8" stroke="#e4d9bc" stroke-width="1.8"/><path d="M439.5,219.8 L404.5,253.5 L375.1,288.2" stroke="#e4d9bc" stroke-width="1.8"/><path d="M439.5,219.8 L425.9,261.6 L446.2,311.2" stroke="#e4d9bc" stroke-width="1.8"/><path d="M251.7,265.7 L263.0,225.5 L279.1,182.6" stroke="#e4d9bc" stroke-width="1.8"/><path d="M347.7,226.8 L366.5,257.9 L375.1,288.2" stroke="#e4d9bc" stroke-width="1.8"/><path d="M347.7,226.8 L387.9,203.5 L434.9,166.6" stroke="#e4d9bc" stroke-width="1.8"/><path d="M329.2,337.4 L315.7,307.8 L303.2,277.0" stroke="#e4d9bc" stroke-width="1.8"/><path d="M203.0,483.2 L229.8,494.0 L260.9,485.1" stroke="#e4d9bc" stroke-width="1.8"/><path d="M329.2,337.4 L127.1,300.4" stroke="#e4d9bc" stroke-width="3.2"/><path d="M329.2,337.4 L466.6,147.7" stroke="#e4d9bc" stroke-width="3.2"/><path d="M329.2,337.4 L364.9,473.5" stroke="#e4d9bc" stroke-width="3.2"/></g><path d="M112.4,390.5 L112.4,361.1 M112.4,361.1 L117.4,330.8 M117.4,330.8 L124.3,309.1 M131.0,292.2 L141.2,270.5 M141.2,270.5 L159.2,241.7 M159.2,241.7 L180.9,214.6 M180.9,214.6 L205.7,189.8 M205.7,189.8 L233.2,167.8 M233.2,167.8 L262.9,149.2 M262.9,149.2 L294.0,134.5 M294.0,134.5 L326.0,124.2 M326.0,124.2 L357.9,118.7 M357.9,118.7 L388.8,118.3 M388.8,118.3 L417.8,123.1 M417.8,123.1 L444.0,133.0 M444.0,133.0 L458.9,142.8 M472.9,154.3 L484.9,166.7 M484.9,166.7 L498.5,189.2 M498.5,189.2 L507.1,214.6 M507.1,214.6 L510.8,242.1 M510.8,242.1 L509.6,270.7 M509.6,270.7 L503.8,299.9 M503.8,299.9 L493.9,328.9 M493.9,328.9 L480.0,357.3 M480.0,357.3 L462.7,384.5 M462.7,384.5 L442.2,410.2 M442.2,410.2 L418.8,433.9 M418.8,433.9 L392.9,455.1 M392.9,455.1 L372.5,468.5 M356.7,477.6 L335.1,488.5 M335.1,488.5 L304.4,499.5 M304.4,499.5 L273.2,506.1 M273.2,506.1 L242.6,507.9 M242.6,507.9 L213.4,504.7 M213.4,504.7 L186.5,496.3 M186.5,496.3 L162.7,483.1 M162.7,483.1 L143.0,465.2 M143.0,465.2 L127.8,443.3 M127.8,443.3 L117.5,418.2 M117.5,418.2 L112.4,390.5" fill="none" stroke="#3c3628" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/><g fill="#3c3628" stroke="#1c1810" stroke-width="0.6"><circle cx="112.4" cy="390.5" r="2.6"/><circle cx="112.4" cy="361.1" r="2.6"/><circle cx="117.4" cy="330.8" r="2.6"/><circle cx="127.1" cy="300.4" r="2.6"/><circle cx="141.2" cy="270.5" r="2.6"/><circle cx="159.2" cy="241.7" r="2.6"/><circle cx="180.9" cy="214.6" r="2.6"/><circle cx="205.7" cy="189.8" r="2.6"/><circle cx="233.2" cy="167.8" r="2.6"/><circle cx="262.9" cy="149.2" r="2.6"/><circle cx="294.0" cy="134.5" r="2.6"/><circle cx="326.0" cy="124.2" r="2.6"/><circle cx="357.9" cy="118.7" r="2.6"/><circle cx="388.8" cy="118.3" r="2.6"/><circle cx="417.8" cy="123.1" r="2.6"/><circle cx="444.0" cy="133.0" r="2.6"/><circle cx="466.6" cy="147.7" r="2.6"/><circle cx="484.9" cy="166.7" r="2.6"/><circle cx="498.5" cy="189.2" r="2.6"/><circle cx="507.1" cy="214.6" r="2.6"/><circle cx="510.8" cy="242.1" r="2.6"/><circle cx="509.6" cy="270.7" r="2.6"/><circle cx="503.8" cy="299.9" r="2.6"/><circle cx="493.9" cy="328.9" r="2.6"/><circle cx="480.0" cy="357.3" r="2.6"/><circle cx="462.7" cy="384.5" r="2.6"/><circle cx="442.2" cy="410.2" r="2.6"/><circle cx="418.8" cy="433.9" r="2.6"/><circle cx="392.9" cy="455.1" r="2.6"/><circle cx="364.9" cy="473.5" r="2.6"/><circle cx="335.1" cy="488.5" r="2.6"/><circle cx="304.4" cy="499.5" r="2.6"/><circle cx="273.2" cy="506.1" r="2.6"/><circle cx="242.6" cy="507.9" r="2.6"/><circle cx="213.4" cy="504.7" r="2.6"/><circle cx="186.5" cy="496.3" r="2.6"/><circle cx="162.7" cy="483.1" r="2.6"/><circle cx="143.0" cy="465.2" r="2.6"/><circle cx="127.8" cy="443.3" r="2.6"/><circle cx="117.5" cy="418.2" r="2.6"/></g><g fill="#3c3628" stroke="#1c1810" stroke-width="0.8"><rect x="124.1" y="297.4" width="6.0" height="6.0"/><rect x="463.6" y="144.7" width="6.0" height="6.0"/><rect x="361.9" y="470.5" width="6.0" height="6.0"/></g><polygon points="329.2,330.9 330.8,335.2 335.4,335.4 331.8,338.3 333.1,342.7 329.2,340.1 325.4,342.7 326.7,338.3 323.1,335.4 327.6,335.2" fill="#3c3628" stroke="#f7f3ea" stroke-width="1.4" stroke-linejoin="round" paint-order="stroke"/><g font-family="Georgia, serif" font-size="8" text-anchor="middle"><text x="192.3" y="254.5" stroke="#f7f3ea" stroke-width="2.5" paint-order="stroke" fill="#23211c">Slums</text><text x="319.3" y="471.9" stroke="#f7f3ea" stroke-width="2.5" paint-order="stroke" fill="#23211c">Slums</text><text x="439.5" y="222.8" stroke="#f7f3ea" stroke-width="2.5" paint-order="stroke" fill="#23211c">Slums</text><text x="251.7" y="268.7" stroke="#f7f3ea" stroke-width="2.5" paint-order="stroke" fill="#23211c">Garrison</text><text x="432.1" y="386.6" stroke="#f7f3ea" stroke-width="2.5" paint-order="stroke" fill="#23211c">Craftsmen</text><text x="378.3" y="438.1" stroke="#f7f3ea" stroke-width="2.5" paint-order="stroke" fill="#23211c">Craftsmen</text><text x="347.7" y="229.8" stroke="#f7f3ea" stroke-width="2.5" paint-order="stroke" fill="#23211c">Craftsmen</text><text x="223.7" y="453.8" stroke="#f7f3ea" stroke-width="2.5" paint-order="stroke" fill="#23211c">Noble</text><text x="329.2" y="340.4" stroke="#f7f3ea" stroke-width="2.5" paint-order="stroke" fill="#23211c">Citadel</text><text x="203.0" y="486.2" stroke="#f7f3ea" stroke-width="2.5" paint-order="stroke" fill="#23211c">Craftsmen</text><text x="331.5" y="412.0" stroke="#f7f3ea" stroke-width="2.5" paint-order="stroke" fill="#23211c">Craftsmen</text><text x="375.1" y="291.2" stroke="#f7f3ea" stroke-width="2.5" paint-order="stroke" fill="#23211c">Craftsmen</text><text x="153.0" y="426.9" stroke="#f7f3ea" stroke-width="2.5" paint-order="stroke" fill="#23211c">Garrison</text><text x="272.9" y="433.3" stroke="#f7f3ea" stroke-width="2.5" paint-order="stroke" fill="#23211c">Slums</text><text x="434.9" y="169.6" stroke="#f7f3ea" stroke-width="2.5" paint-order="stroke" fill="#23211c">Slums</text><text x="260.9" y="488.1" stroke="#f7f3ea" stroke-width="2.5" paint-order="stroke" fill="#23211c">Temple</text><text x="446.2" y="314.2" stroke="#f7f3ea" stroke-width="2.5" paint-order="stroke" fill="#23211c">Garrison</text><text x="382.1" y="364.5" stroke="#f7f3ea" stroke-width="2.5" paint-order="stroke" fill="#23211c">Craftsmen</text><text x="279.1" y="185.6" stroke="#f7f3ea" stroke-width="2.5" paint-order="stroke" fill="#23211c">Residential</text><text x="280.0" y="380.8" stroke="#f7f3ea" stroke-width="2.5" paint-order="stroke" fill="#23211c">Residential</text><text x="203.8" y="339.8" stroke="#f7f3ea" stroke-width="2.5" paint-order="stroke" fill="#23211c">Slums</text><text x="366.0" y="156.1" stroke="#f7f3ea" stroke-width="2.5" paint-order="stroke" fill="#23211c">Garrison</text><text x="263.7" y="333.1" stroke="#f7f3ea" stroke-width="2.5" paint-order="stroke" fill="#23211c">Residential</text><text x="143.9" y="352.9" stroke="#f7f3ea" stroke-width="2.5" paint-order="stroke" fill="#23211c">Garrison</text><text x="484.1" y="258.9" stroke="#f7f3ea" stroke-width="2.5" paint-order="stroke" fill="#23211c">Noble</text><text x="303.2" y="280.0" stroke="#f7f3ea" stroke-width="2.5" paint-order="stroke" fill="#23211c">Temple</text><text x="208.6" y="405.5" stroke="#f7f3ea" stroke-width="2.5" paint-order="stroke" fill="#23211c">Residential</text><text x="163.3" y="466.8" stroke="#f7f3ea" stroke-width="2.5" paint-order="stroke" fill="#23211c">Noble</text></g><text x="315.0" y="20" text-anchor="middle" font-family="Georgia, serif" font-size="16" font-weight="bold" stroke="#f7f3ea" stroke-width="3" paint-order="stroke" fill="#23211c">Caldwingate</text></svg>
@@ -215,6 +215,7 @@ def main() -> None:
215
215
  emit("shantytown", render_settlement("shantytown", seed=5))
216
216
  emit("metropolis", render_settlement("metropolis", seed=5))
217
217
  emit("grid-city", render_settlement("grid_city", seed=7))
218
+ emit("fortress-town", render_settlement("fortress_town", seed=3))
218
219
  emit("roads", render_roads(seed=7))
219
220
  emit("regions", render_regions(seed=4))
220
221
  emit("template-isthmus", render_template("isthmus", 0.5, seed=5))
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "mapwright"
7
- version = "0.21.0"
7
+ version = "0.22.0"
8
8
  description = "Domain-neutral procedural fantasy map & world generation: Voronoi terrain, hydraulic erosion, biomes, rivers, Markov place-names, and shaded-relief SVG."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -38,6 +38,7 @@ from .regions import Region, RegionGenerator
38
38
  from .rng import SeededRNG
39
39
  from .roads import Road, RegionalRoadGenerator
40
40
  from .settlement import (
41
+ Landmark,
41
42
  Lot,
42
43
  Settlement,
43
44
  SettlementConfig,
@@ -60,7 +61,7 @@ from .terrain import (
60
61
  compute_cell_polygons,
61
62
  )
62
63
 
63
- __version__ = "0.21.0"
64
+ __version__ = "0.22.0"
64
65
 
65
66
  __all__ = [
66
67
  "SeededRNG",
@@ -102,5 +103,6 @@ __all__ = [
102
103
  "Lot",
103
104
  "Street",
104
105
  "Wall",
106
+ "Landmark",
105
107
  "SETTLEMENT_PRESETS",
106
108
  ]
@@ -149,6 +149,12 @@ _ENUM_SPEC: list[tuple] = [
149
149
  ("layout", ("organic", "grid"), "organic",
150
150
  "Street pattern: 'organic' = winding ward-to-ward roads (the classic look); "
151
151
  "'grid' = a geometric street grid aligned to the town's long axis."),
152
+ ("purpose", ("general", "trade", "fortress", "religious", "harbor",
153
+ "extraction", "transit"), "general",
154
+ "What the town exists for. Anything but 'general' seeds a central landmark "
155
+ "(a special ward the main roads focus on) and biases the ward-kind mix: "
156
+ "trade→market, fortress→citadel, religious→temple, harbor→docks, "
157
+ "extraction→mine, transit→plaza."),
152
158
  ]
153
159
 
154
160
  # Ward kinds. One central market, optional dockside ward when coastal, and a
@@ -160,6 +166,29 @@ _OTHER_KINDS: list[str] = (
160
166
  + ["temple"] * 1 + ["garrison"] * 1
161
167
  )
162
168
 
169
+ # Town purpose → the kind of the central landmark ward (absent ⇒ no landmark, the
170
+ # central ward stays a plain market). Some reuse existing ward kinds (market,
171
+ # temple, docks); citadel/mine/plaza are landmark-only kinds.
172
+ _LANDMARK_KIND: dict[str, str] = {
173
+ "trade": "market",
174
+ "fortress": "citadel",
175
+ "religious": "temple",
176
+ "harbor": "docks",
177
+ "extraction": "mine",
178
+ "transit": "plaza",
179
+ }
180
+
181
+ # Town purpose → extra ward kinds folded into the weighted bag (biases the mix
182
+ # toward what the town is for). "general" has no entry ⇒ the bag is unchanged.
183
+ _PURPOSE_WARD_BIAS: dict[str, list[str]] = {
184
+ "trade": ["craftsmen"] * 3,
185
+ "fortress": ["garrison"] * 3,
186
+ "religious": ["temple"] * 3,
187
+ "harbor": ["craftsmen"] * 2,
188
+ "extraction": ["slums"] * 2 + ["craftsmen"] * 2,
189
+ "transit": ["residential"] * 2 + ["craftsmen"] * 1,
190
+ }
191
+
163
192
  # Per-ward lot sizing: a multiplier on the base ``lot_size`` (bigger ⇒ larger,
164
193
  # fewer plots), or ``None`` for an open ward with no buildings. Kinds absent from
165
194
  # this map use the base size (factor 1.0).
@@ -188,14 +217,16 @@ def _block_jitter_factor(era: float, wealth: float) -> float:
188
217
  return _clamp(1.0 - 1.3 * order, 0.25, 1.7)
189
218
 
190
219
 
191
- def _ward_kind_pool(wealth: float) -> list[str]:
192
- """The weighted ward-kind bag, shifted by wealth: poor ⇒ more slums, rich ⇒
193
- more noble/temple. Equals ``_OTHER_KINDS`` exactly at ``wealth == 0.5``."""
220
+ def _ward_kind_pool(wealth: float, purpose: str = "general") -> list[str]:
221
+ """The weighted ward-kind bag, shifted by wealth (poor ⇒ more slums, rich ⇒
222
+ more noble/temple) and biased by ``purpose``. Equals ``_OTHER_KINDS`` exactly
223
+ at ``wealth == 0.5`` and ``purpose == "general"``."""
194
224
  noble = max(0, round(2 + (wealth - 0.5) * 5))
195
225
  temple = max(0, round(1 + (wealth - 0.5) * 2))
196
226
  slums = max(0, round(2 - (wealth - 0.5) * 6))
197
227
  return (["residential"] * 5 + ["craftsmen"] * 3 + ["noble"] * noble
198
- + ["slums"] * slums + ["temple"] * temple + ["garrison"] * 1)
228
+ + ["slums"] * slums + ["temple"] * temple + ["garrison"] * 1
229
+ + _PURPOSE_WARD_BIAS.get(purpose, []))
199
230
 
200
231
 
201
232
  @dataclass
@@ -214,6 +245,8 @@ class SettlementConfig:
214
245
  """0..1 — ancient/organic ⇄ modern/grid-regular blocks."""
215
246
  layout: str = "organic"
216
247
  """Street pattern: 'organic' (winding ward roads) or 'grid' (geometric grid)."""
248
+ purpose: str = "general"
249
+ """What the town is for; non-'general' seeds a central landmark + biases wards."""
217
250
  walled: bool = False
218
251
  """Whether the town has a wall (stored now, rendered in a later version)."""
219
252
  coastal: bool = False
@@ -239,6 +272,7 @@ class SettlementConfig:
239
272
  "wealth": self.wealth,
240
273
  "era": self.era,
241
274
  "layout": self.layout,
275
+ "purpose": self.purpose,
242
276
  "walled": self.walled,
243
277
  "coastal": self.coastal,
244
278
  }
@@ -306,6 +340,12 @@ SETTLEMENT_PRESETS: dict[str, dict] = {
306
340
  "metropolis": {"population": 30000, "wealth": 0.92, "era": 0.95, "irregularity": 0.18},
307
341
  "grid_city": {"population": 16000, "wealth": 0.7, "era": 0.95,
308
342
  "layout": "grid", "irregularity": 0.25},
343
+ "fortress_town": {"population": 5000, "purpose": "fortress", "walled": True,
344
+ "irregularity": 0.3},
345
+ "pilgrimage_site": {"population": 3000, "purpose": "religious",
346
+ "irregularity": 0.5},
347
+ "mining_camp": {"population": 1500, "purpose": "extraction", "wealth": 0.2,
348
+ "irregularity": 0.7},
309
349
  }
310
350
 
311
351
 
@@ -339,6 +379,35 @@ class Ward:
339
379
  )
340
380
 
341
381
 
382
+ @dataclass
383
+ class Landmark:
384
+ """The town's defining feature (set by a non-'general' ``purpose``): the
385
+ central ward promoted to a special ``kind`` (citadel/temple/mine/…), which the
386
+ main roads focus on. ``ward`` is the id of that ward."""
387
+
388
+ ward: int
389
+ kind: str
390
+ center: Point
391
+ name: str
392
+
393
+ def to_dict(self) -> dict:
394
+ return {
395
+ "ward": self.ward,
396
+ "kind": self.kind,
397
+ "center": [self.center[0], self.center[1]],
398
+ "name": self.name,
399
+ }
400
+
401
+ @classmethod
402
+ def from_dict(cls, data: dict) -> "Landmark":
403
+ return cls(
404
+ ward=int(data["ward"]),
405
+ kind=data["kind"],
406
+ center=(float(data["center"][0]), float(data["center"][1])),
407
+ name=data["name"],
408
+ )
409
+
410
+
342
411
  @dataclass
343
412
  class Lot:
344
413
  """A building plot inside a ward (a convex polygon, the building footprint)."""
@@ -408,7 +477,8 @@ class Wall:
408
477
 
409
478
  @dataclass
410
479
  class Settlement:
411
- """Generated town layout: footprint, wards, lots, streets, gates, wall, coast."""
480
+ """Generated town layout: footprint, wards, lots, streets, gates, wall,
481
+ landmark, coast."""
412
482
 
413
483
  width: int
414
484
  height: int
@@ -419,8 +489,10 @@ class Settlement:
419
489
  streets: list[Street] = field(default_factory=list)
420
490
  gates: list[Point] = field(default_factory=list)
421
491
  wall: Wall | None = None
492
+ landmark: Landmark | None = None # set by a non-'general' purpose
422
493
  walled: bool = False
423
494
  coastal: bool = False
495
+ purpose: str = "general"
424
496
  water_edge: tuple[Point, Point] | None = None # coastline segment, if coastal
425
497
 
426
498
  # -- serialisation / interop ----------------------------------------
@@ -428,7 +500,7 @@ class Settlement:
428
500
  def to_dict(self) -> dict:
429
501
  """A JSON-safe mapping of the whole town, round-tripping via :meth:`from_dict`."""
430
502
  return {
431
- "schema": "mapwright/settlement@4",
503
+ "schema": "mapwright/settlement@5",
432
504
  "width": self.width,
433
505
  "height": self.height,
434
506
  "name": self.name,
@@ -438,8 +510,10 @@ class Settlement:
438
510
  "streets": [st.to_dict() for st in self.streets],
439
511
  "gates": [[x, y] for x, y in self.gates],
440
512
  "wall": None if self.wall is None else self.wall.to_dict(),
513
+ "landmark": None if self.landmark is None else self.landmark.to_dict(),
441
514
  "walled": self.walled,
442
515
  "coastal": self.coastal,
516
+ "purpose": self.purpose,
443
517
  "water_edge": (
444
518
  None if self.water_edge is None
445
519
  else [[self.water_edge[0][0], self.water_edge[0][1]],
@@ -460,8 +534,11 @@ class Settlement:
460
534
  streets=[Street.from_dict(st) for st in data.get("streets", [])],
461
535
  gates=[(float(x), float(y)) for x, y in data.get("gates", [])],
462
536
  wall=(Wall.from_dict(data["wall"]) if data.get("wall") else None),
537
+ landmark=(Landmark.from_dict(data["landmark"])
538
+ if data.get("landmark") else None),
463
539
  walled=bool(data.get("walled", False)),
464
540
  coastal=bool(data.get("coastal", False)),
541
+ purpose=data.get("purpose", "general"),
465
542
  water_edge=(None if we is None
466
543
  else ((float(we[0][0]), float(we[0][1])),
467
544
  (float(we[1][0]), float(we[1][1])))),
@@ -506,15 +583,23 @@ class SettlementGenerator:
506
583
 
507
584
  seeds = self._ward_seeds(footprint, cfg.population)
508
585
  polys = voronoi_cells(seeds, footprint)
509
- wards = self._build_wards(polys, cfg.coastal, water_edge, culture,
510
- _ward_kind_pool(cfg.wealth))
586
+ wards, hub_id = self._build_wards(polys, cfg.coastal, water_edge, culture,
587
+ _ward_kind_pool(cfg.wealth, cfg.purpose),
588
+ cfg.purpose)
589
+ # The hub (central ward) is what main roads focus on — a landmark when the
590
+ # purpose set one, otherwise the plain market.
591
+ hub = wards[hub_id].center if wards else (cx, cy)
592
+ landmark = None
593
+ if wards and cfg.purpose in _LANDMARK_KIND:
594
+ w = wards[hub_id]
595
+ landmark = Landmark(ward=w.id, kind=w.kind, center=w.center, name=w.name)
511
596
  # A grid town aligns both its lots and its streets to the footprint's axes.
512
597
  grid_axes = None
513
598
  if cfg.layout == "grid":
514
599
  _, u, v = self._principal_axis(footprint)
515
600
  grid_axes = (u, v)
516
601
  lots = self._build_lots(wards, cfg, grid_axes)
517
- streets, gates = self._build_streets(wards, footprint, cfg, water_edge)
602
+ streets, gates = self._build_streets(wards, footprint, cfg, water_edge, hub)
518
603
  wall = self._build_wall(footprint, cfg.walled, cfg.coastal, water_edge, gates)
519
604
 
520
605
  return Settlement(
@@ -527,6 +612,8 @@ class SettlementGenerator:
527
612
  streets=streets,
528
613
  gates=gates,
529
614
  wall=wall,
615
+ landmark=landmark,
616
+ purpose=cfg.purpose,
530
617
  walled=cfg.walled,
531
618
  coastal=cfg.coastal,
532
619
  water_edge=water_edge,
@@ -624,18 +711,22 @@ class SettlementGenerator:
624
711
  water_edge: tuple[Point, Point] | None,
625
712
  culture: str,
626
713
  kind_pool: list[str],
627
- ) -> list[Ward]:
628
- """Name + classify each non-degenerate ward (central = market, coastal
629
- nearest-water = docks, rest drawn from the wealth-weighted ``kind_pool``)."""
714
+ purpose: str = "general",
715
+ ) -> tuple[list[Ward], int]:
716
+ """Name + classify each non-degenerate ward (central = market, or a purpose
717
+ landmark; coastal nearest-water = docks; rest drawn from the weighted
718
+ ``kind_pool``). Returns the wards and the *hub* ward's id (the central
719
+ ward — a landmark when ``purpose`` set one, else the market)."""
630
720
  valid = [(i, p) for i, p in enumerate(polys) if len(p) >= 3]
631
721
  if not valid:
632
- return []
722
+ return [], 0
633
723
  centers = {i: polygon_centroid(p) for i, p in valid}
634
724
 
635
725
  mean_x = sum(c[0] for c in centers.values()) / len(centers)
636
726
  mean_y = sum(c[1] for c in centers.values()) / len(centers)
637
727
  market = min(centers, key=lambda i: (centers[i][0] - mean_x) ** 2
638
728
  + (centers[i][1] - mean_y) ** 2)
729
+ hub_kind = _LANDMARK_KIND.get(purpose, _MARKET) # central ward's kind
639
730
 
640
731
  docks = None
641
732
  if coastal and water_edge is not None:
@@ -649,15 +740,17 @@ class SettlementGenerator:
649
740
  + (centers[i][1] - wy) ** 2)
650
741
 
651
742
  wards: list[Ward] = []
743
+ hub_id = 0
652
744
  for new_id, (i, poly) in enumerate(valid):
653
745
  if i == market:
654
- kind = _MARKET
746
+ kind = hub_kind
747
+ hub_id = new_id
655
748
  elif i == docks:
656
749
  kind = _DOCKS
657
750
  else:
658
751
  kind = self._rng.choice(kind_pool)
659
752
  wards.append(Ward(new_id, poly, centers[i], self._names.place(culture), kind))
660
- return wards
753
+ return wards, hub_id
661
754
 
662
755
  # -- lots (recursive bisection of each ward) -------------------------
663
756
 
@@ -760,10 +853,12 @@ class SettlementGenerator:
760
853
  footprint: list[Point],
761
854
  cfg: SettlementConfig,
762
855
  water_edge: tuple[Point, Point] | None,
856
+ hub: Point | None = None,
763
857
  ) -> tuple[list[Street], list[Point]]:
764
858
  """Build the street network. ``layout="grid"`` lays a geometric grid aligned
765
859
  to the town's long axis; otherwise the classic organic network (MST + a few
766
- loops over adjacent wards, plus main roads from each gate to the market)."""
860
+ loops over adjacent wards, plus main roads from each gate to the ``hub`` —
861
+ the central landmark/market)."""
767
862
  if cfg.layout == "grid":
768
863
  return self._build_grid_streets(wards, footprint, cfg, water_edge)
769
864
  coastal = cfg.coastal
@@ -799,11 +894,14 @@ class SettlementGenerator:
799
894
  path = [centers[i], mid, centers[j]] if mid else [centers[i], centers[j]]
800
895
  streets.append(Street(path, "minor"))
801
896
 
802
- # Main roads: market ↔ each gate.
803
- market = next((w.center for w in wards if w.kind == _MARKET), wards[0].center)
897
+ # Main roads: the hub (central landmark/market) ↔ each gate.
898
+ focus = hub
899
+ if focus is None:
900
+ focus = next((w.center for w in wards if w.kind == _MARKET),
901
+ wards[0].center)
804
902
  gates = self._gates(footprint, coastal, water_edge)
805
903
  for gate in gates:
806
- streets.append(Street([market, gate], "main"))
904
+ streets.append(Street([focus, gate], "main"))
807
905
  return streets, gates
808
906
 
809
907
  @staticmethod