C - in fact it started as an exercise in C programming. It uses MFC to interface to Windows - initially MFC V1, so it doesn't use the CDocument/CView stuff although it would probably have been easier if it had been around. C has evolved over the years too - the code uses templates quite a bit, it uses multiple inheritance in just a couple of places where it turns out to be a help.
There are several representations used at different times. The principal one uses an array of live cell numbers for each row, with the rows held as a singly-linked list. Thus it uses four bytes per live cell, and nothing for dead cells. (For a long time coordinates were held in two bytes rather than four, but memory is so cheap now that the extra flexibility seems worth having. Surprisingly, the performance actually improved with this change, despite the extra cache churn). This is very convenient to grow (see below), but hard to add to or find if a cell is alive. So two other structures are also used: a binary tree for insertion, and a hash table when lookup is important (for example during pattern recognition).
This is intimately tied to the data structure (and goes back to my original PDP-8 implementation). Three pointers keep track of the position in adjacent rows, and as the pointers move down the array, the state of the neighbor cells is used to update the neighbor state of the "current" cell. There are complications to deal with gaps in each row and between rows. This is reasonably fast, and has some nice features - it makes it easy to work with different rules, to update the display information, and to count population and calculate a "signature" for a pattern.
You can find all the gory details here.
Considered, yes. I spent some time looking at a bitmap-based algorithm, which would probably have been 30% or so faster. But it didn't track population or signature, and each growth rule required its own unique code. In real life performance is dominated by the graphical output anyway.
No. Life is just too short.
The growth algorithm computes a signature for every generation, at negligible cost. This signature, which is based on the neighbor state for each live cell, is the same for any two patterns composed of the same objects (collections of contiguous cells). This means that moving objects such as gliders do not affect it. If two nearby generations have the same signature, then another cycle is grown. If the signatures match throughout the cycle, then the patterns themselves are compared (which is a much more expensive operation). Anything which is not common to the two generations is analysed to see if it is composed of known moving objects (typically gliders). If so then their trajectories are analysed to foresee possible interactions. Sometimes the only way to analyse them is to run the pattern until there is no possibility of interaction - why is why you sometimes see a pattern (particularly a random broth) grow for thousands of generations, then get a message saying it is not interesting from a much lower generation.
It is also based on the signatures. The process starts by breaking the pattern into objects (collections of contiguous cells). Patterns from the libraries are similarly analysed, so the object signatures can be looked up and the objects identified. This is enough to identify patterns which are composed of a single object (e.g. block, blinker). However some objects may either stand alone or form part of a larger pattern. A "discriminator tree" is built for such objects which identifies cells that can be tested to rapidly eliminate or confirm candidate patterns (which can run to hundreds for some common sub-patterns).
Because you didn't ask me yet. I'm happy to answer any questions about the Winlife32 implementation if you send them to firstname.lastname@example.org.