Spaces:
Running on Zero
Running on Zero
| """Compact city description for the LLM prompt (<600 chars, always). | |
| Covers: NEMOCITY, population, building counts by kind, named landmarks and | |
| streets, traffic index + worst street, growth ring. Duck-types over World | |
| (no engine.world import — no cycles). | |
| """ | |
| from __future__ import annotations | |
| import time | |
| from typing import Any, Optional | |
| from . import constants as C | |
| from . import traffic | |
| from .city import CityState | |
| MAX_LEN = 599 # contract: strictly under 600 chars | |
| _MAX_TALLY_KINDS = 6 | |
| _MAX_LANDMARKS = 4 | |
| _MAX_STREETS = 5 | |
| _A_OR_AN_VOWEL = "aeiou" | |
| def _count_phrase(noun: str, n: int) -> str: | |
| noun = noun.replace("_", " ") | |
| if n == 1: | |
| article = "an" if noun[:1] in _A_OR_AN_VOWEL else "a" | |
| return f"{article} {noun}" | |
| plural = noun + ("es" if noun.endswith(("s", "x", "z", "ch", "sh")) else "s") | |
| return f"{n} {plural}" | |
| def summarize(world: Any, now_s: Optional[float] = None) -> str: | |
| if now_s is None: | |
| now_s = time.time() | |
| city = CityState.from_events(getattr(world, "features", ()) or ()) | |
| parts: list[str] = [ | |
| f"NEMOCITY, pop {city.population(now_s)}, epoch {getattr(world, 'epoch', 0)}" | |
| ] | |
| counts = city.counts_by_kind() | |
| if counts: | |
| bits = [ | |
| _count_phrase(kind, counts[kind]) | |
| for kind in list(counts)[:_MAX_TALLY_KINDS] | |
| ] | |
| if len(counts) > _MAX_TALLY_KINDS: | |
| bits.append("more") | |
| parts.append("buildings: " + ", ".join(bits)) | |
| parts.append(f"jobs {city.jobs}, homes for {city.housing_capacity}") | |
| landmarks = [ | |
| b.name for b in reversed(city.buildings) | |
| if b.name and C.BUILDINGS[b.kind]["attract"] >= 3 | |
| ][:_MAX_LANDMARKS] | |
| if landmarks: | |
| parts.append("landmarks: " + ", ".join(landmarks)) | |
| streets = list(city.street_cells) | |
| if streets: | |
| tail = f" +{len(streets) - _MAX_STREETS}" if len(streets) > _MAX_STREETS else "" | |
| parts.append("streets: " + ", ".join(streets[:_MAX_STREETS]) + tail) | |
| assignment = traffic.assign(city, now_s) | |
| line = f"traffic index {assignment.traffic_index}" | |
| if assignment.max_ratio > 0: | |
| worst = assignment.top_cells(1) | |
| if worst: | |
| line += f" (worst: {assignment.net.name(worst[0])} {assignment.ratio(worst[0]):.1f}x)" | |
| parts.append(line) | |
| parts.append(f"growth ring {city.growth_radius}") | |
| text = " | ".join(parts) | |
| if len(text) > MAX_LEN: | |
| text = text[: MAX_LEN - 3] + "..." | |
| return text | |