I have been asked why I'm using a separate chip for handling the buttons, so I thought I will just explain it in here. The question was why not just use the edge connector, and then use the additional pins available on the Micro:bit for reading the buttons. There are actually to reasons for that:
I don't like the edge connector.
Ever since I first saw it, I tried to work around it with something smaller, lighter and less expensive — you can look at my #Micro:header — but I never managed to get a reliable connection without damaging the Micro:bit itself. I do have a bunch of those connectors, both soldered on a breakout board, and loose, but they are simply too large for such a small handheld device, I don't have a Fritzing footprint for them (and don't feel like making one), and soldering all the pins is a chore. Bolts are, on the other hand, a tested and reliable alternative.
Handling buttons is not trivial.
Looking at the code for the button controller you wouldn't guess that, but handling the button presses properly is actually a non-trivial task. You have to de-bounce them, and you have to buffer them — those are some simple things we came to expect from anything with buttons, because it's so common. Doing that in the limited version of Micropython that runs on the Micro:bit, without access to timers or interrupts, while at the same time handling the logic of whatever game you are playing would be a challenge, if not outright impossible. Doing it in C as a built-in module would be a little easier (that's how I did it for #µGame), but getting that extra code merged and released would take ages — the current release is a year behind, and there is never enough memory, so I don't think they would happily accept an extra module that is only useful in one project. Having to use a custom Micropython firmware would make it impossible to code using Mu or any of the other dedicated editors.
So considering all that, I decided to use a dedicated chip for the buttons. I initially started with a simple gpio expander chip, like in #D1 Mini X-Pad Shield, but that still requires you to poll it constantly and do the de-bouncing and buffering yourself. You can also easily miss presses. Then I switched to the HT16K33 that I'm using in #PewPew FeatherWing, because it does the de-bouncing and buffering for you, but that's an expensive and bulky chip, that we are not really fully utilizing. So I looked for the cheapest microcontroller with enough pins, and ATtiny24 seems like the perfect fit, especially since I already have experience with AVR.