Projects
A tour of what I've built outside of work, starting with what I'm working on now, then the things real people use, and ending with the games I wrote as a teenager that are still sitting on GitHub, because deleting them would be dishonest.
Machine learning
Python / PyTorch
OR-Tools
An agent trained to play enclose.horse, a daily puzzle game about fencing in a horse. An exact CP-SAT solver acts as both teacher and ruler: it labels the training data and defines the optimality ratio the learned agent is scored against. A U-Net predicts per-cell wall logits, and a decoder turns those heatmaps into provably legal enclosures with a BFS reachability engine as arbiter. A Gymnasium environment lets PPO fine-tune from the imitation checkpoint, sharing one 16-channel encoder so the warm start actually works.
Validation against the real game
The solver's optimum matches the game's own published optimalScore on
232 of 232 archive puzzles, every one proved optimal. The scoring engine
independently reproduces the best human solution on 286 of 286 community levels.
Thirty-four tests check both solver formulations against exhaustive search. That validation
is what makes every number below trustworthy.
Results
The trained model is dramatically better than a no-learning baseline on the distribution it trained on, and no better on the real game:
| Prefix-only decoder | Fixed prior | Trained | Ratio |
|---|---|---|---|
| Synthetic validation (in-distribution) | 0.134 | 0.659 | 4.9x |
| Real daily archive | 0.081 | 0.099 | 1.2x |
| Player-made community levels | 0.303 | 0.249 | 0.8x |
The gap is distribution shift, not overfitting or undertraining, established three ways: held-out synthetic performance matches training performance, so nothing is being memorised; 10x more data and 3x more epochs improved the in-distribution prior roughly fivefold while barely moving the real archive; and the drop is largest on the most out-of-distribution set. Differences on the full decoder carry paired-bootstrap 95% confidence intervals. That isolates the puzzle generator, rather than the model, as the bottleneck and the next thing to fix.
Measured against people rather than against the solver, the agent lands at the
13.6th percentile of players on a median level, across 444,000 human
submissions on 286 levels. PLAN.md is a 30KB engineering log of the nine
bugs found and fixed along the way, including an unsound CP-SAT formulation and a misread
rule that was silently inflating every training label.
Source and full write-up »
Shipped software
Dart / Flutter
The Instant Mix feature in Finamp, the leading open-source Jellyfin music client for iOS and Android. Tap a song, artist or album and the app builds an endless radio-style queue from it. I wired the Jellyfin instant-mix API into the app's Chopper client, extended the audio service to seed and take over the play queue, and built the multi-select UI across three screens.
Four merged pull requests, ~600 lines added. “This is really well done, merged :)” said the maintainer. Read the full story » · Source
iOS / Android / AWS
A company I co-founded and a product I built. Customers ordered from their table by tapping an NFC tag, and geofence triggers checked them out automatically when they left. I wrote the customer-facing iOS app and Android app end to end, plus the Android POS app, including SSO, social login and payments, and built the entire serverless AWS backend on Lambda, API Gateway and DynamoDB.
Closed source. See my career page for the full write-up.
The arcade
Before any of the above, there was pygame. These are the projects I learned to program on, published between 2017 and 2018 and left exactly as they were. They are not good code. They are why I write code at all.
| Project | What it is | Year |
|---|---|---|
| KickBall Python |
A soccer game built on real projectile motion. Split into three modules: one for the velocity equations, one for the pygame plumbing, and one for the menu and buttons. My first repository, and the first time I separated physics from rendering. | 2017 |
| AppleEater Python |
Snake, but you race a computer opponent to ten apples. Fifteen source files with an
actual object model built from Snake, Apple and
Opponent classes, a pause menu, persistent high scores, and a hand-rolled
centre-origin image rotation. The player can wrap through walls; the AI can't. That's the
handicap. |
2017 |
| Car_Game Python |
Dodge traffic with the arrow keys. Difficulty escalates on score thresholds: a second obstacle at 25, a faster red one at 100. Near misses award bonus points. The README ends with a list of its own bugs, including “the entire code is in one file, it should be separated.” Correct then, correct now. | 2017 |
| pingpong Python |
Pong against the computer, with a menu, a scorekeeper and a proper screen-update loop broken out into its own module. The most cleanly organised of the four. You can watch the structure improve across the repos in date order. | 2020 |
Utilities & exercises
| Project | What it is | Year |
|---|---|---|
| Webscraping Python |
A BeautifulSoup scraper that walks the Dallas Craigslist car listings, applies filters and writes the results out to CSV. Built because I wanted to buy a car and didn't want to refresh a page. | 2018 |
| SimpleAlgos Python |
Exactly what the name says: prime sieves and odd/even partitioning, kept around as a marker of where things started. | 2018 |
Most of what I build now ships inside private repositories at work: document infrastructure, tax systems, AI classification pipelines. The career page describes those in as much detail as I'm able to share, and I'm always happy to talk through any of them, so drop me a line.