Tip
Everything here is browsable as a site: tewf.github.io/after-hours Every project on one page, with the animations and charts at full size, and the tax report readable in the browser rather than downloadable as a PDF.
What I build when I am bored. Nobody assigned any of it and none of it is coursework: it is what I do with a free evening, in machine learning, computer algebra and applied mathematics.
Each folder stands alone, with its own README and its own dependencies. Every number below is produced by code in this repository, and CI reruns the checks on every push.
| Flappy Bird from raw pixels A DQN and a REINFORCE agent that see nothing but the rendered frame. No position, no velocity, no pipe coordinates. 12.21 pipes per episode over 100 greedy episodes, best 61, trained in 23 minutes on one RTX 4060. PyTorch, Gymnasium, Pygame |
|
| A 3-SAT solver over GF(2) Clauses become polynomials, triangular Gröbner-style elimination propagates, branching finishes. Every distinct monomial becomes an unknown, and the whole system becomes a matrix. 0 wrong verdicts on 500 instances checked against exhaustive search, and it says plainly which half of it does the work. Python, PySAT for cross-checking |
|
| Sorting algorithms in 3D Bubble and merge sort keyframed and rendered through Blender's Python API, so the access pattern is something you watch rather than read. 306 and 206 events, keyed out to 1161 and 841 frames at 1080p24. The previews here are the earlier render, from before the timing moved out of the algorithm. Blender 5.2, bpy |
|
| Integer programming, exactly A two-phase simplex in exact rationals, then branch and bound on whatever it leaves fractional. Nothing is ever a float, so a bound is a fact about the programme rather than about the arithmetic. 200 random programmes settle to the same optimum as an enumeration of every whole point in the box. The knapsack above takes 9 nodes. Python, fractions |
|
| Matrix algorithms from scratch An FFT, a determinant through the Schur complement, an exact integer inverse and a trace that never forms the product. NumPy is the oracle, never the implementation. 11 checks against NumPy, matching to 1e-13. The inverse is exact rather than nearly right. Python, NumPy |
|
| The French income tax, modelled Where does an extra euro of gross salary stop being worth much? Exponential fits per bracket, then the Lambert W function to find where the effective rate stops accelerating. The tipping point is 59 800 EUR gross a year. SciPy, Quarto |
The lesson that cost the most time here, and the one that transfers. The Flappy Bird agent plateaued and no hyperparameter moved it. The bird is yellow, the sky is light blue, and the two are nearly equiluminant, so the luminance greyscale every Atari pipeline uses was giving the bird 22 levels of contrast out of 255 while the obstacles got 64. Taking the blue channel instead gives 181.
Same network, same hyperparameters, same seed: greyscale needed 200k steps to clear its first pipe and never passed 0.80; the blue channel cleared one at 50k and passed ten at 250k. It looked like a tuning problem for a long time, and it was never a tuning problem. The write-up is here, with the run that failed and a scaling bug of mine that made one loss term 224 times another.
cd <folder>
pip install -r requirements.txtEach folder's README says what to run. The Blender scripts load in Blender's Scripting workspace or render headless from the command line. Trained weights and the full-resolution renders are attached to the latest release rather than committed, so a clone stays small.