{
  "schema": "pointcast.gridtown/v1",
  "version": "1.1.1",
  "title": "Gridtown",
  "url": "https://pointcast.xyz/gridtown/",
  "description": "A small city you can hold in a browser. Lay roads, zone homes, shops and workshops, power them, plant parks, and keep the budget honest. Twelve lessons, one tap at a time, saved on your device.",
  "published": "2026-10-22",
  "author": "PointCast · Claude Code",
  "play": {
    "input": "Tap or click a tile with a tool selected; drag to paint roads, zones and bulldozing. Arrow keys move a cursor, Enter builds, number keys pick tools, N advances a month.",
    "time": "One month per Next month press, or one every two seconds with Auto on.",
    "storage": {
      "where": "browser-local",
      "key": "pc:gridtown:city:v1",
      "portable": "Export or import city JSON; the same file reloads the same town."
    },
    "share": {
      "map": "https://pointcast.xyz/gridtown/?seed={seed} opens the same empty terrain for anyone.",
      "city": "https://pointcast.xyz/gridtown/#city={code} opens a whole town read-only; the visitor can inspect it and keep a copy, which replaces their saved city.",
      "code": "g1.{seed}.{month}.{funds}.{tax}.{256 tile letters}.{url-encoded name}: numbers in base 36, funds may start with -. Tiles: g grass, w water, t trees, r road, p park, e power plant; homes a-d, shops h-k, workshops m,n,o,q for levels 0-3. Water must match the seed."
    },
    "inspect": "Inspect any tile (I) for its road, power, demand, air and land value, and the one next step that would let it grow. The verdict comes from running the next month, so \"It will grow next month\" is always true.",
    "price": "free",
    "account": "none"
  },
  "board": {
    "size": 16,
    "startingFunds": 2500,
    "maxLevel": 3,
    "tiles": [
      {
        "id": "grass",
        "label": "Grass",
        "terrain": true,
        "buildable": true
      },
      {
        "id": "water",
        "label": "Water",
        "terrain": true,
        "buildable": false
      },
      {
        "id": "trees",
        "label": "Trees",
        "terrain": true,
        "buildable": true
      },
      {
        "id": "road",
        "label": "Road",
        "terrain": false
      },
      {
        "id": "res",
        "label": "Homes",
        "terrain": false,
        "zone": true
      },
      {
        "id": "com",
        "label": "Shops",
        "terrain": false,
        "zone": true
      },
      {
        "id": "ind",
        "label": "Workshops",
        "terrain": false,
        "zone": true
      },
      {
        "id": "park",
        "label": "Park",
        "terrain": false
      },
      {
        "id": "power",
        "label": "Power plant",
        "terrain": false
      }
    ]
  },
  "tools": [
    {
      "id": "road",
      "label": "Road",
      "cost": 10,
      "key": "1",
      "hint": "Every zone needs a road touching one of its four sides."
    },
    {
      "id": "res",
      "label": "Homes",
      "cost": 50,
      "key": "2",
      "hint": "People move in when there are jobs, power and a road. Parks and water raise the value."
    },
    {
      "id": "com",
      "label": "Shops",
      "cost": 60,
      "key": "3",
      "hint": "Shops need customers. They grow with population."
    },
    {
      "id": "ind",
      "label": "Workshops",
      "cost": 70,
      "key": "4",
      "hint": "The most jobs per tile. Smoke covers every tile within two steps, so keep homes three away."
    },
    {
      "id": "park",
      "label": "Park",
      "cost": 40,
      "key": "5",
      "hint": "Cleans the air two tiles around it and raises land value for homes."
    },
    {
      "id": "power",
      "label": "Power plant",
      "cost": 400,
      "key": "6",
      "hint": "Powers 24 zone tiles anywhere in town. Costs 25 a month to run. Pollutes like a workshop."
    },
    {
      "id": "bulldoze",
      "label": "Bulldoze",
      "cost": 5,
      "key": "0",
      "hint": "Return a tile to grass. Always allowed, even when the town is broke."
    }
  ],
  "rules": {
    "growth": "Zone tiles are visited in reading order from the top left. A zone gains one level a month when it touches a road on one of four sides, is powered, and demand for it is positive at that moment; demand is recomputed after every tile that changes, so growth stops once the town has what it asked for. A zone loses a level when dark or cut off, or when demand is below -40. A home in smoke stops at level one and loses a level a month until it gets there.",
    "people": {
      "residentsPerHomeLevel": 8,
      "jobsPerShopLevel": 6,
      "jobsPerWorkshopLevel": 10
    },
    "power": {
      "zoneTilesPerPlant": 24,
      "order": "reading order from the top left; zones beyond capacity are dark"
    },
    "pollution": "A workshop adds 2 units of smoke and a power plant 3 to every tile within two steps (Manhattan distance, inclusive), whatever its level, so a clear home is three steps away. A park removes 2 units within two steps, so one park cancels one workshop, and adds land value; water and trees add value to neighbors. Only a park within two steps counts for the park lesson.",
    "demand": {
      "basis": "Every term counts people and jobs that exist, never tiles that are only zoned, so extra empty zoning waits instead of freezing the town.",
      "homes": "30 + (jobs - people) / 2, clamped to -100..100",
      "shops": "20 + 2 x (round(0.3 x people) - shop jobs), clamped",
      "workshops": "20 + 1.5 x (round(0.7 x people) - workshop jobs), clamped"
    },
    "money": {
      "income": "(people + jobs / 2) x taxRate / 10 per month",
      "upkeep": {
        "road": 2,
        "park": 2,
        "power": 25
      },
      "taxRange": [
        0,
        20
      ],
      "defaultTax": 7
    },
    "happiness": "Starts at 50. Plus up to 25 for full employment, up to 15 for parks per 40 residents, up to 10 for land value, and 1 per tax point under 7 up to 3; minus 3 per tax point over 7 and up to 30 for smoke over homes.",
    "bulldoze": "Always allowed, whatever the funds, so a broke town can shed upkeep. The page keeps one step of undo for builds.",
    "determinism": "The map comes from a 32-bit seed. Nothing after that is random: the same moves on the same seed give the same town."
  },
  "lessons": [
    {
      "number": 1,
      "id": "road",
      "title": "Lay a road",
      "text": "Pick the Road tool and tap three tiles in a row. Drag to paint."
    },
    {
      "number": 2,
      "id": "homes",
      "title": "Zone four homes",
      "text": "Homes go next to a road. Tap four grass tiles touching your road."
    },
    {
      "number": 3,
      "id": "power",
      "title": "Build a power plant",
      "text": "Nothing grows in the dark. A plant powers 24 zone tiles anywhere in town."
    },
    {
      "number": 4,
      "id": "jobs",
      "title": "Zone two shops and two workshops",
      "text": "The first few dozen move in on their own. After that, homes follow jobs. Shops sell, workshops make."
    },
    {
      "number": 5,
      "id": "month",
      "title": "Let a month pass",
      "text": "Press Next month, or turn on Auto. Watch the ledger."
    },
    {
      "number": 6,
      "id": "fifty",
      "title": "Reach 50 residents",
      "text": "Zones fill in one level at a time when they have power, a road and demand."
    },
    {
      "number": 7,
      "id": "park",
      "title": "Plant a park near homes",
      "text": "A park within two tiles of a home raises its value and cleans the air."
    },
    {
      "number": 8,
      "id": "clean",
      "title": "Keep every home out of the smoke",
      "text": "Workshops and plants put smoke on every tile within two steps. Keep homes three away, or add parks."
    },
    {
      "number": 9,
      "id": "black",
      "title": "Finish a month in the black",
      "text": "Roads, parks and plants cost upkeep. Taxes from people and jobs pay for them."
    },
    {
      "number": 10,
      "id": "twohundred",
      "title": "Reach 200 residents",
      "text": "Balance homes against jobs. The RCI bars show what the town is asking for."
    },
    {
      "number": 11,
      "id": "happy",
      "title": "Reach 80% happiness with 100 residents",
      "text": "Jobs for everyone, clean air, parks per person and a fair tax rate."
    },
    {
      "number": 12,
      "id": "year",
      "title": "Run the town for a year",
      "text": "Twelve months without going broke. Then it is your city."
    }
  ],
  "titles": [
    {
      "min": 0,
      "title": "Open country"
    },
    {
      "min": 1,
      "title": "Hamlet"
    },
    {
      "min": 50,
      "title": "Village"
    },
    {
      "min": 200,
      "title": "Town"
    },
    {
      "min": 500,
      "title": "City"
    },
    {
      "min": 1000,
      "title": "Metropolis"
    }
  ],
  "sample": {
    "seed": 20261022,
    "cityCode": "g1.c29i6.0.1xg.7.gtggggtwwggggtggttgggttgwtggtttggggggggwgggttttggtgtggwgggggttgggtggtgwgggggggggggtgttgwgggwwgggtgtgtggwgggwwwgggtgggwwggggwwwggtgtggwwggggggggggtgggwggggggggggtgtgggwgggggggggtttgggwggggggggggttggggwgggggggggtgggggwwgggggggggggggggwgtgggggggggggggwwtggggg.Gridtown",
    "legend": {
      ".": "grass",
      "~": "water",
      "t": "trees",
      "#": "road",
      "R": "homes",
      "C": "shops",
      "I": "workshops",
      "P": "park",
      "*": "power plant"
    },
    "map": [
      ".t....t~~....t..",
      "tt...tt.~t..ttt.",
      ".......~...tttt.",
      ".t.t..~.....tt..",
      ".t..t.~.........",
      "..t.tt.~...~~...",
      "t.t.t..~...~~~..",
      ".t...~~....~~~..",
      "t.t..~~.........",
      ".t...~..........",
      "t.t...~.........",
      "ttt...~.........",
      ".tt....~........",
      ".t.....~~.......",
      "........~.t.....",
      "........~~t....."
    ]
  },
  "source": "https://github.com/mhoydich/pointcast/blob/main/src/lib/gridtown.mjs"
}