Skip to content

Commit 67e74c5

Browse files
authored
add games folder and simple game demonstrating nu code concepts (#1100)
I'd like to contribute this game. The code exemplifies several nu concepts.
1 parent a515c42 commit 67e74c5

File tree

2 files changed

+214
-0
lines changed

2 files changed

+214
-0
lines changed

games/humlespring/README.md

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# Humlespring Text Adventure
2+
3+
A simple text adventure game written in Nushell.
4+
5+
The game utilizes Nushell's functional programming capabilities in managing the game map and player state.
6+
7+
* **Immutable Data Structures**: The game map (`$map`) is defined as a record, which is an immutable data structure. This means the map's contents cannot be changed directly during gameplay, promoting data integrity and predictable game behavior. Locations, exits, items, and NPCs are all defined within this structure.
8+
9+
* **Data Transformation**: The `describe_location` function takes the player state and map as input and transforms this data to produce the location description. It uses the `get` command to access location data and `str join` to format the output, demonstrating functional data manipulation.
10+
11+
* **State Management**: The player state (`$player_state`) is a mutable record, but updates to it are handled in a controlled manner within the main loop. Actions like "go", "take", and "search" update the state by assigning new values to the `$player_state` record.
12+
13+
## How to Play
14+
15+
1. Make sure you have Nushell installed (`https://www.nushell.sh/`).
16+
2. Save the script as `script.nu`.
17+
3. Run the game by executing `nu script.nu` in your terminal.
18+
19+
## Commands
20+
21+
* `look` or `l`: Describe the current location.
22+
* `go` or `move` [direction]: Move in the specified direction (e.g., `go north`).
23+
* `take` or `get` [item]: Pick up an item (e.g., `take leaflet`).
24+
* `inventory` or `i`: View your inventory.
25+
* `talk` [npc]: Talk to a non-player character (e.g., `talk alexander`).
26+
* `search` [object]: Search an object in the current location (e.g., `search tree`).
27+
* `give` [item]: Give an item to an NPC (e.g., `give cat`).
28+
* `quit` or `exit`: Quit the game.
29+
30+
## Goal
31+
32+
Olivia has lost her cat, Mittens. Find Mittens and return her to Olivia in the village square.

games/humlespring/script.nu

Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
#!/usr/bin/env nu
2+
3+
# --- Game Data ---
4+
5+
# Define the locations in the village
6+
let map = {
7+
village_square: {
8+
desc: "You are in the charming village square of Humlespring. Cobblestones pave the ground. Paths lead north, east, and west. Olivia is here, looking worried."
9+
exits: { north: "tavern", east: "market", west: "alexander_house" }
10+
items: ["leaflet"]
11+
npcs: {
12+
olivia: "Olivia paces back and forth. 'Oh, Alexander... have you seen Mittens? My cat is missing! I last saw her near the old oak tree by the market.'"
13+
}
14+
},
15+
tavern: {
16+
desc: "The 'Leaky Mug' tavern. It smells faintly of ale and sawdust. The only exit is south."
17+
exits: { south: "village_square" }
18+
items: ["mug"]
19+
npcs: {}
20+
},
21+
market: {
22+
desc: "The village market area. Stalls are mostly empty now, but an old oak tree stands tall nearby. A faint 'meow' can be heard from the tree. The path leads back west."
23+
exits: { west: "village_square" }
24+
items: [] # Mittens isn't an 'item' to take, but is found here
25+
npcs: {}
26+
special: "You look up the old oak tree and see Mittens stuck on a branch! You carefully coax her down." # Add a trigger for this
27+
},
28+
alexander_house: {
29+
desc: "You are outside a small, tidy cottage. This must be Alexander's house. The only exit is east. Alexander is tending his small garden."
30+
exits: { east: "village_square" }
31+
items: ["watering_can"]
32+
npcs: {
33+
alexander: "'Ah, hello!' Alexander says, wiping his brow. 'Lovely day, isn't it? Though Olivia seems quite upset about her cat, Mittens. Maybe check near the big oak by the market?'"
34+
}
35+
}
36+
}
37+
38+
# --- Game State ---
39+
40+
# Player's current status (mutable)
41+
mut player_state = {
42+
location: "village_square" # Starting location ID
43+
inventory: []
44+
found_cat: false
45+
}
46+
47+
# --- Helper Functions ---
48+
49+
# Describe the current location
50+
def describe_location [state: record, map: record] {
51+
let current_loc_id = $state.location
52+
let loc_data = ($map | get $current_loc_id)
53+
54+
# Description
55+
print $loc_data.desc
56+
57+
# Items
58+
if not ($loc_data.items | is-empty) {
59+
print $"You see here: ($loc_data.items | str join ', ')"
60+
}
61+
62+
# NPCs
63+
let npcs = ($loc_data.npcs | columns)
64+
if not ($npcs | is-empty) {
65+
print $"People here: ($npcs | str join ', ')"
66+
}
67+
68+
# Exits
69+
let exits = ($loc_data.exits | columns | str join ', ')
70+
print $"Exits are: ($exits)"
71+
72+
# Check for winning condition trigger
73+
if $current_loc_id == "market" and not $state.found_cat {
74+
print "\nHint: Maybe you should 'search tree'?"
75+
}
76+
}
77+
78+
# Handle player actions are now inlined in the main loop below
79+
80+
# --- Main Game Loop ---
81+
82+
print "Welcome to Humlespring!"
83+
print "-------------------------"
84+
describe_location $player_state $map
85+
86+
loop {
87+
# Get player input
88+
let user_input = (input "> " | str trim)
89+
90+
# Skip empty input
91+
if $user_input == "" { continue }
92+
93+
# Parse input (simple verb-noun)
94+
let parts = ($user_input | split row " ")
95+
let verb = ($parts | get 0 | str downcase)
96+
# Use `get` and `default` for robust noun handling (handles commands with no noun)
97+
let noun = if ($parts | length) > 1 { $parts | get 1 | default "" | str downcase } else {""}
98+
99+
# grab the current location once
100+
let loc_data = ($map | get $player_state.location)
101+
102+
# Handle the action directly in the loop
103+
match $verb {
104+
"quit" | "exit" => {
105+
print "Goodbye!"
106+
exit
107+
}
108+
"look" | "l" => {
109+
describe_location $player_state $map
110+
}
111+
"go" | "move" => {
112+
if ( $noun not-in ( $loc_data.exits | columns )) {
113+
print "You can't go that way."
114+
} else {
115+
let new_loc = ($loc_data.exits | get $noun)
116+
$player_state.location = $new_loc
117+
print $"You go ($noun)."
118+
print "" # line break
119+
describe_location $player_state $map
120+
}
121+
}
122+
"inventory" | "i" => {
123+
if ($player_state.inventory | is-empty) {
124+
print "Your inventory is empty."
125+
} else {
126+
print $"You are carrying: ($player_state.inventory | str join ', ')"
127+
}
128+
}
129+
"take" | "get" => {
130+
if ($loc_data.items | find -r $noun).0? != null {
131+
# Just add to inventory
132+
$player_state.inventory = ($player_state.inventory | append $noun)
133+
print $"You take the ($noun)."
134+
} else {
135+
print $"There is no '($noun)' here to take."
136+
}
137+
}
138+
"talk" => {
139+
if ( $noun not-in ( $loc_data.npcs | columns )) {
140+
print $"You cannot talk to ($noun) " } else {
141+
if ($loc_data.npcs | get $noun) == null {
142+
print $"There is no one called '($noun)' here to talk to."
143+
} else {
144+
print ($loc_data.npcs | get $noun)
145+
}
146+
}
147+
}
148+
"search" => {
149+
if $noun == "tree" and $player_state.location == "market" {
150+
if $player_state.found_cat {
151+
print "You already found Mittens!"
152+
} else {
153+
print ($loc_data | get special)
154+
$player_state.found_cat = true
155+
print "\nMittens purrs happily in your arms. You should return her to Olivia!"
156+
}
157+
} else {
158+
print "You search around, but find nothing special."
159+
}
160+
}
161+
"give" => {
162+
if $noun == "cat" and $player_state.location == "village_square" and $player_state.found_cat {
163+
if ('olivia' in ($loc_data.npcs | columns)) {
164+
print "\nYou give Mittens back to Olivia. She is overjoyed!"
165+
print "'Oh, thank you, thank you!' she cries, hugging her cat. 'You saved her!'"
166+
print "\n*** Congratulations! You completed the adventure! ***"
167+
exit
168+
} else {
169+
print "Olivia isn't here right now."
170+
}
171+
} else if $player_state.found_cat and $noun == "cat" {
172+
print "You need to be in the village square to give the cat back to Olivia."
173+
} else {
174+
print "You don't have that to give, or you can't give it here/now."
175+
}
176+
}
177+
_ => {
178+
print "I don't understand that command. Try: go, look, take, inventory, talk, search, give, quit."
179+
}
180+
}
181+
print "" # Add a newline for better readability
182+
}

0 commit comments

Comments
 (0)