Chapter 11: Game World
Let's build the world for our text adventure.
The World Module
Create adventure/world.sgl:
(define-library (adventure world)
(import (sigil struct))
(export
;; Struct constructors and accessors
room room? room-id room-name room-description room-exits room-items
item item? item-id item-name item-description item-takeable
game-state game-state?
game-state-current-room
game-state-inventory
game-state-rooms
game-state-items
game-state-won
;; World creation
create-world
;; State queries
lookup-room
lookup-item
get-room-items
room-has-exit?
get-exit-room)
(begin
;;; ========== Structs ==========
(define-struct room
(id)
(name)
(description)
(exits default: '())
(items default: '()))
(define-struct item
(id)
(name)
(description)
(takeable default: #t))
(define-struct game-state
(current-room)
(inventory default: '())
(rooms)
(items)
(won default: #f))
;;; ========== World Data ==========
(define kitchen
(room id: 'kitchen
name: "The Kitchen"
description: "A cozy kitchen with copper pots hanging from the ceiling.
The morning light streams through a window over the sink.
A door leads east to the hallway."
exits: '((east . hallway))
items: '(knife apple)))
(define hallway
(room id: 'hallway
name: "The Hallway"
description: "A long hallway with a worn carpet runner.
Paintings of stern ancestors line the walls.
Doors lead west to the kitchen, east to the study,
and south to the garden. The front door is to the north,
but it's locked tight."
exits: '((west . kitchen)
(east . study)
(south . garden))))
(define study
(room id: 'study
name: "The Study"
description: "A dusty study filled with books and papers.
A large oak desk dominates the room.
A door leads west to the hallway."
exits: '((west . hallway))
items: '(note)))
(define garden
(room id: 'garden
name: "The Garden"
description: "A beautiful garden with overgrown roses.
A stone fountain sits in the center, long dry.
Something glints beneath the dead leaves.
A path leads north back to the hallway."
exits: '((north . hallway))
items: '(key)))
;;; ========== Items ==========
(define items-list
(list
(item id: 'knife
name: "a kitchen knife"
description: "A sharp kitchen knife. Handle with care.")
(item id: 'apple
name: "a red apple"
description: "A crisp red apple. It looks delicious.")
(item id: 'note
name: "a crumpled note"
description: "A handwritten note that reads:
'The key to freedom lies where water once flowed.'")
(item id: 'key
name: "a golden key"
description: "A small golden key. It might unlock something important.")))
;;; ========== World Creation ==========
(define (create-world)
(game-state current-room: 'kitchen
inventory: '()
rooms: (list (cons 'kitchen kitchen)
(cons 'hallway hallway)
(cons 'study study)
(cons 'garden garden))
items: (map (lambda (i) (cons (item-id i) i))
items-list)))
;;; ========== Query Functions ==========
(define (lookup-room state room-id)
(let ((entry (assq room-id (game-state-rooms state))))
(if entry (cdr entry) #f)))
(define (lookup-item state item-id)
(let ((entry (assq item-id (game-state-items state))))
(if entry (cdr entry) #f)))
(define (get-room-items state room-id)
(let ((rm (lookup-room state room-id)))
(if rm (room-items rm) '())))
(define (room-has-exit? state room-id direction)
(let ((rm (lookup-room state room-id)))
(if rm
(assq direction (room-exits rm))
#f)))
(define (get-exit-room state room-id direction)
(let ((exit (room-has-exit? state room-id direction)))
(if exit (cdr exit) #f)))))Understanding the Structs
Rooms
Each room has:
id— Symbol for internal referencename— What the player sees as the titledescription— The full room descriptionexits— Association list mapping directions to room idsitems— List of item ids present in the room
Items
Each item has:
id— Symbol for internal referencename— How it appears in room descriptions ("a golden key")description— What the player sees when examining ittakeable— Can the player pick it up?
Game State
The game-state holds:
current-room— Where the player isinventory— Items the player carriesrooms— All rooms in the worlditems— All items in the worldwon— Whether the player has won
Immutable State Updates
We update state by creating new structs. Pass the old struct as the first argument to copy it, then override specific fields:
;; Move to a new room
(game-state old-state current-room: 'garden)
;; Add item to inventory
(game-state old-state
inventory: (cons 'key (game-state-inventory old-state)))The old state remains unchanged.
Testing the World
Create a simple test file test-world.sgl:
(import (adventure world))
(define state (create-world))
;; Test room lookup
(define rm (lookup-room state 'kitchen))
(display "Kitchen: ")
(display (room-name rm))
(newline)
;; Test exits
(display "Kitchen exits: ")
(display (room-exits rm))
(newline)
;; Test items
(display "Items in kitchen: ")
(display (room-items rm))
(newline)
;; Test item lookup
(define k (lookup-item state 'key))
(display "Key description: ")
(display (item-description k))
(newline)Run it (from the directory containing adventure/):
sigil eval -L . -f test-world.sglRoom Connections Diagram
[Front Door]
|
LOCKED
|
[Kitchen] ---east--- [Hallway] ---east--- [Study]
|
south
|
[Garden]
(has key)The player starts in the Kitchen. The key is in the Garden. Once they have the key, they can unlock the front door in the Hallway and win!
Win Condition
Notice that game-state includes a won field (defaulting to #f). We'll set this to #t when the player uses the key at the front door.
Practice Exercises
- Add another room (perhaps an "Attic") with a new item
- Create an item that can't be taken (like a heavy statue)
- Add more detail to room descriptions
What's Next
The world is ready! Now let's build the command parser and game logic.