Grid Navigation System
Status: DESIGN Created: 2026-04-01 Supersedes: grid-layout.md (expanded with navigation, expandable grids, economy integration, multiplayer)
Last updated: October 21, 2026
Zoo Simulator - Grid Navigation System Design
Status: DESIGN Created: 2026-04-01 Supersedes: grid-layout.md (expanded with navigation, expandable grids, economy integration, multiplayer)
Overview
Transform Zoo Simulator from a container-based manager into a spatial strategy game with navigable grid zones. Players walk their zoo using N/S/E/W commands, place habitats and attractions on specific grid squares, and grow their zoo from a small 3x3 starter to a sprawling multi-zone complex. Other players can visit and walk through your zoo.
Design Pillars
- Start Small, Grow Big - 3x3 starter grid expands as players earn money and progress
- Placement Matters - Adjacency bonuses reward thoughtful layout design
- Walk Your Zoo - N/S/E/W navigation reuses proven Mystery/DF patterns
- Visit Friends - Party members and friends can tour your zoo layout
- Backward Compatible - Existing zoos auto-migrate with default layouts
Grid Model
Expandable Grid Sizes
Grids start small and grow as the player's zoo develops:
| Grid Size | Unlock Condition | Plots | Cost to Expand |
|---|---|---|---|
| 3x3 | Default (new zone) | 9 | Free |
| 5x5 | Zoo Score >= 200 | 25 | 5,000 Zoo Bucks |
| 7x7 | Zoo Score >= 500 | 49 | 15,000 Zoo Bucks |
| 9x9 | Zoo Score >= 800 | 81 | 30,000 Zoo Bucks |
| 11x11 | DEFERRED — not implemented (2026-05-16) | 121 | 50 Golden Tickets |
Each zone has its own grid. Different zones can be different sizes.
Build-Order Numbering & Persistence (player-facing "Attraction N" / "Habitat N")
Zones, habitats, and attractions live in Map<String, ?> keyed by random UUIDs. The player-facing 1-based numbering (view attractions / view habitats, and the buy animal habitat N / move animal habitat N / upgrade attraction N selectors) MUST reflect build order (oldest = 1), and the display and the selector MUST agree (fb 5370/5374).
A LinkedHashMap preserves build order only while the account stays Redis-hot. On the MySQL account_data JSON hydration path (any cache miss / TTL / corruption), MySQL normalizes JSON object keys to length-then-lexical order — and equal-length UUID keys sort purely lexically, scrambling build order (fb 8800/8801/8803: "attraction 3 is attraction 1"). The durable fix: each Attraction / Habitat (and pre-existing ZooZone) carries a builtDate; ZooLayoutOrdering sorts by it at every numbering call site, with legacy null-builtDate rows falling back to the trailing build number baked into the default name ("ride 3" -> 3). Numbering is NEVER derived from raw map iteration order.
zoo_account cache codec
builds the zoo_account Redis cache with NameBasedEnumKryo5Codec (not the default ordinal codec). A ZooAccount's serialized graph embeds drift-prone enums (Species, AttractionType, ZoneTheme, HabitatType, animal sex). The stock ordinal codec rebinds a cached enum to a different constant when constants are inserted mid-list (e.g. Species gained SQUIRREL/RACCOON/OPOSSUM/SKUNK between KANGAROO and the bird block, shifting every later ordinal), so a pre-deploy cached account decodes to the wrong species and crashes advance-day / breeding / feeding (fb 8800/8803/8824/8586/8587). Old entries self-heal on first read post-deploy (decode fails -> MySQL fallback -> re-cache under the name-based codec).
ZooGridSquare Model
@Data
public class ZooGridSquare implements Serializable {
private Integer x;
private Integer y;
// What's built here
private String plotType; // EMPTY, HABITAT, ATTRACTION, PATH, ENTRANCE, DECORATION, CONSTRUCTION
private String linkedId; // UUID of habitat/attraction placed here (null for path/decoration/empty)
private String linkedName; // Display name ("Savanna Habitat 1", "Restaurant")
// Descriptions (shown when player walks here)
private String description; // Full description
private String shortDescription; // Revisit text
// Navigation
private Boolean isBlocked; // Can't walk here (under construction, wall)
private Boolean isWalkable; // Players and visitors can traverse (paths, entrances)
// Visitor appeal
private Integer appealBonus; // From decorations and adjacency
private Boolean isVisitorAccessible; // Connected to entrance via path network
// Construction
private String constructionType; // What's being built (null if complete)
private Integer constructionDaysLeft; // 0 = complete
}
Plot Types
| Type | Symbol | Walkable | Visitors See | Player Builds |
|---|---|---|---|---|
| EMPTY | . |
Yes | Nothing | Available for building |
| HABITAT | H |
No | Animals (from adjacent path) | Build habitat command |
| ATTRACTION | A |
Yes | Enter and spend money | Build attraction command |
| PATH | = |
Yes | Walk along | Build path (200 Bucks) |
| ENTRANCE | E |
Yes | Entry/exit point | Fixed (auto-placed at edges) |
| DECORATION | D |
No | Aesthetic boost | Place decoration (500-2000) |
| CONSTRUCTION | C |
No | "Under construction" | Auto during build |
Walkable vs. visitor-accessible: the
Walkablecolumn governs player movement and where structures may be placed — EMPTY land is walkable so a player can route through it while building. It does NOT define the visitor path network. Visitor accessibility is a BFS from each entrance that follows ONLY PATH, ATTRACTION, and ENTRANCE squares; a habitat earns visitor revenue only when it is adjacent to a square on that path network. Empty land never connects a habitat to the entrance for revenue purposes ().
Default 3x3 Grid (New Zone)
Status (2026-05-30): SEEDED ON DEMAND via . createZone still does not seed the grid (ZooZone.gridSquares is @Builder.Default new LinkedHashMap<>(), empty), but reconcileGrid is now the canonical grid-population path and is idempotent: on the first build or navigation it initializes the E-path-E grid (entrances at opposite corners + L-shaped path), then back-fills every habitat/attraction in the zone container that is not yet linked to a grid square, expanding the grid when it runs out of plots. It is called at build time (, , and the zone-builder fallback) AND on every navigation/view ( enter/navigate/look/view-map). This fixed the bug where structures built after the first navigation never appeared on the grid (the old migrateExistingZone was a one-time op that no-opped once gridSquares was non-empty), reported as "all my zone plots are staying empty". migrateExistingZone is retained as a thin wrapper over reconcileGrid.
E = .
. . .
. . E
- Entrance at (0,0) and (2,2) — opposite corners
- One path connecting them
- 6 empty plots for building
Zone Data Model Changes
Add to existing ZooZone:
// Grid-based layout (new fields)
private Map<String, ZooGridSquare> gridSquares; // Key: "x,y"
private Integer gridWidth; // Current grid size (3, 5, 7, 9, or 11)
private Integer gridHeight; // Same as width (square grids)
Navigation System
Reuses Proven Patterns From
| Pattern | Source Game | What We Borrow |
|---|---|---|
| N/S/E/W movement | Mystery | Delta-based coordinate movement, move() method |
| Grid squares model | Mystery | Map<String, Square> keyed by "x,y" |
| Zone transitions | Mystery | Enter zone, go back to parent |
| Direction normalization | DF | 8-direction parsing, prefix stripping |
| Auto-detection on arrival | Mystery | Show what's at new location |
| interface | Common | Spring bean delegation pattern |
Movement Commands
| Command | Action |
|---|---|
| "go north" / "north" / "n" | Move to (x, y+1) |
| "go south" / "south" / "s" | Move to (x, y-1) |
| "go east" / "east" / "e" | Move to (x+1, y) |
| "go west" / "west" / "w" | Move to (x-1, y) |
| "look" / "look around" | Describe current square and adjacent |
| "view map" / "map" | Speak a voice-friendly layout summary of the current zone, no ASCII grid () |
| "enter zone [name]" | Switch to different zone (accepts 1 enter zone, enter zone 1, enter zone one, theme name, or zone name) |
| "go back" / "back" | Return to zone selection |
Movement Flow (Per Step)
1. Calculate target: newX = currentX + dx, newY = currentY + dy
2. Bounds check: 0 <= newX < gridWidth, 0 <= newY < gridHeight
3. Lookup: targetSquare = zone.gridSquares.get(newX + "," + newY)
4. If null or blocked: "There is no path [direction] from here." then the open exits, "You can go
south, east, or west.", offered as options. The direction is the first compass word anywhere in
what the player said ("north into my zoo"), as a whole word.
5. Update position: progress.currentX = newX, progress.currentY = newY
6. Play SFX: FOOTSTEPS_HALLWAY (paths), CREAKY_DOOR (entering habitat area)
7. Show arrival text:
- Square description
- Adjacent habitat info ("To the north you can see the Lion habitat")
- Adjacent attraction info ("There's a Restaurant to the east")
- Available exits
8. Auto-detect: habitat viewing from adjacent path squares
What Players See When Walking
On a PATH square:
You walk along the zoo path. To the north, you can see the Savanna Habitat
with 3 lions and 2 zebras. To the east, there's a Restaurant (8 visitors
inside). From here you can go north, south, east, west.
On an ATTRACTION square:
You're at the Restaurant in Zone 1 (Africa). 12 visitors are dining.
Today's revenue: 240 Zoo Bucks. To the west you can see the Savanna
Habitat. From here you can go north, south, east, west.
On an EMPTY square:
An empty plot ready for development. You can build a habitat, attraction,
path, or decoration here. From here you can go south, west.
View Map Command (Voice Layout)
view map is read aloud by Alexa, so it does NOT render an ASCII grid. Equals signs, pipes, single-letter legends, and bracketed coordinates are voice-hostile (; .renderAsciiMap rewritten in commit 6564686465). It speaks the zone size, a natural-language count of each feature type, and where the player is standing:
Here is the layout of Africa. It is five squares wide and five squares deep. It has two entrances, two habitats, one attraction, six walkways, and one decoration. You are in the south west area. Walk north, south, east, or west to explore, or say look around for details.
Per-square detail for the current and adjacent squares comes from the look-around and movement describePosition path, not from view map.
Placement System
Build/Place Commands
| Command | Result |
|---|---|
| "place habitat savanna" | Build savanna habitat on current empty square |
| "place attraction restaurant" | Build restaurant on current empty square |
| "build path" | Convert current empty square to path |
| "place decoration fountain" | Add decoration to current empty square |
| "move habitat north" | Relocate habitat to adjacent empty square (50% cost) |
| "demolish" | Remove structure from current square (25% refund) |
| "expand zone" | Upgrade grid from 3x3 to 5x5, etc. |
Placement Rules
- Can only place on EMPTY squares
- Must be standing on or adjacent to the target square
- Habitats require matching zone theme (lion needs savanna-compatible zone)
- Construction takes 1 day (square shows CONSTRUCTION, usable next advance-day)
- Paths are instant (no construction delay)
- Decorations are instant
Construction Flow
Player: "place habitat savanna"
→ Validates: standing on empty square, has funds, habitat type matches zone
→ Deducts cost (2,500 Zoo Bucks for savanna)
→ Square becomes CONSTRUCTION (plotType="CONSTRUCTION", constructionType="HABITAT")
→ constructionDaysLeft = 1
→ "Construction started! Your Savanna Habitat will be ready tomorrow."
→ Next advance-day: constructionDaysLeft-- → 0 → convert to HABITAT
→ "Your Savanna Habitat is now open! You can buy animals for it."
Adjacency Bonus System
When visitors view exhibits from adjacent path squares:
| Adjacent Pair | Bonus | Reason |
|---|---|---|
| Attraction + Habitat | +30% attraction revenue | Hungry/curious after watching animals |
| Attraction + Entrance | +20% attraction revenue | Impulse traffic entering/leaving |
| Habitat + Habitat | +10% satisfaction | Thematic coherence |
| Decoration + Any | +5% appeal to adjacent | Aesthetic enhancement |
Note (2026-05-16): Original design called for finer-grained pairs (Restaurant+Habitat 30%, Shop+Entrance 20%, Playground+Habitat-babies 25%, Ride+Restaurant 10%, Habitat+Habitat 10%, Decoration 5%). collapses every AttractionType into a single ATTRACTION plot type — so the table above matches code reality. Reintroducing per-AttractionType bonuses would require inspecting the linked attraction in getAdjacencyBonusPair and is filed as a future enhancement (LOW severity).
Adjacency Calculation
public double calculateAdjacencyBonus(ZooZone zone, int x, int y) {
double bonus = 0.0;
ZooGridSquare center = zone.getGridSquares().get(x + "," + y);
// Check 4 cardinal neighbors
for each neighbor in [(x-1,y), (x+1,y), (x,y-1), (x,y+1)]:
ZooGridSquare adj = zone.getGridSquares().get(neighbor);
if (adj == null) continue;
bonus += getAdjacencyBonusPair(center, adj);
return bonus;
}
Visitor Pathing
BFS from Entrances
Visitors enter at ENTRANCE squares and walk along connected PATH/ATTRACTION squares:
1. Find all ENTRANCE squares in zone
2. BFS from each entrance along walkable squares (PATH, ATTRACTION, ENTRANCE)
3. Mark all reachable squares as isVisitorAccessible = true
4. For each reachable PATH square, check adjacent HABITAT squares
5. Habitats adjacent to reachable paths earn visitor revenue
6. Habitats NOT adjacent to any reachable path earn ZERO visitor revenue
Critical Design Point: Building habitats without connecting paths means no visitors see them. This creates the core strategic tension.
Visitor Journey (Modified from Current)
Current system generates visitors globally. New system:
Per visitor per zone:
1. Enter at random entrance
2. BFS walk along paths (visit 3-8 squares based on morale)
3. At each PATH square: view adjacent habitats (morale += appealBonus * quality)
4. At each ATTRACTION square: spend money (revenue += revenuePerVisitor * morale)
5. Apply adjacency bonuses to all revenue/appeal calculations
6. Track total distance walked (shorter paths = more efficient zoo)
Economy Integration
Grid Expansion Costs
| Upgrade | Cost | Unlock |
|---|---|---|
| 3x3 -> 5x5 | 5,000 Zoo Bucks | Score >= 200 |
| 5x5 -> 7x7 | 15,000 Zoo Bucks | Score >= 500 |
| 7x7 -> 9x9 | 30,000 Zoo Bucks | Score >= 800 |
| 9x9 -> 11x11 | 50 Golden Tickets | DEFERRED — .expandZone caps at 9x9 today; no Golden Ticket spend path exists (2026-05-16) |
Building Costs on Grid
Same as existing costs but placement is now on a specific square:
| Structure | Cost | Time |
|---|---|---|
| Path | 200 Zoo Bucks | Instant |
| Decoration (fountain) | 500 Zoo Bucks | Instant |
| Decoration (garden) | 1,000 Zoo Bucks | Instant |
| Decoration (statue) | 2,000 Zoo Bucks | Instant |
| Habitat (varies) | 2,000-4,000 | 1 day |
| Attraction (varies) | 1,500-5,000 | 1 day |
| Relocation | 50% of build cost | Instant |
| Demolish | 25% refund | Instant |
Premium Grid Content
Golden Ticket purchases available on the grid:
| Item | GT Cost | Effect |
|---|---|---|
| Premium Decoration (dragon statue) | 10 GT | +15% appeal to all adjacent |
| Premium Path (golden walkway) | 5 GT | +10% visitor spending on this path |
| Instant Construction | 3 GT per building | Skip 1-day wait |
| Grid Expansion (11x11) | 50 GT | DEFERRED — premium-only max size, not yet implemented |
Multiplayer: Visit Other Zoos
Status: SHIPPED 2026-05-26 (Phase 2 of expansion-model.md). Handler + service + read-only walk + rate-zoo + destination-economy visit rewards now LIVE on develop. , , , zoo_simulator.zoo_visits, and zoo_simulator.zoo_visit_ratings shipped. ZOO_RATE_ZOO enum added; ZOO_VISIT_ZOO / ZOO_LEAVE_VISIT / ZOO_RATE_ZOO registered in process_action_registry + process_action_handlers + process_alternate_synonyms + process_one_shot_values. Canonical economic spec moved to expansion-model.md § Social/Visit Layer (Q4 destination-economy formulas) — the original "100 ZB × 3/day" spec below is SUPERSEDED. Scenario tests: 1539-1544. Phase 7 covers party-coop / comments / friend-list / featured-zoo-of-day.
Visit Flow
Player: "visit [friend name]'s zoo" or "visit zoo"
→ Load target player's zoo account (read-only)
→ Place visiting player at target's first zone entrance
→ Visiting player navigates freely using N/S/E/W
→ Each habitat viewed: show animals, training, enrichment
→ Each attraction visited: show revenue stats
→ Visiting player can leave comments ("rate zoo")
→ Visit counts toward target's daily visitor count (+5 per real visit)
Party Visit (Cooperative)
Party members can visit the leader's zoo together:
// (extends Mystery's party pattern)
public void visitLeaderZoo(GameBasicProperties props) {
// Teleport party member to leader's current zone + position
// Shared viewing: all party members see same zoo
// Party chat works during visit
}
Visit Rewards
| Who | Reward |
|---|---|
| Visitor | 100 Zoo Bucks per zoo visited (3/day cap) |
| Zoo owner | +5 visitors per real player visit |
| Both | Friendship XP (if friends system active) |
Visit Limitations
- Can't modify another player's zoo
- Can't buy/sell animals in other zoos
- Read-only navigation with viewing descriptions
- Visit sessions time out after 10 minutes of inactivity
Migration Plan (Existing Zoos)
Auto-Layout Algorithm
When zone.gridSquares == null (existing zoo, first load with grid system):
1. Set gridWidth = gridHeight = 3 (default starter)
2. Place entrance at (0,0) and (2,2)
3. Build path: (0,0)→(1,0)→(1,1)→(1,2)→(2,2)
4. Place first habitat at (0,1) (adjacent to path)
5. Place remaining habitats at (2,0), (0,2) (adjacent to path where possible)
6. Place first attraction at (2,1) (on path)
7. Remaining structures placed on remaining empty squares
8. If more structures than squares: auto-expand to 5x5, re-run
This ensures:
- All existing habitats are on the grid and visible to visitors
- All habitats are adjacent to at least one path
- No progress is lost
- Layout is functional (not optimal, player can rearrange)
NLU Registration
Relocation (ZOO_MOVE_STRUCTURE → ) is designed here but not
built: has no moveStructure, so the enum carries no ZOO_MOVE_STRUCTURE
constant. Building it adds the constant, its process_action_handlers row and its
process_action_registry row together; fails
while the registry row is missing.
FreeTextDeclarationAction Additions
// Grid navigation
ZOO_NAVIGATE, // matchedKey = NORTH, SOUTH, EAST, WEST
ZOO_LOOK, // View current square and surroundings
ZOO_VIEW_MAP, // Speak voice-friendly zone layout, no ASCII ()
// Grid placement
ZOO_PLACE_STRUCTURE, // matchedKey = habitat type, attraction type, path, decoration
ZOO_MOVE_STRUCTURE, // matchedKey = direction
ZOO_DEMOLISH, // Remove structure from current square
ZOO_EXPAND_ZONE, // Upgrade grid size
// Visiting
ZOO_VISIT_ZOO, // Visit another player's zoo
ZOO_RATE_ZOO, // Rate a visited zoo
ZOO_LEAVE_VISIT, // End visit
process_action_handlers
INSERT INTO game_process.process_action_handlers
(story_action, game_type, handler_bean, handler_method, enabled) VALUES
('ZOO_NAVIGATE', 'ZOO_SIMULATOR', 'zooGridNavigationHandler', 'navigate', 1),
('ZOO_LOOK', 'ZOO_SIMULATOR', 'zooGridNavigationHandler', 'lookAround', 1),
('ZOO_VIEW_MAP', 'ZOO_SIMULATOR', 'zooGridNavigationHandler', 'viewMap', 1),
('ZOO_PLACE_STRUCTURE', 'ZOO_SIMULATOR', 'zooGridPlacementHandler', 'placeStructure', 1),
('ZOO_MOVE_STRUCTURE', 'ZOO_SIMULATOR', 'zooGridPlacementHandler', 'moveStructure', 1),
('ZOO_DEMOLISH', 'ZOO_SIMULATOR', 'zooGridPlacementHandler', 'demolish', 1),
('ZOO_EXPAND_ZONE', 'ZOO_SIMULATOR', 'zooGridPlacementHandler', 'expandZone', 1),
('ZOO_VISIT_ZOO', 'ZOO_SIMULATOR', 'zooVisitHandler', 'visitZoo', 1),
('ZOO_RATE_ZOO', 'ZOO_SIMULATOR', 'zooVisitHandler', 'rateZoo', 1),
('ZOO_LEAVE_VISIT', 'ZOO_SIMULATOR', 'zooVisitHandler', 'leaveVisit', 1);
process_alternate_synonyms
INSERT INTO game_process.process_alternate_synonyms
(original_key, synonym_phrases, game_context) VALUES
('ZOO_NAVIGATE', 'go north', 'ZOO_SIMULATOR'),
('ZOO_NAVIGATE', 'go south', 'ZOO_SIMULATOR'),
('ZOO_NAVIGATE', 'go east', 'ZOO_SIMULATOR'),
('ZOO_NAVIGATE', 'go west', 'ZOO_SIMULATOR'),
('ZOO_NAVIGATE', 'north', 'ZOO_SIMULATOR'),
('ZOO_NAVIGATE', 'south', 'ZOO_SIMULATOR'),
('ZOO_NAVIGATE', 'east', 'ZOO_SIMULATOR'),
('ZOO_NAVIGATE', 'west', 'ZOO_SIMULATOR'),
('ZOO_NAVIGATE', 'walk', 'ZOO_SIMULATOR'),
('ZOO_NAVIGATE', 'move', 'ZOO_SIMULATOR'),
('ZOO_LOOK', 'look around', 'ZOO_SIMULATOR'),
('ZOO_LOOK', 'look', 'ZOO_SIMULATOR'),
('ZOO_LOOK', 'where am i', 'ZOO_SIMULATOR'),
('ZOO_VIEW_MAP', 'view map', 'ZOO_SIMULATOR'),
('ZOO_VIEW_MAP', 'show map', 'ZOO_SIMULATOR'),
('ZOO_VIEW_MAP', 'map', 'ZOO_SIMULATOR'),
('ZOO_VIEW_MAP', 'zoo layout', 'ZOO_SIMULATOR'),
('ZOO_PLACE_STRUCTURE', 'place habitat', 'ZOO_SIMULATOR'),
('ZOO_PLACE_STRUCTURE', 'place attraction', 'ZOO_SIMULATOR'),
('ZOO_PLACE_STRUCTURE', 'build path', 'ZOO_SIMULATOR'),
('ZOO_PLACE_STRUCTURE', 'place decoration', 'ZOO_SIMULATOR'),
('ZOO_PLACE_STRUCTURE', 'put habitat here', 'ZOO_SIMULATOR'),
('ZOO_PLACE_STRUCTURE', 'put restaurant here', 'ZOO_SIMULATOR'),
('ZOO_MOVE_STRUCTURE', 'move habitat', 'ZOO_SIMULATOR'),
('ZOO_MOVE_STRUCTURE', 'relocate', 'ZOO_SIMULATOR'),
('ZOO_DEMOLISH', 'demolish', 'ZOO_SIMULATOR'),
('ZOO_DEMOLISH', 'remove building', 'ZOO_SIMULATOR'),
('ZOO_EXPAND_ZONE', 'expand zone', 'ZOO_SIMULATOR'),
('ZOO_EXPAND_ZONE', 'upgrade zone', 'ZOO_SIMULATOR'),
('ZOO_EXPAND_ZONE', 'make zone bigger', 'ZOO_SIMULATOR'),
('ZOO_VISIT_ZOO', 'visit zoo', 'ZOO_SIMULATOR'),
('ZOO_VISIT_ZOO', 'visit friend', 'ZOO_SIMULATOR'),
('ZOO_RATE_ZOO', 'rate zoo', 'ZOO_SIMULATOR'),
('ZOO_LEAVE_VISIT', 'leave visit', 'ZOO_SIMULATOR'),
('ZOO_LEAVE_VISIT', 'go home', 'ZOO_SIMULATOR');
Scoring Impact
New Score Components
| Component | Max Points | Formula |
|---|---|---|
| Path Network Efficiency | 50 | connected_habitats / total_habitats * 50 |
| Adjacency Optimization | 50 | total_adjacency_bonus / max_possible * 50 |
| Grid Utilization | 25 | used_squares / total_squares * 25 |
| Visitor Path Coverage | 25 | visitor_accessible_habitats / total_habitats * 25 |
Added to existing 1000-point scale as bonus (max 1150 with perfect grid).
Example Playthrough
> build zone africa
Created Zone 1 with AFRICA theme! Your starting layout is a 3x3 grid.
E = .
. . .
. . E
You're at the entrance (0,0). Go east to explore.
> go east
You walk along the path. This is an empty plot. You can build here.
From here you can go east, west.
> place habitat savanna
Construction started! Your Savanna Habitat will be ready tomorrow.
Cost: 2,500 Zoo Bucks. Balance: 12,000 Zoo Bucks.
> go east
This square is empty. To the west, construction is underway.
From here you can go south, west.
> build path
Path built! Cost: 200 Zoo Bucks. Balance: 11,800 Zoo Bucks.
> go south
You walk along the path. This is an empty plot.
From here you can go north, south, west.
> place attraction restaurant
Construction started! Your Restaurant will be ready tomorrow.
Cost: 3,000 Zoo Bucks. Balance: 8,800 Zoo Bucks.
> advance day
=== Day Summary ===
Construction complete: Savanna Habitat, Restaurant
Visitors: 25 (12 visited the Restaurant near the Savanna Habitat!)
Revenue: 580 Zoo Bucks (Restaurant earned +30% from habitat adjacency)
> view map
Here is the layout of Africa. It is three squares wide and three squares deep. It has two entrances, one habitat, and two walkways. You are in the east area. Walk north, south, east, or west to explore, or say look around for details.