Metadata-Version: 2.4
Name: dcssvox
Version: 0.10.2
Summary: Hands-free voice control and narration for Dungeon Crawl Stone Soup, built for Termux.
Author-email: Joel Taylor <joeltaylor734@gmail.com>
License-Expression: MIT
Project-URL: Homepage, https://github.com/joeltco/dcssvox
Project-URL: Repository, https://github.com/joeltco/dcssvox
Keywords: dcss,crawl,roguelike,voice,accessibility,termux,hands-free
Classifier: Development Status :: 3 - Alpha
Classifier: Environment :: Console
Classifier: Intended Audience :: End Users/Desktop
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: Games/Entertainment
Classifier: Operating System :: POSIX :: Linux
Classifier: Operating System :: Android
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Provides-Extra: llm
Requires-Dist: anthropic>=0.40; extra == "llm"
Dynamic: license-file

# dcssvox

Hands-free voice control and narration for **Dungeon Crawl Stone Soup**, built
for playing on a phone under Termux with headphones and a mic.

You say what to do; it reads back what happened. No keyboard, no screen-staring.

```
you:  "explore"
it:   "You encounter a bat."
you:  "attack east"
it:   "You hit the bat. Health 17 of 19."
```

## Why it works

DCSS is turn-based and lives in a terminal, which makes it close to the ideal
target for voice: nothing has a reaction window, and both the screen and the
input are trivially machine-accessible. dcssvox runs crawl inside `tmux` and
drives it from outside — keystrokes in via `send-keys`, the rendered screen out
via `capture-pane`. crawl itself is never patched.

Three design rules, all aimed at someone whose hands are busy:

* **Never stuck.** There is no keyboard to bail out with, so every modal state
  is recognised and has a spoken way out. `recover()` walks crawl back to
  normal play without needing to classify the state correctly.
* **Never surprised.** Irreversible actions (quaff, drop, read, take off) are
  read back and wait for "yes" before a key is sent.
* **Never guessing.** Item and spell names are matched against the letters
  crawl *just printed on screen*, not a hand-maintained table. If nothing
  matches convincingly it says so instead of pressing something.

Speech priority comes from crawl's own message channels: dcssvox pins each of
the 30 channels to a colour in its generated rc, so the SGR code on a message
row is an exact, language-independent priority signal. "An endoplasm is
nearby!" is urgent; "The endoplasm barely misses you." is not read at all.

## Install (Termux)

```sh
pkg install crawl tmux git cmake clang make
python -m pip install dcssvox
dcssvox doctor          # tells you what's missing
```

Character creation is done by voice too — just say the species, background and
weapon ("minotaur", "berserker", "mace"). A character name is supplied for you,
because crawl's name prompt is free text and has no voice path.

For speech output also install the **Termux:API app** (from F-Droid) plus
`pkg install termux-api`. For voice input:

```sh
dcssvox setup           # builds whisper.cpp + fetches tiny.en (one time)
dcssvox play
```

Watch the game on screen any time with `tmux attach -t dcssvox`.

## Without a mic

`dcssvox type` runs the exact same loop with typed input — useful for testing
the command vocabulary, and it works on any Linux box.

## Vocabulary

| Say | Does |
|---|---|
| explore / keep going | auto-explore until something happens |
| north, go east, southwest | move one square |
| run north | travel in a direction |
| **attack** / fight / kill it | autofight the nearest enemy — no direction needed |
| fire / shoot | autofire your quivered action at the nearest enemy |
| attack east | attack in a specific direction |
| rest / wait | long rest / pass one turn |
| downstairs / travel to the stairs | descend / auto-travel |
| what's around | names, direction and distance of everything in view |
| status / where am i | health, magic, statuses, location |
| inventory | read what you're carrying |
| quaff curing / read blinking / wield mace | menu actions by name |
| cast magic dart | cast, aim and fire in one command |
| cast magic dart at the orc | aim at a named target |
| stop / cancel | interrupt, back out of anything |
| again | repeat the last thing said |
| yes / no | answer crawl's own questions |
| strength / intelligence / … | answer a level-up prompt by name |
| what should I do | situation summary (needs `--llm`) |
| **skills** / **spells** / **learn a spell** | open the screen and read the options aloud |
| abilities / my god / character | same, for those screens |

Anything outside the vocabulary is **dropped in silence** rather than guessed
at — with an always-on mic that refusal is the whole safety model.

## Nothing fails quietly

The rule that shapes most of this: a command that silently does nothing is a
dead end when you cannot look at the screen. So every command that should take
a turn is checked, and when it doesn't, dcssvox says why *and what to do
instead*:

```
"can't rest, kobold northeast. say attack northeast, or go southwest"
"too hurt to autofight, health 9 of 27. say rest"
"nothing left to explore. say downstairs to go deeper"
"no stairs found yet. say explore first"
"blocked going southeast"
```

The direction matters more than it looks. Below half health crawl refuses to
autofight, and with a monster visible it refuses to rest — so both commands you
know are declined at once, and "attack a direction" is useless advice unless
something tells you *which*. dcssvox works the bearing out from the monster
list and the map.

Questions that block the game are read aloud with their options, including
crawl's level-up prompt, and answered by name:

```
crawl: Increase (S)trength, (I)ntelligence, or (D)exterity?
you:   "strength"
```

## Optional: the Claude tier

If you already have Claude Code installed, this costs nothing extra — it runs
on your existing subscription:

```sh
dcssvox play --llm          # uses the `claude` CLI if present
```

Otherwise it can use the API, which is billed separately:

```sh
pip install 'dcssvox[llm]'
export ANTHROPIC_API_KEY=...
dcssvox play --llm
```

`dcssvox doctor` reports which backend it found.

Everything ordinary still runs on the local grammar — offline, instant, free.
Claude (Haiku 4.5) is consulted in exactly three places:

* **Long item and spell names.** "Borgnjor's Vile Clutch" is what a recogniser
  mangles and what token matching handles badly. Only fires when local matching
  is weak or torn.
* **Phrases outside the vocabulary.** "get me out of here", "chug a heal". With
  no keyboard, an unparsed command is a dead end, so this is a safety net more
  than a convenience.
* **"what should I do"** — two sentences on your situation.

It is grounded and cannot invent: the letter enum is built from the letters
crawl just printed, the command enum is built from the local grammar, and its
answer is re-parsed by that same grammar before anything is pressed. Every
failure — no key, timeout, network drop — falls back to the local grammar
rather than wedging the game. Irreversible actions are still confirmed aloud
whether you said them or Claude inferred them.

## Status

Alpha. The deterministic loop is verified end-to-end against crawl 0.34.1.
The whisper.cpp listener and Termux TTS are written but not yet exercised on a
real device.

## License

MIT
