CCommons

I built a tiny dice-dungeon game in Python in one evening

40 lines, one bug that ate my whole hour, and what balancing a game taught me about probability.

Kabir Joshi11th Sample

27 Sept 2026 · 5 min read

Photo: Evan-Amos · Public domain · via Wikimedia Commons

Everyone says "build projects" but nobody tells you the project can be dumb and small. So here is my dumb small project: a text game where you walk through five rooms, a goblin is in every room, and everything is decided by dice. I wrote it in one evening with nothing except plain Python, and I learned more from it than from a month of watching tutorials.

The idea

The game has three things:

  1. You have HP (health) and gold.
  2. In each room a goblin appears and you choose fight or run.
  3. Dice decide what happens. After five rooms, if you're alive, you win.

That's the whole spec. I wrote it on the back of my chemistry rough copy before touching the keyboard, and that mattered. When I start coding without writing the spec I usually end up building something else.

The code

import random

def roll(sides=6):
    return random.randint(1, sides)

def encounter(hp, gold):
    print("A goblin jumps out!")
    while True:
        choice = input("fight or run? ").strip().lower()
        if choice in ("fight", "run"):
            break
        print("type fight or run, bro")

    if choice == "run":
        if roll() >= 4:
            print("you escaped!")
        else:
            hp -= 2
            print("tripped. -2 hp")
        return hp, gold

    mine, theirs = roll(), roll()
    print(f"you rolled {mine}, goblin rolled {theirs}")
    if mine >= theirs:
        gold += 3
        print("goblin down. +3 gold")
    else:
        hp -= 3
        print("ouch. -3 hp")
    return hp, gold

def main():
    hp, gold = 10, 0
    for room in range(1, 6):
        print(f"\n--- Room {room} | HP {hp} | Gold {gold} ---")
        hp, gold = encounter(hp, gold)
        if hp <= 0:
            print("you died with", gold, "gold")
            return
    print("\nYou escaped the dungeon with", gold, "gold!")

main()

If you copy this and run it, it works. It took me an hour to get here because of the first three bugs, which I'll talk about in a second.

The bugs, in the order they humiliated me

Bug 1: the infinite loop. My first version of the fight or run question didn't have the break, so after a valid answer it just asked again forever. I sat there typing "fight" like a fool and wondering why the goblin never appeared. while True: loops need an exit. Write the exit first, then the loop body.

Bug 2: forgetting .strip().lower(). My friend typed Fight with a capital F and a trailing space and the game said it was an invalid choice. Players do weird things. Whatever the user types, clean it first.

Bug 3: I changed hp inside the function and nothing happened outside it. This is the classic. Numbers are passed by value, so changing hp inside encounter doesn't touch the hp in main. The fix is what you see above: return hp, gold and then hp, gold = encounter(hp, gold). I understood functions properly only after this one.

The part I didn't expect: the maths

After it worked I played ten times and kept dying. Was the game unfair or was I unlucky? So I did the probability, which is basically class 11 stuff.

When both roll a six-sided die and I win on ties, the number of favourable outcomes is: 6 ties plus 15 where mine is higher, so 21 out of 36.

P(win a fight)=2136=712≈0.58P(\text{win a fight}) = \frac{21}{36} = \frac{7}{12} \approx 0.58

So each fight, I gain 3 gold with probability 712\frac{7}{12} and lose 3 HP with probability 512\frac{5}{12}. The expected change in HP per fight is

E[ΔHP]=−3×512=−1.25E[\Delta \text{HP}] = -3 \times \frac{5}{12} = -1.25

Over five rooms that's about −6.25-6.25 HP on average, out of 10 HP to start. So most runs are survivable but the variance is big, which is why it felt so deadly.

And running? You escape if you roll 4 or more, which is 36=12\frac{3}{6} = \frac{1}{2}. Otherwise you lose 2 HP, so the expected loss is −1-1 HP per run. Running costs less health than fighting, but gives zero gold. So the game quietly became a risk vs reward choice, and I hadn't even designed it that way. I just typed numbers.

What I changed after the maths

I made the goblins get tougher in later rooms (they roll a 6-sided die plus the room number divided by two, rounded down) and I added a potion room. Then I noticed the potion room made the early game too easy, so I moved it. This tweak-test-tweak cycle is exactly what real game designers do and it was weirdly addictive.

Things I want to add next, in order:

  • A shop between rooms where gold buys a re-roll
  • Saving the high score to a text file
  • Different monsters with different dice (a d4 bat, a d8 troll)

What I'd tell a beginner

  • Write the spec first, even if it's silly. Ten lines in plain English.
  • Make it run early. My first version only printed the room number. Then I added the goblin. Then the dice. Never try to write the whole thing in one go.
  • Print everything while debugging. print(hp) is not a crime.
  • Pick a game, not a calculator. I was bored of "make a todo app" and I never would have finished it. Wanting to see the goblin die kept me going.

I ran it for my little cousin and she asked if the goblin could have a name. So now there's a goblin_names list with seven names and one of them is "Sharmaji". Don't ask.

WhatsAppPost on X

13 responses

Sign in to join the conversation.

  • [+]Rohan Sharma11thSample· 22d ago

    bro i can't code to save my life but this is the first python thing i've read where I understood what's happening. the goblin Sharmaji is sending me

    • [+]Kabir Joshi11thSample· 22d agoAuthor

      the whole point! if the project is fun the code makes sense. you should try changing the numbers, hp from 10 to 5 and see how fast the game becomes a horror

  • [+]Arjun Nair12thSample· 22d ago

    Your calculation is right. One small refinement: since fights are independent, the number of fights you win in five rooms (if you always fight) is binomial with n=5n=5, p=712p=\frac{7}{12}. You could compute the exact chance of surviving 10 HP: you die if you lose 4 or more fights (4 losses = 12 HP). That's a nice little exercise.

    • [+]Kabir Joshi11thSample· 22d agoAuthor

      oh that's cool. so survive if losses ≤ 3? I'll try it. P(losses≤3)P(\text{losses} \le 3) with ploss=512p_{loss} = \frac{5}{12}... I'll code it with a loop and check by simulating 100000 runs

      • [+]Arjun Nair12thSample· 22d ago

        Yes, losses ≤ 3 since 3 losses = 9 HP and you stay at 1 HP. Simulation vs formula agreeing is the satisfying bit.

  • [+]Tushar PawarDropperSample· 22d ago

    good write up. tiny style note: you can replace the infinite loop with a helper ask(prompt, options) function so you reuse it for the shop later. also seed the random generator while debugging so the dice repeat (random.seed(1)). saved me so much time in my hackathon game

    • [+]Kabir Joshi11thSample· 22d agoAuthor

      random.seed!! i didn't know that. that makes debugging deterministic right? that's actually huge, thank you

      • [+]Tushar PawarDropperSample· 22d ago

        yep same seed, same dice. remove it before you give it to anyone or every game will be identical lol

  • [+]Ishaan Bose11thSample· 21d ago

    i read this at 11pm and now i want to make a football manager version where every match is decided by dice. is that a thing already

    • [+]Neel Chatterjee12thSample· 21d ago

      Several of them exist. The dice version is a good start, but the real fun is the table of probabilities behind each player. You could use a chess-style rating and compute win chance from the difference. Not hard.

      • [+]Ishaan Bose11thSample· 21d ago

        neel you just gave me another weekend of homework i didn't need 😭 thanks

  • [+]Vivek Mishra12thSample· 20d ago

    I am learning python on my cousin's old laptop. I tried your code and got error because I had a tab and spaces mixed. After fixing it works. The goblin killed me three times. Very nice small project, now I will try to add a shop.

    • [+]Kabir Joshi11thSample· 20d agoAuthor

      the tabs+spaces thing gets everyone, python is super picky. set your editor to convert tabs to 4 spaces and it never happens again. good luck with the shop!

More from Kabir Joshi