1
Fork 0
mirror of https://github.com/RGBCube/nu_scripts synced 2025-07-30 13:47:46 +00:00

add games folder and simple game demonstrating nu code concepts (#1100)

I'd like to contribute this game.

The code exemplifies several nu concepts.
This commit is contained in:
Lennart Kiil 2025-04-23 01:10:59 +02:00 committed by GitHub
parent a515c4217e
commit 67e74c5657
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 214 additions and 0 deletions

View file

@ -0,0 +1,32 @@
# Humlespring Text Adventure
A simple text adventure game written in Nushell.
The game utilizes Nushell's functional programming capabilities in managing the game map and player state.
* **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.
* **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.
* **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.
## How to Play
1. Make sure you have Nushell installed (`https://www.nushell.sh/`).
2. Save the script as `script.nu`.
3. Run the game by executing `nu script.nu` in your terminal.
## Commands
* `look` or `l`: Describe the current location.
* `go` or `move` [direction]: Move in the specified direction (e.g., `go north`).
* `take` or `get` [item]: Pick up an item (e.g., `take leaflet`).
* `inventory` or `i`: View your inventory.
* `talk` [npc]: Talk to a non-player character (e.g., `talk alexander`).
* `search` [object]: Search an object in the current location (e.g., `search tree`).
* `give` [item]: Give an item to an NPC (e.g., `give cat`).
* `quit` or `exit`: Quit the game.
## Goal
Olivia has lost her cat, Mittens. Find Mittens and return her to Olivia in the village square.

182
games/humlespring/script.nu Executable file
View file

@ -0,0 +1,182 @@
#!/usr/bin/env nu
# --- Game Data ---
# Define the locations in the village
let map = {
village_square: {
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."
exits: { north: "tavern", east: "market", west: "alexander_house" }
items: ["leaflet"]
npcs: {
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.'"
}
},
tavern: {
desc: "The 'Leaky Mug' tavern. It smells faintly of ale and sawdust. The only exit is south."
exits: { south: "village_square" }
items: ["mug"]
npcs: {}
},
market: {
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."
exits: { west: "village_square" }
items: [] # Mittens isn't an 'item' to take, but is found here
npcs: {}
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
},
alexander_house: {
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."
exits: { east: "village_square" }
items: ["watering_can"]
npcs: {
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?'"
}
}
}
# --- Game State ---
# Player's current status (mutable)
mut player_state = {
location: "village_square" # Starting location ID
inventory: []
found_cat: false
}
# --- Helper Functions ---
# Describe the current location
def describe_location [state: record, map: record] {
let current_loc_id = $state.location
let loc_data = ($map | get $current_loc_id)
# Description
print $loc_data.desc
# Items
if not ($loc_data.items | is-empty) {
print $"You see here: ($loc_data.items | str join ', ')"
}
# NPCs
let npcs = ($loc_data.npcs | columns)
if not ($npcs | is-empty) {
print $"People here: ($npcs | str join ', ')"
}
# Exits
let exits = ($loc_data.exits | columns | str join ', ')
print $"Exits are: ($exits)"
# Check for winning condition trigger
if $current_loc_id == "market" and not $state.found_cat {
print "\nHint: Maybe you should 'search tree'?"
}
}
# Handle player actions are now inlined in the main loop below
# --- Main Game Loop ---
print "Welcome to Humlespring!"
print "-------------------------"
describe_location $player_state $map
loop {
# Get player input
let user_input = (input "> " | str trim)
# Skip empty input
if $user_input == "" { continue }
# Parse input (simple verb-noun)
let parts = ($user_input | split row " ")
let verb = ($parts | get 0 | str downcase)
# Use `get` and `default` for robust noun handling (handles commands with no noun)
let noun = if ($parts | length) > 1 { $parts | get 1 | default "" | str downcase } else {""}
# grab the current location once
let loc_data = ($map | get $player_state.location)
# Handle the action directly in the loop
match $verb {
"quit" | "exit" => {
print "Goodbye!"
exit
}
"look" | "l" => {
describe_location $player_state $map
}
"go" | "move" => {
if ( $noun not-in ( $loc_data.exits | columns )) {
print "You can't go that way."
} else {
let new_loc = ($loc_data.exits | get $noun)
$player_state.location = $new_loc
print $"You go ($noun)."
print "" # line break
describe_location $player_state $map
}
}
"inventory" | "i" => {
if ($player_state.inventory | is-empty) {
print "Your inventory is empty."
} else {
print $"You are carrying: ($player_state.inventory | str join ', ')"
}
}
"take" | "get" => {
if ($loc_data.items | find -r $noun).0? != null {
# Just add to inventory
$player_state.inventory = ($player_state.inventory | append $noun)
print $"You take the ($noun)."
} else {
print $"There is no '($noun)' here to take."
}
}
"talk" => {
if ( $noun not-in ( $loc_data.npcs | columns )) {
print $"You cannot talk to ($noun) " } else {
if ($loc_data.npcs | get $noun) == null {
print $"There is no one called '($noun)' here to talk to."
} else {
print ($loc_data.npcs | get $noun)
}
}
}
"search" => {
if $noun == "tree" and $player_state.location == "market" {
if $player_state.found_cat {
print "You already found Mittens!"
} else {
print ($loc_data | get special)
$player_state.found_cat = true
print "\nMittens purrs happily in your arms. You should return her to Olivia!"
}
} else {
print "You search around, but find nothing special."
}
}
"give" => {
if $noun == "cat" and $player_state.location == "village_square" and $player_state.found_cat {
if ('olivia' in ($loc_data.npcs | columns)) {
print "\nYou give Mittens back to Olivia. She is overjoyed!"
print "'Oh, thank you, thank you!' she cries, hugging her cat. 'You saved her!'"
print "\n*** Congratulations! You completed the adventure! ***"
exit
} else {
print "Olivia isn't here right now."
}
} else if $player_state.found_cat and $noun == "cat" {
print "You need to be in the village square to give the cat back to Olivia."
} else {
print "You don't have that to give, or you can't give it here/now."
}
}
_ => {
print "I don't understand that command. Try: go, look, take, inventory, talk, search, give, quit."
}
}
print "" # Add a newline for better readability
}