search() - Search Function

Summary

The search() function represents investigation actions where a character examines a location, object, or area for items, clues, or secrets. It’s a specialized action type that often combines with intention= (what you’re looking for) and result() (what you find) to create complete investigation narratives.

Syntax

search("location_or_object")
search("target", intention="goal")
search("target") ~ "method"
search("target", intention="goal") ~ "method" -> result()

Parameters

ParameterTypeRequiredDescription
targetString or EntityYesWhat is being searched (location, object, person)
intentionPropertyOptionalWhat the searcher hopes to find
mannerModifier (~)OptionalHow the search is conducted

Description

The search() function is NDL’s primary construct for investigation actions. It represents the deliberate act of looking for something, whether:

  • Searching a location for items or clues
  • Examining an object for hidden features
  • Investigating a scene for evidence
  • Looking for a specific item or person

Unlike general do($"entity", "search") actions, the search() function is a specialized keyword that signals investigation mechanics to both the parser and the LLM.

Real Examples

Basic Search with Intent and Result

search("pile of wood", intention="find makeshift club") -> result()

Actual LLM Output (Gemma 2 9B IT):

Your fingers sift through the rough-hewn logs, searching for something sturdy and manageable. You find it - a thick, gnarled branch, its ends still bearing the marks of the axe. It's not much, but it's better than nothing.

Analysis:

  • search("pile of wood") - specifies what is being searched
  • intention="find makeshift club" - specifies what is sought
  • -> result() - indicates success (finding something appropriate)
  • LLM generates tactile, physical narration of the search and discovery

Search with Manner

search("room") ~ "carefully"

LLM Output:

You carefully search the room, methodically checking every corner, drawer, and surface. Nothing escapes your thorough examination.

Analysis: The manner ~ "carefully" affects how the search is narrated (methodical, thorough vs hasty, cursory).

Common Patterns

Search with Intention

search("debris", intention="find survivors")
search("desk", intention="find secret compartment")
search("corpse", intention="determine cause of death")

The intention guides what the searcher is looking for, affecting both game logic and narration.

Search with Result

search("room") -> result("find_hidden_door")
search("chest") -> result("find_nothing")
search("bookshelf") -> result("discover_secret_passage")

The result specifies what was found (or not found).

Search with Method

search("crime scene") ~ "forensic tools"
search("area") ~ "magical detection"
search("ruins") ~ "torch light"

The manner specifies tools or approach used in the search.

Complete Search Chain

search("ancient tome", intention="find ritual spell") ~ "carefully" -> result("success") -> gain("spell_knowledge")

Full context: what, why, how, outcome, and consequence.

Search Targets

Locations

search("room")
search("forest clearing")
search("cave entrance")
search("crime scene")
search("battlefield")

Objects

search("desk")
search("chest")
search("corpse")
search("bookshelf")
search("pile of debris")

Abstract Targets

search("memories")
search("archives for records")
search("crowd for familiar face")

Entity References

search($"suspicious_crate")
search($"current_location")

Intention in Searches

The intention property is particularly important for search actions:

search("room", intention="find key")

LLM Focus: Narration focuses on looking for key-like objects.

General Investigation

search("room", intention="find anything useful")

LLM Focus: Broader narration, examining various items.

Clue Hunting

search("scene", intention="determine what happened")

LLM Focus: Looking for contextual clues and evidence.

search("tavern", intention="find informant")

LLM Focus: Scanning for people matching description.

Manner in Searches

The manner modifier affects search approach and narration:

Speed/Care

search("room") ~ "hastily"  # Quick, might miss things
search("room") ~ "meticulously"  # Slow, thorough
search("room") ~ "carefully"  # Balanced approach

Tools/Methods

search("area") ~ "with hands"  # Tactile search
search("area") ~ "with torch"  # Visual search with light
search("area") ~ "with divination magic"  # Magical detection
search("area") ~ "with metal detector"  # Specialized tool

Stealth/Obviousness

search("office") ~ "discreetly"  # Don't disturb, hide search
search("office") ~ "thoroughly disrupting everything"  # Obvious, messy

Result Outcomes

Success

search("location") -> result("find item")
search("location") -> result("discover secret")
search("location") -> result("locate clue")

Failure

search("location") -> result("find nothing")
search("location") -> result("miss hidden item")
search("location") -> result("false_lead")

Complications

search("trapped chest") -> result("trigger trap")
search("guarded room") -> result("alert guards")
search("cursed object") -> result("activate curse")

Partial Success

search("complex scene") -> result("partial_information")
search("damaged records") -> result("fragmentary_clues")

Usage Examples by Genre

Fantasy RPG

search("dragon's hoard", intention="find magic sword") ~ "carefully avoiding treasure guardian" -> result("find legendary blade")

Mystery/Detective

search("crime scene", intention="find murder weapon") ~ "forensic examination" -> result("discover fingerprints")

Horror

search("abandoned house", intention="find way out") ~ "frantically" -> result("trigger something sinister")

Sci-Fi

search("alien ruins", intention="find technology") ~ "scanner device" -> result("detect energy signature")

Heist

search("security office", intention="find vault codes") ~ "quickly and quietly" -> result("success") -> gain("access codes")

Integration with Game Systems

Skill Check Integration

search("hidden door") -> roll("perception") -> result("success") -> reveal($"secret_passage")

The search triggers a perception check, which determines the result.

Difficulty Modifiers

# Easy search
search("obvious location") -> result("immediate_find")
 
# Hard search
search("expertly hidden compartment") ~ "thorough search" -> roll("investigation", difficulty=20) -> result("barely_find")

Time Consumption

search("large library", intention="find specific book") ~ "methodically" -> wait("2 hours") -> result("find tome")

Searches can take time, represented by wait().

Inventory Interaction

search("chest") -> result("find treasure") -> gain_items(["gold", "potion", "gem"])

Successful searches add items to inventory.

Advanced Patterns

search("room") -> result("notice strange bookshelf")
-> do($"examine bookshelf") -> result("find mechanism")
-> do($"activate mechanism") -> reveal($"secret_passage")

Search leads to discovery, which leads to further investigation.

search($"party", "battlefield", intention="find survivors") ~ "spreading out" -> result("find wounded soldier")

Multiple characters searching together.

search("room", intention="find evidence") -> result("failure")
-> wait("think about it")
-> search("room", intention="look in unusual places") ~ "different approach" -> result("success")

Initial failure, followed by new strategy.

search($"player", "ancient site") -> roll("investigation") -> result("15")
-> search($"rival", "ancient site") -> roll("investigation") -> result("18")
-> system_response("The rival finds the artifact first")

Multiple parties searching, one succeeds first.

Implementation Notes

Parsing Search Actions

import re
 
def parse_search(ndl_string):
    """Parse search function from NDL."""
    match = re.search(r'search\("([^"]+)"(?:,\s*intention="([^"]+)")?\)', ndl_string)
 
    if match:
        return {
            'target': match.group(1),
            'intention': match.group(2) if match.group(2) else None
        }
    return None
 
# Usage
search_data = parse_search('search("room", intention="find key")')
# Returns: {'target': 'room', 'intention': 'find key'}

Generating Search NDL

def create_search(target, intention=None, manner=None, result=None):
    """Generate search NDL."""
    parts = []
 
    # Base search
    if intention:
        parts.append(f'search("{target}", intention="{intention}")')
    else:
        parts.append(f'search("{target}")')
 
    # Manner
    if manner:
        parts[-1] += f' ~ "{manner}"'
 
    # Result
    if result:
        parts.append(f'result("{result}")')
 
    return ' -> '.join(parts)
 
# Usage
ndl = create_search("chest", intention="find gold", manner="carefully", result="success")
# Returns: 'search("chest", intention="find gold") ~ "carefully" -> result("success")'

Backend Search Logic

def process_search(character, target, intention, difficulty):
    """Backend determines search outcome."""
    # Modify difficulty based on conditions
    if intention and intention in target.contains:
        difficulty -= 5  # Easier if you know what you're looking for
 
    # Roll perception/investigation
    roll = dice.d20() + character.perception_bonus
 
    if roll >= difficulty:
        # Success: find something
        if intention:
            item = target.find_specific(intention)
        else:
            item = target.find_random()
 
        return {
            'result': 'success',
            'found': item,
            'ndl': f'search("{target.name}", intention="{intention}") -> result("find {item.name}")'
        }
    else:
        # Failure: find nothing
        return {
            'result': 'failure',
            'found': None,
            'ndl': f'search("{target.name}", intention="{intention}") -> result("find_nothing")'
        }

Anti-Patterns

❌ Missing Target

search()  # What is being searched?

Problem: No target specified.

Better:

search("room")
result("find key")  # How was it found?

Problem: Result without the search action.

Better:

search("desk") -> result("find key")

❌ Contradictory Intention and Result

search("room", intention="find weapon") -> result("find spell scroll")

Problem: Found something different from what was sought (unless that’s the point).

Better:

search("room", intention="find weapon") -> result("find sword")
# or
search("room", intention="find weapon") -> result("find spell scroll instead")
search("room where John hid the ancient ruby key behind the loose floorboard near the fireplace")

Problem: Too much detail makes search trivial.

Better:

search("room", intention="find hidden key") ~ "checking floor near fireplace"

Best Practices

1. Specify Target Clearly

✓ search("suspect's office")
✗ search("place")

2. Use Intention for Focused Searches

# Specific goal
search("library", intention="find forbidden tome")
 
# General investigation
search("library")

3. Match Manner to Context

# Stealth scenario
search("guard room") ~ "quietly"
 
# Archaeology
search("ruins") ~ "archaeological methods"
 
# Emergency
search("rubble") ~ "frantically"

4. Always Provide Result for Important Searches

✓ search("crime scene") -> result("find murder weapon")
✗ search("crime scene")  # LLM might invent what's found

5. Chain Search with Consequences

search("desk") -> result("find letter") -> reveal("conspiracy") -> trigger($"plot_advancement")

Design Philosophy

Player Agency

Searches represent player choice to investigate:

Player: "I search the room"
Backend: Determines what is found (if anything)
NDL: search("room") -> result("find clue")
LLM: Narrates the search and discovery

Systematic Investigation

Search is a game mechanic, not narrative fiat:

  • Player initiates search
  • Backend applies rules (perception check, difficulty, etc.)
  • Result is deterministic
  • LLM provides flavor text

Reward for Thoroughness

Searches reward player attentiveness:

# Player explicitly searches: finds item
search("bookshelf") -> result("find secret button")
 
# Player doesn't search: misses item
# (item remains hidden until searched)

See Also


Key Takeaway

The search() function is NDL’s specialized construct for investigation actions. By explicitly specifying what is being searched, why (intention), how (manner), and what is found (result), it enables deterministic, game-logic-driven investigation while allowing the LLM to generate evocative narration of discovery, examination, and revelation. This prevents the LLM from inventing items or clues that don’t exist in the game world.