I came to the realization a couple weekends ago that I needed to write a compiler. I was hoping to avoid it by simply reducing the type of commands that the pinball machine could run, but I keep coming back to the issue that I want this to be a generic pinball driving machine. That being said, the pinball rule set must be able to handle all sorts of different instructions. Some of the instructions will be high-bred instructions to make writing pinball rules easier for users, but many will simply be math like instructions. I’ve spent a good deal of time trying to come up with a good number of these high-bred instructions, and a good amount of time on the architecture of the rules file. I will eventually write a document on writing a pinball rules file, but doing a first cut for my simple pinball machine has really helped to make the ideas concrete.
Here is a link to the current rules file (I’ll point this out so that people can find it more easily and not need to download the whole code base):
The first section of the file is simply stating what hardware needs to exist for the pinball machine and names each of the solenoids, inputs, and LEDs. This is so each used hardware pin can be identified by name. Next section is variables and indexed variables. This is going to be implemented as a simple array of integers with the dictionary (keyed off the name) handing back the offset into the array. Next sections are sound and video clips. These will be pre-allocated so they can quickly be switched between or even play multiple sounds at once.
The next section contains the processing chains. This is the section that requires a compiler. The input pins named in the above sections can be used to alter the operation of the machine.
A simple example would be kicking the ball into the shooter lane. While it would be easiest to kick the solenoid, and just hope that the ball got onto the playfield, it might not necessarily happen. The solenoid might not be working properly. One way to deal with this is to kick the solenoid and check the switch if a ball shows up in the shooter lane (one of the input switches). At the same time, if the ball doesn’t show up, you want to try to kick the ball another few times just in case the issue fixes itself. (Maybe two of the balls were interfering with each other, or maybe the solenoid was a little sticky, and a second kick would work.) If the maximum number of retries is exceeded, the pinball machine should display an error and not allow anybody else to put more money in the machine. This simple action of the pinball shows that it needs to support timers, math functions (incrementing the retry count), comparisons, and reading switch inputs during the loop. The process chains that implement this are Proc_Start_Ball_Init and Proc_Start_Ball_Start. The init function is run once when a mode is entered, while the start routine is run after each tick. At the end of this chain, the pinball machine will move to the Mode_Ball_In_Play or Mode_Error.
I originally thought that I could implement this with a simplified amount of functions, but during writing the rules file, I found it was much easier to assume that I had that ability to use anything that is available to me in any normal language. That means that it is time to write a simple compiler. (Luckily it is only the top end of the compiler to convert command lines into pseudo-code. I don’t need to write the bottom end of the compiler. Of course, if I would write the bottom half of the compiler, it would be much faster, but then I would need the parser to create code and then compile the generated code. I currently believe it will be fast enough without doing this last step, but it is arguable that it would be a cleaner design using self generated code.)
This has taught me not to laugh at my friends when they were taking compiler theory in college, while I was taking fields and waves. Maybe I should have taken it as an elective. Somebody on the web wrote a article about how a pinball machine is the ultimate renaissance man machine. It requires electrical engineering, mechanical engineering, graphics design, animation, and computer science disciplines. I have certainly enjoyed the strange paths that it has taken me and allowed me to probe some new areas that I was not expecting.
Anyhow, there are also high-bred commands. An example would be the LED_ROT_RIGHT/LED_ROT_LEFT commands. These commands are very useful for implementing the “change lane” function. In most modern pinball machines, when you hit the right flipper, the inlanes, or outlanes lights rotate to the right so that it makes it easier to complete all of the lights. The left flipper sometimes does the same thing to the left. (It actually freaks me out when playing early 70s machines where you actually have to shoot accurately instead of just rotating the lights to complete a set of lights.) In the pinball machine, hitting the right flipper simple rotates the subset of lights. The high-bred command rotates the group by using the LED_ROT_xxxx command, then providing the mask of bits to rotate, and the variable containing the currently lit bits. The Proc_Flipper chain does this processing. It is a high-bred command because it does a lot processing beneath the command, but the user just has to write a simple upper layer command to get it to happen.
If anybody has any comments on what they would need in a rules file, I would be glad to hear them. Once the framework is finished, it will be simple to add more commands, but suggestions would be appreciated to make sure that I don’t miss major functionality that is required. It is always best to plan for major features at the start to make sure that the architecture supports them.