The problem

Look at the centre of a tumour and you will find almost no oxygen. This matters more than it sounds: without oxygen, radiation kills cells far less effectively. A hypoxic core is not just a curiosity of tumour anatomy, it is a direct cause of treatment failure and recurrence.

It is also completely invisible. You cannot see it on a scan, you cannot manipulate it in a classroom, and you cannot experience the dilemma it creates: every attempt to destroy the hypoxic core also damages the healthy tissue around it.

Tumor Lab started from that gap. I wanted to take a real mechanism in radiobiology and make it something a person could actually see, touch and lose against. Where the idea came from

Two things pushed me here.

The first is Foldit, the protein-folding game from the University of Washington. In 2011, its players worked out in about ten days the crystal structure of a retroviral protease that laboratories had been chasing for fifteen years. What stuck with me was not that amateurs beat researchers. It was that the game was producing science simply by being played. Play itself was the contribution.

The second is a contradiction I kept running into. The UnivaBio theme is AI for Human Health. My first instinct was to not use AI. Not out of purism — an image classifier trains in an afternoon, and at the end of it nobody understands the disease any better. I wanted the difficulty to live in the biology, not in the plumbing. A model that reaches 98% accuracy on a dataset tells a judge nothing about whether you understand a tumour.

Foldit resolved the contradiction for me. If playing produces science, then played games are data. So instead of putting AI before human reasoning to replace it, I put it after, to learn from it. First make the simulation correct and the game worth playing. Then learn from how people play it.

That is the roadmap, and I am explicit about it: the engine is done and validated, the learning step is the next one. What Tumor Lab is

A mechanistic tumour growth simulator, wrapped in a turn-based strategy game.

You choose a scenario and then manage a tumour over time: measure it, irradiate it, reduce its blood supply, watch the healthy-tissue toxicity climb, and try to finish with the tumour controlled and the organ intact. Five modes move from a guided tutorial to a strict in vitro laboratory regime.

Everything on screen is computed. Nothing is pre-drawn. How I built it

The engine is 13 Python modules. Each one owns a piece of the biology: Module Responsibility transport.py Conservative finite-volume reaction-diffusion for O₂, glucose, lactate, pH metabolism.py ATP, growth, acidification growth.py Avasculary growth with a free boundary vascular.py Hypoxic angiogenic switch, imperfect perfusion, anti-angiogenic pressure therapy.py Linear-quadratic radiotherapy with the oxygen enhancement ratio toxicity.py Normal tissue complication probability in the healthy organ agents.py 3D agent-based model, used for cross-validation evolution.py Mutation, genetic drift, intratumoural selection game.py Game environment, partially observable state ai.py Explainable deterministic adversary validation.py Verification and validation harness plotting.py Reproducible scientific figures parameters.py Parameters, each one tied to a bibliographic source

Around it: a Flask application with SQLite for accounts and scores, deployed on PythonAnywhere, plus C# scripts for a Unity 3D client.

The key equation is the one I apply to every fraction. A cell population's survival follows the linear-quadratic model: S(D)=e−αD−βD2 S(D)=e−αD−βD2

where the effective dose in hypoxic tissue is scaled by the oxygen enhancement ratio. A tumour, however, is not one population. Cells differ in radiosensitivity — Britten and colleagues measured the surviving fraction at 2 Gy varying between 0.24 and 0.52 among clones from a single tumour. So I carry a discretised distribution of radiosensitivity, and after every fraction I reweight it by survival: wi←wi Si(D)∑jwj Sj(D) wi​←∑j​wj​Sj​(D)wi​Si​(D)​

The well-oxygenated, radiosensitive cells die disproportionately. What remains is a more resistant tumour. That is Darwinian selection, produced by the arithmetic rather than by a scripted event — and it is what the player is actually fighting. The hard parts

The timescale broke my first design. EMT6/Ro spheroids double in about 20.6 hours. Over a week that is a 285-fold regrowth. Five fractions of 2 Gy in normoxia leave about 3% of cells alive. Multiply: 0.03 × 285 ≈ 8.5. Conventional fractionation mathematically cannot control this spheroid. My first build therefore rewarded the opposite of what I wanted: hitting as hard as possible won, and the standard clinical protocol lost.

The reason is that human tumours double in 30 to 100 days, not 20 hours. With a 20-day doubling time the same arithmetic gives a four-fold reduction per week, and control works. So I added a clinical timescale mode, made it the default for the game, and documented loudly that it leaves the validated in vitro regime. The strict regime is still available as its own game mode. I would rather tell the player that two regimes exist than pretend one model does both.

N had to go. The agent-based model produces resistance selection cell by cell, which is exactly what the game needs — but its cost grows linearly with cell count. Measured on my machine, one simulated day goes from 2 seconds at 10³ cells to 258 seconds at 8·10⁴. Unplayable after five turns. The continuous model costs about 1 second per turn regardless of size, but it only knows one homogeneous population, so it cannot select for resistance at all.

I reconciled them by transporting a discretised radiosensitivity distribution over the continuous field, following the construction of Alfonso and Berk (2019), and validating that the resistance selected along this fast path matches the agent-based model on the same protocol. The game runs in a second per turn and still evolves.

A factor of a thousand. In evolution.py, the probability of becoming radioresistant is conditioned on a driver mutation appearing first. The effective rate per division is around 10⁻⁴, not the 0.1 I first passed in. Using 0.1 produced a 12.7% drift in resistance on the unirradiated control, where the agent-based model measures 0.000%. Resistance appearing without treatment would have destroyed the meaning of the entire game. It is now documented in the code so it cannot come back.

Publishing it. I will be honest about the least scientific part of this project: git fought me, and I used an AI coding assistant to get the repository onto GitHub. UnivaBio explicitly allows AI assistants, so I have left that sentence in. Everything in tumeur/ is mine. What the results show

I refused to treat validation as decoration. The harness runs 31 of 32 checks against published experimental data — the EMT6/Ro spheroid and the Freyer & Sutherland (1986) protocol reproducing how oxygen and glucose supply determine final spheroid size. The allele spectrum produced by the evolution module follows the 1/f law with R² ≈ 0.99.

One check fails. It concerns necrosis in rich medium, and I documented the discrepancy rather than widening the tolerance to make it pass. There is a rule in the README that I wrote for myself: never relax a scientific tolerance just to make a test pass. What I learned

That verification is not the last step but the thing that tells you what you have actually built. That two models of the same system can be reconciled instead of one being thrown away. That the most interesting behaviour in a simulation is often the behaviour you did not put there — the first time resistance emerged from the fractionation arithmetic and beat my own hand-written strategy, the project changed from an illustration into an instrument.

And a fair amount of radiobiology: oxygen diffusion, the linear-quadratic model, the oxygen enhancement ratio, NTCP, and why a clinician cannot simply turn the dose up. What I am honest about

Tumor Lab is an educational and theoretical model. It is not a medical device, not a treatment planner, and not a patient predictor. The vascular_human regime is a mechanism-based scenario, not patient measurements, and the README says so.

The website is still a work in progress: the interface keeps moving and I am fixing things now. The engine is tested and the game is playable, but there is no guest access yet — you need an account to try it, which I know is a barrier and is my next fix.

And the step I care about most is not built. The explainable adversary in ai.py is deterministic and deliberately simple, and it only reads what the player can see. The neural network that learns from played games, and that one day might predict how a tumour will evolve and propose a fractionation schedule, is the next part of the work. I am not claiming it as done. Try it

The website is live and the source is public:

Play: https://haroldmaugez.pythonanywhere.com/
Code: https://github.com/HaroldMaugez/tumor-lab

Create a free account and start with the tutorial, then try lungs. That is the mode where no brutal strategy works, and the one I would most like feedback on.

Share this project:

Updates

Submission history