Skip to main content

Vimny

A dungeon crawler where the dungeons are text buffers and every puzzle is solved with real Vim commands.

The floor is made of characters. Walls are the ends of lines. A door opens for the word you cut out of the floor and carried there.

Every room has a keystroke budget to enforce efficiency, and a par — the cheapest route that exists. Finishing wins the room; finishing at par means you found the way a fluent Vim user would have and fully learned the lesson taught at each dungeon.

A terminal playing The Character Cataracts. The dungeon floor is written text — "Most files you encounter will be scribed in letters so you can jump quite easily to anything you can type!" — split by rivers of water. The cursor hops from character to character with f, F, t and T, aims at the wrong letter, presses u to snap back (and watches the keystroke count go down again), then reaches the exit at 21 keystrokes against a par of 19.

The Character Cataracts, the fourth level. f/F/t/T jump to a character you can see; the water is impassable, so you aim rather than walk. This run misfires once, takes it back with u, and clears the room at 21 against a par of 19 — a win, but only one of the two stars.

How it works

The map is a buffer.

Editing reflows the line exactly as Vim does: insert, delete or paste and the rest of the row shifts along, and whatever gets shoved past a wall falls into the void. Cut a word out of the floor and the floor closes up behind it.

Dungeon concept Vim concept
Floor cells Characters
Character runs Words
Empty floor between runs Whitespace
Room row Line
Room wall End of line
Dungeon File

Keystroke budget: Every puzzle room displays a budget. Reaching the exit within it completes the room. The par is the minimum possible keystrokes using the level's taught commands — hitting par earns a second star. u (undo) returns budget; you can backtrack freely.

Note — u undoes motions, which Vim's does not. In Vim, undo is for changes: it will not walk your cursor back from a 4j. In Vimny it will, refunding the keystrokes with it. This is a deliberate break, made so that a wrong turn is not something you pay for. Everything u does to edits is Vim's behaviour, undo stack and all.

Note — par is not the absolute minimum on search levels. On levels that use search (/, ?), par is computed assuming you type the full search term the level highlights (e.g. /cipher<CR>). Because a search pattern only needs enough characters to land uniquely on the target, an expert can type a shorter prefix (e.g. /cip<CR>) and finish under par. This is intentional: par assumes you play the puzzle out rather than guess its answer.

Terrain: Levels use terrain to make a particular Vim command the only good answer, rather than merely the intended one.

  • Void runes — holes in the floor. Landing on one costs 1 HP, but count motions pass through them silently: only the final landing cell bites. So a void rune bars stepping, never jumping.
  • Water — impassable on foot; you have to jump over it.
  • Fogged water — impassable on foot and obscures your vision, so a blind jump like $ won't clear it. You have to aim at a character you can see.

Play it

Requirements are just Linux or macOS, Python 3.9+, and a terminal at least 80 columns wide. Windows is untested.

uvx vimny

Requires uv, which fetches its own Python.

pip install vimny
vimny

Requires pip and Python 3.9+.

Progress saves to ~/.Vimny/saves/<player>.json, one file per player.

Playing from a clone

To read or change the code, skip the packaged install:

git clone --depth 1 https://github.com/chkiss/Vimny.git
cd Vimny
pip install blessed
python3 main.py

--depth 1 skips 13 MB of history. Drop it if you intend to send a pull request.

Terminal size and Windows

The playfield grows with the window up to 189 columns — the overworld and The Archivist's Library use the extra width — and stops widening beyond that. 80 columns is the supported minimum.

Vimny reaches the console through blessed and jinxed, which ought to work in Windows Terminal (untested). Try pip install vimny and submit an issue if that leads to an error.

Levels

The main sequence of sixty levels is complete. The curriculum runs from hjkl through motions and counts, operators and text objects, visual mode, search and macros, registers, marks and jumps, to Ex commands (:s, :g) — with boss levels that make you use the lot at once, and a bonus wing for the corners of Vim that don't fit a straight line.

Each level teaches one thing and is built so that thing is the cheapest way through, not merely the intended one.

The full curriculum — all 60 levels
# Name Commands
0 The First Cave h j k l u :w :q :q!
1 The Line Halls ^ $ 0
1.1 The Reliquary x
2 The Counting Crypts [count] prefix
3 The Rune Halls w b e
4 The Character Cataracts f F t T
5 The Goblin Gauntlet ; , p
5.1 The Warden's Keep (boss)
6 The WORD Forge W B E
7 The Backward Vaults ge gE
8 The Lineheads G gg
9 The Screen Vault H M L
10 The Bracket Vaults %
12 The Runic Archives } {
13 The Sentence Corridor ) (
13.1 The Warden Surveyor (boss)
14 The Seekers' Labyrinth / ? n N * #
14.1 The Binder's Reliquary :h za :q
15 The Waypoint Sanctum m ' `
16 The Archivist's Library :set wrap :e! :w {file}
16.1 The Warden Pathfinder (boss)
17 The Operator's Vault d{m} dd
18 The Cipher Cell r D X
19 The Beacon Tiers y yy P
20 The Echo Vault .
20.1 The Warden Manifold (boss)
21 The Inscription Halls i a
22 The Change Annex c{m} cE cc s
23 The Change Extension S C Y
24 The Sculpting Chambers I A o O
25 The Overwrite Halls R
26 The Case Chambers ~ g~ gU gu
27 The Joiner's Gate J gJ
28 The Alignment Halls >> <<
29 The Indentation Sanctum >{m} <{m} =
29.1 The Warden Scrivener (boss)
30 The Sight Sanctum v {m} d/c/~
31 The Selection Halls V <C-v>
32 The Word Enclosure iw aw iW aW
33 The Bracket Enclosure i( a(
34 The Brace & Square Enclosure i[ a[ i{ a{
35 The Quote Enclosure i" a" i' a'
36 The Tag Enclosure it at
37 The Sentence Enclosure is as
38 The Paragraph Enclosure ip ap
38.1 The Grandmaster's Sanctum (boss)
39 The Spellwright's Forge :s/// :g &
40 The Culling Ledger :d _ :a,bd :v//d
41 The Shelving Room :m :t :> :<
42 The Refrain Vault & :&& :j :y
43 The Stair Rail + - _
44 The Last Reach g_ g* gi gp
45 The Buried Word g* n
46 The Wet Ink gi
47 The Hall of Echoes q @ "
48 The Gauntlet —
48.1 The Warden Eternal (boss)
R1 The Unnamed Hold "" y p
R2 The Named Vault "ay "by "aP "bP

Commands

The full command reference (also the hint-bar source) is vimny/render/vim_commands.md; this table mirrors it.

Show all commands
Command Effect
u undo
:w write (save)
:q quit
:q! quit without saving
h left
j down
k up
l right
0 line start
^ first non-blank
$ end of line
x delete char
[N]hjkl count move
w word start
b word back
e word end
f{c} jump to char
F{c} jump back to char
t{c} before next char
T{c} after prev char
; repeat
, reverse
p paste
W WORD start
B WORD back
E WORD end
ge word-end back
gE WORD-end back
G last line
gg first line
[N]G go to line N
H top of screen
M middle of screen
L bottom of screen
% match bracket
} next block
{ prev block
) next sentence
( prev sentence
v visual mode
v{m} d/c/~/p/r/J act on the selection
V select whole lines
<C-v> select a block
:h {name} open the Codex to a page
za unfold / fold a section
:q close the book
/{pat} search
?{pat} search back
n next match
N prev match
* search word
# search word back
m{a} set mark
`{a} to mark
'{a} to mark ↑
:set wrap wrap lines
:e! reload file
:w {file} save as
d{m} dd delete
c{m} change
cc change line
r{c} replace char
D delete to line end
X delete before cursor
c{m} cc change
s substitute
S substitute line
C change to end
Y yank line
y{m} yy yank
P paste before
. repeat change
i insert
a append
Esc exit insert
I insert at start
A append at end
o new line below
O new line above
R replace mode
~ toggle case
gU{m} uppercase
gu{m} lowercase
g~{m} toggle case
J join lines
gJ join, no space
>{m} indent
<{m} dedent
={m} apply the law
iw inner word
aw a word
iW inner WORD
aW a WORD
i( inner (
a( a ()
i[ inner [
a[ a []
i{ inner {
a{ a {}
i" inner "
a" a ""
i' inner '
a' a ''
it inner tag
at a tag
is inner sentence
as a sentence
ip inner paragraph
ap a paragraph
:s/old/new/ substitute
:%s//g substitute all
:g/pat/d global delete
& repeat last :s
:{n}d delete line n
:{a},{b}d delete range
:{r}v//d keep matching
:d _ cut, keep reg
q{a} record macro
@{a} play macro
@@ repeat macro
"{a} named reg
+ down, first word
{n}_ to line n below
g_ last non-blank
g* search substring
g# substring back
gi resume inserting
gp paste, cursor after

What Vim commands does Vimny not teach?

Vimny aims for Vim-faithfulness in everything it does implement, but some commands are deliberately out of scope:

  • Scrolling & viewport — zz zt zb <C-d> <C-u> <C-f> <C-b> <C-e> <C-y>: dungeons fit the screen; there is no viewport-scroll model (H/M/L are the only screen-relative commands).
  • U (vi's line-undo) — u and the redo scroll (<C-r>) cover the undo story; a third undo channel would complicate it for a key modern Vim users rarely reach for.
  • Window/tab/buffer management — Vimny is a single buffer by design; each dungeon is the file. On the roadmap, not in the curriculum.
  • Insert-mode editing keys — <C-w>, <C-u>, <C-o>, <C-r>{reg} are implemented and can be found as scrolls, but no level teaches them. They are priced to be free (<C-w> and <C-u> cost nothing; <C-r> charges per pasted character, exactly what typing the text would cost), so no puzzle can force them at par. Pricing them by keystroke would make a register paste cheaper than typing and hand every text-entry level a shortcut.
  • Completion, plugins, ex-mode scripting — out of scope.
  • NORMAL-mode Enter — a duplicate of +, which the Stair Rail already teaches.

Working on Vimny

Project layout
main.py                    Launcher — `python3 main.py` and nothing else
vimny/                     Everything importable lives under one package
  game.py                  Game loop, run_dungeon / run_overworld, the forge
  engine/
    world.py               Room, Dungeon, Entity, CharRun, CellType, Seal
    player.py              Player dataclass
    vim_parser.py          Keystroke → action dict
    command_guard.py       action_allowed — what the curriculum has taught yet
    motion.py              apply_motion, move_player, the fog laws
    operator.py            d y c p and friends — operator + text object
    text_object.py         iw aw i( a" ip … — the spans an operator acts on
    insert.py              i a I A o O s S, INSERT-mode editing
    reflow.py              Reflow editing primitives (insert/delete/join/ledge-build)
    visual.py              v V <C-v> — the selections
    search.py              / ? n N * # — Vim-regex search, matched per line
    substitute.py          :s :g :v & — ex substitute & global
    registers.py           named/unnamed registers, clip ↔ text
    macro.py               q @ — record and replay
    jumplist.py            <C-o> <C-i> — where you have been
    tape.py                The keystroke-tape notation (<Space> <CR> <Esc> <C-v>)
    budget.py              Budget tracking
  generation/
    dungeon_gen.py         build_dungeon_<slug> per level, par solvers
  content/
    levels.py              Level definitions (slug identity), known_commands(slug)
    scrolls.py             Scroll text + the scroll catalogue
    passwords.py           The password pools a fancy_door opens for
  render/
    renderer.py            Read-only dungeon view (no mutation)
    overworld.py           Read-only netrw overworld buffer
    title.py               Title screen and name prompt
    scroll_library.py      The scrolls you have collected
    remote_shelf.py        Browse the remote level shelf
    symbols.py             Every glyph the game draws, with width fallbacks
    hint_bar.py            Hint-bar text (reads vim_commands.md)
    vim_commands.md        Hint-bar text source (token → keys/desc)
  sharing/                 Levels as DATA — the authoring/sharing pipeline
    format.py              The level file format: parse, build, export
    validate.py            Every rule a level file must satisfy
    draft.py               The forge's in-progress level
    replay.py              Replay a keystroke tape through the real game loop
    jumpgolf.py            Does a line jump beat a tape's travel? (par audit)
    remote.py              The one place Vimny makes a network request
    submit.py              :submit — a prefilled pull request link for your browser
    cli.py                 python3 -m vimny.sharing — validate / audit / export / …
  save/
    save_manager.py        Progress I/O, layout save
  art/                     Wizard art, poems, the word pools
  tools/                   Offline audits (cheese, par)
tests/                     pytest test suite
packaging/                 Release runbook (PyPI)
docs/ARCHITECTURE.md       The canonical reference: architecture, laws, conventions
docs/AUTHORING.md          Writing a level, in the forge or in an editor
docs/SPEC.md               Design vision, UI spec, forward-looking notes
docs/LEVELS_PLAN.md        Design rubric + the levels not yet built
docs/blueprints/           Design docs for wings not yet built

Run the tests with python3 -m pytest, and python3 -m vimny.sharing audit to check every level's par against a replay of its own solution.

Writing your own levels

There are two ways in, and they produce the same thing — a level is a plain JSON file either way.

In the game — the forge. An authoring bench in the overworld under forge/, where a level is built by playing it: paint the room, place the text and the doors, then :record walks your own solution and captures it as the level's answer. The par comes from replaying that recording, so a level cannot ship claiming a route nobody has walked. The forge is admin-only — sign in with the player name admin to reach it. That name also unlocks every level and reveals each puzzle's solution, so use a separate save for authoring.

In a text editor. The format is documented, so you never have to use the forge:

python3 -m vimny.sharing export rune_halls mylevel.json   # start from a working level
python3 -m vimny.sharing validate mylevel.json            # check it
python3 -m vimny.sharing install  mylevel.json            # put it on your shelf

Either way, drop the file in ~/.Vimny/levels/ and it shows up in the overworld under community/.

The shelf. Community levels live at github.com/chkiss/vimny-levels — type :e remote in the overworld to browse what's there and install any of them without leaving the game. To add yours, open a pull request against that repo with your level file; it is checked by the same validator you can run yourself (python3 -m vimny.sharing validate mylevel.json), so if it passes locally it will pass there.

Two things worth knowing before you install a level someone else wrote:

  • A level is data, not code. Vimny reads the file and builds a room from it. Nothing in it is ever executed — that is what makes it safe to play, rather than anyone having vetted it.
  • Vimny goes online only when you ask it to, in one place. Nothing is fetched at startup, in the background, or on a timer: there is no phone-home, no telemetry, and no update check. The single exception is the remote shelf — type :e remote in the overworld and Vimny fetches a public index of community levels over HTTPS so you can browse and install them.

A community level's par comes from replaying the author's own solution, so it is labelled author's par — the cost of a route that definitely works, not a promise that no shorter one exists. Nobody gets to type in their own par or budget.

Full guide: docs/AUTHORING.md.

Upcoming features

  • The Registry — a bonus wing on the register family. The yank register ("0) and the black hole ("_) already work; still to come are the delete ring ("1–"9), the small-delete register ("-), the read-only registers (": ". "% "#), the expression register ("=), the system clipboard ("* "+), the search register ("/), and a boss to close it out. The first two levels are in.
  • Folds — za opens and shuts a section of the codex (:h), but nothing folds the dungeon itself. zf and a level built around it are the work.
  • Windows, tabs and buffers — Vimny is one buffer per dungeon today; multi-buffer play is on the roadmap.
  • Alternate levels — a community level may already declare itself a replacement for a shipped one, and the validator holds it to that slug's exact lesson. Nothing offers the swap to a player yet.

Design principles

  • Vim fidelity above all else. Commands behave exactly as they do in Vim.
  • Efficiency is enforced by par and budget. The keystroke budget makes Vim's core value proposition central, and the par encourages the player to strive for perfect execution efficiency.
  • Everything is a buffer. Dungeons are files; the overworld is a directory; :w, :q, :e are real mechanics.

See docs/ARCHITECTURE.md if you want to work on the engine — it is the canonical reference for the architecture, the laws the levels are held to, and the conventions. docs/LEVELS_PLAN.md is what's planned next, and docs/SPEC.md the design vision & UI.

License

Vimny is free software, licensed under the GNU General Public License v3.0 — see LICENSE for the full text.

Metadata

Release files for vimny 1.0.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for vimny 1.0.1
File Size Uploaded Provenance Actions
vimny-1.0.1.tar.gz 661.6 kB With provenance Details for vimny-1.0.1.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for vimny 1.0.1
File Interpreter ABI Platform Actions
vimny-1.0.1-py3-none-any.whl Python 3 none any Details for vimny-1.0.1-py3-none-any.whl

Total release size: 1.4 MB

Release files / vimny-1.0.1.tar.gz

Download URL vimny-1.0.1.tar.gz
Size 661.6 kB
Tags Source
SHA-256 checksum
How to use checksums
27ddb2c2c38a5ccd72003decec3cc774aa86a4b3ec442aff4475f7ed41bcaa3e
BLAKE2b-256 checksum
How to use checksums
51253f75f4e824ad2233852652ac71334957a092c25af605602da4de7b3e2b77
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 4, 2026.

Transparency log

Release files / vimny-1.0.1-py3-none-any.whl

Download URL vimny-1.0.1-py3-none-any.whl
Size 706.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
e64ca78d956a5f339def41bae77e0ef2a674547633e3a47a105c330b437237d5
BLAKE2b-256 checksum
How to use checksums
2da32c06dac7f8be89d6cc0ac77b66ea3ae8f7f91eb4f3db216ab0b804da6b54
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 4, 2026.

Transparency log

Security for vimny 1.0.1

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for vimny-1.0.1.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 4, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

1.0.1 This release

2 release files Full provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page