npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

zork-machine

v1.0.4

Published

Zork I implemented as a streaming state machine using Coremachine

Readme

Zork Coremachine

Zork I: The Great Underground Empire, reimplemented as a streaming state machine using Coremachine.

input stream  →  Coremachine (Zork)  →  output stream
  (text)           (state machine)        (state + text)

Architecture

The key design insight: Coremachine's state machine states represent game phases (playing, dead, victory), while all actual game world state lives in context — rooms, items, inventory, position, flags, score.

┌──────────────────────────────────────────────────────┐
│ Coremachine (Duplex Stream)                          │
│                                                      │
│  WRITE SIDE                      READ SIDE           │
│  { action: 'COMMAND',    →    { state: 'playing',    │
│    value: { text } }          context: {             │
│                                  output: '...',      │
│                                  currentRoom,        │
│                                  inventory,          │
│                                  score, ...          │
│                                }}                    │
│                                                      │
│  Machine States: playing | dead | victory            │
│  Context: entire game world (rooms, items, flags)    │
│  Hypercore: append-only history (undo/redo/replay)   │
└──────────────────────────────────────────────────────┘

Why this works

  • Stream-native: Coremachine is a Duplex stream. Pipe text commands in, pipe state changes out. Plugs directly into Cellery or any streaming pipeline.
  • Persistent: Every action is appended to Hypercore. Close the game, reopen it — state is restored. Time-travel with backward()/forward().
  • Distributed: Since it's Hypercore, the game state can sync between peers. Spectate a game, or build a shared adventure.
  • Renderer-agnostic: The output is structured data ({ state, context }). Render it as TUI, HTML, React, native — whatever.

Quick Start

Play without Hypercore (pure streams)

node example-pipeline.js

Play with Coremachine + Hypercore persistence

npm install coremachine corestore
node example-coremachine.js

Run tests

node test.js

Usage with Coremachine

import Corestore from 'corestore'
import { Zork } from './zork-machine.js'

const store = new Corestore('./zork-save')
const zork = Zork(store)

// Write commands in
zork.write({ action: 'COMMAND', value: { text: 'open mailbox' } })

// Read state changes out
zork.on('data', ({ state, context }) => {
  console.log(context.output) // "Opening the small mailbox reveals a leaflet."
  console.log(context.score) // 0
  console.log(context.inventory) // []
})

// Or pipe it
inputStream.pipe(zork).pipe(rendererStream)

Game Content

Rooms (~40 rooms)

  • Above ground: West/North/South/Behind House, Kitchen, Living Room, Attic, Forest areas, Clearing, Canyon View, Rocky Ledge, Canyon Bottom, End of Rainbow, Up a Tree
  • Underground: Cellar, Troll Room, East-West Passage, Round Room, Loud Room, Damp Cave, North-South Passage, Chasm, Deep Canyon, Dam, Dam Lobby, Maintenance Room, Reservoir, Gallery, Studio, South Temple
  • Maze: 5 maze rooms + dead end + grating room

Items & Treasures

  • Tools: Sword (glows near enemies), Lantern (limited battery), Rope, Knife, Wrench, Screwdriver, Air Pump, Rusty Key
  • Treasures: Jeweled Egg (5pts), Platinum Bar (10pts), Painting (6pts), Pot of Gold (10pts), Ivory Torch (6pts)
  • Interactables: Mailbox, Trophy Case, Rug/Trap Door, Pile of Leaves/Grating, Brown Sack, Bottle, Bird's Nest, Brass Bell, Candles, Black Book, Guidebook

Features

  • Natural language parser (handles aliases, multi-word items, prepositions)
  • Darkness system (lantern required underground, grue death)
  • Combat (troll fight with weapon damage system)
  • Sword glow near enemies
  • Container system (open/close, items inside items)
  • Trophy case scoring
  • Puzzle flags (rug, leaves, grating, etc.)
  • Easter eggs (xyzzy, plugh, hello sailor)

File Structure

zork-machine/
├── zork-machine.js        # Main export — Coremachine definition factory
├── rooms.js               # Room definitions (exits, descriptions, conditions)
├── items.js               # Item definitions (properties, handlers, treasures)
├── parser.js              # Natural language command parser
├── engine.js              # Game logic (movement, items, combat, etc.)
└── package.json

Extending

Add a room

// In rooms.js
'treasure-room': {
  name: 'Treasure Room',
  description: 'A room full of glittering treasures.',
  exits: { south: 'round-room' },
  items: ['diamond'],
  dark: true  // requires light source
}

Add an item

// In items.js
'diamond': {
  name: 'huge diamond',
  description: 'There is an enormous diamond here.',
  takeable: true,
  treasure: true,
  score: 15,
  onExamine: () => 'A flawless diamond the size of your fist.'
}

Add a parser alias

// In parser.js ITEM_ALIASES
diamond: 'diamond', gem: 'diamond'

Add a custom interaction

Items can have handler functions for any verb:

'magic-mirror': {
  name: 'magic mirror',
  onExamine: (ctx) => {
    if (ctx.inventory.includes('sword')) {
      return 'You see a brave adventurer wielding a glowing sword.'
    }
    return 'You see a scared-looking adventurer.'
  },
  onUse: (ctx, target) => {
    // Custom logic
  }
}

License

MIT

Game content inspired by Zork I: The Great Underground Empire by Infocom (1980). ZORK is a registered trademark of Infocom, Inc.