A tiny Python roguelike prototype built over a weekend sprint, following the excellent Roguelike Tutorial (tcod, 2019).
Tutorial reference: https://rogueliketutorials.com/tutorials/tcod/2019/part-1/
I wanted to develop a quick example of the roguelike loop (map gen -> input -> field of view > combat > inventory) without ceremony. This repo is intentionally minimal but structured so you can extend it.
- Python 3.10 to 3.12 recommended
- macOS, Linux, or Windows
- A virtual environment (
python -m venv .venv
)
git clone https://github.com/<your-username>/trytrytry.git
cd trytrytry
python -m venv .venv && source .venv/bin/activate # on Windows: .venv\Scripts\activate
pip install -U pip
pip install -r requirements.txt
python -m trytrytry
# or, if you have a top-level script:
python main.py
A window appears with a player @
on a dungeon map. Have fun and try, try, try.
- Arrows / HJKL / Numpad: Move
- .` (period): Wait a turn
- ESC / q: Quit
If you followed the tutorial verbatim, movement keys may live in
input_handlers.py
. Adjust here if you remap.
trytrytry/
├─ trytrytry/ # game package (importable; run via `-m trytrytry`)
│ ├─ __init__.py
│ ├─ main.py # entry point (sets up context & game loop)
│ ├─ engine.py # turn loop, rendering hooks
│ ├─ input_handlers.py # keyboard handling
│ ├─ entity.py # Actor/Item base classes
│ ├─ components/ # (optional) Fighter, Inventory, AI, etc.
│ ├─ game_map.py # map + FOV
│ ├─ procgen.py # dungeon generation
│ ├─ render_functions.py # draw tiles/entities/UI
│ └─ data/ # tilesets, fonts, config
├─ tests/ # basic smoke tests
├─ LICENSE
├─ README.md
├─ requirements.txt
├─ pyproject.toml # (optional) packaging & tooling config
└─ .gitignore
Your current
trytrytry/source
can become the package root (trytrytry/
); move files into it and add__init__.py
sopython -m trytrytry
works cleanly.
- [tcod / libtcod] for console rendering & input
- Python standard library (dataclasses / typing)
- (Optional)
numpy
for FOV & map math
- Basic combat log and HP bars
- Simple item system (healing potion)
- Save/Load (pickle or JSON for game state)
- Minimal UI polish (help screen; keybinding overlay)
- One smoke test (map gen + walkable tile assertion)
- GitHub Actions: run tests on PRs
Stretch:
- Field-of-view shadows; exploration memory ("fog of war")
- Simple AI (chase/attack)
- Inventory + equipment slots
- Multiple dungeon floors + stairs
Add a docs/
folder with screenshot.png
or a tiny GIF and embed here.

- Built from/alongside the Roguelike Tutorial (tcod, 2019) by the rogueliketutorials.com community.
- New code here © 2025 Eric Janusson, released under the MIT License (see
LICENSE
). - If you copied tutorial code verbatim, keep original attribution comments where present.
PRs welcome. Please:
- Keep functions small and testable.
- Prefer pure logic in components; keep rendering thin.
- Add/adjust a test for any behavior change.
pip install -r requirements-dev.txt
pytest -q
ruff check .
Why python -m trytrytry
?
Running the package avoids import gotchas and makes packaging/tests easier later.
Why tcod? It's a classic roguelike stack with great docs and a batteries-included feel for rendering & input.
tcod>=13,<19
numpy>=1.24,<3
If your code doesn’t use
numpy
, drop it. If it does use type hints or dataclasses on 3.10+, you don’t need extra deps. Pin more tightly if you want fully reproducible builds.
Optionally add a dev file:
# requirements-dev.txt
pytest>=8
ruff>=0.5
[project]
name = "trytrytry"
version = "0.1.0"
description = "Weekend Python roguelike prototype (tcod)."
readme = "README.md"
requires-python = ">=3.10"
dependencies = [
"tcod>=13,<19",
"numpy>=1.24,<3",
]
[project.scripts]
trytrytry = "trytrytry.main:main"
[tool.ruff]
line-length = 100
target-version = "py311"
[tool.pytest.ini_options]
addopts = "-q"
pythonpath = ["."]
This lets users pip install -e .
and then run trytrytry
directly.
If you don’t already have one, this tiny scaffold will run in a tcod window and call your engine:
# trytrytry/main.py
from __future__ import annotations
import tcod
SCREEN_WIDTH, SCREEN_HEIGHT = 80, 50
WINDOW_TITLE = "trytrytry"
def main() -> None:
tileset = tcod.tileset.load_tilesheet(
"trytrytry/data/dejavu10x10_gs_tc.png", 32, 8, tcod.tileset.CHARMAP_TCOD
)
with tcod.context.new_terminal(
SCREEN_WIDTH,
SCREEN_HEIGHT,
tileset=tileset,
title=WINDOW_TITLE,
vsync=True,
) as context:
console = tcod.Console(SCREEN_WIDTH, SCREEN_HEIGHT, order="F")
while True:
console.clear()
console.print(1, 1, "hello from trytrytry (@ to move later)")
context.present(console)
for event in tcod.event.wait():
if isinstance(event, tcod.event.Quit):
raise SystemExit
if __name__ == "__main__":
main()
Add a tileset PNG to trytrytry/data/
(the tutorial uses DejaVu or a cp437 tileset). You can swap this for your current engine loop.
def test_imports():
import trytrytry # noqa: F401
- Description & topics: "python, roguelike, tcod, libtcod, tutorial, game-dev"
- Default branch:
main
- Actions: add a simple CI
.github/workflows/ci.yml
:
(For weekend code, allowing tests to soft-fail is fine.)