First stop on the GUI framework search: PyQt4. Out of the gate it’s looking good. It’s simple to draw primitive shapes in an off-screen buffer and then rescale that image and blit a portion of it into a bare window. Restricting window size (minimum, maximum, granularity) is also a piece of cake.
Testing window resizing helped me decide to commit to x2 (double-size) pixels. I knew x4 would be too large but between x2 and x3 I wasn’t sure. x3, it turns out, causes a problem with maximized windows because many desktop sizes aren’t divisible by 3. x2 is sufficiently pixely, still leaves plenty of resolution for fine details, and it’s a safe bet for filling maximized windows properly even with window decorations and borders and the like.
I started putting together methods for scrolling/panning the contents of the window, and I hit a major problem. My CPU was burning. Panning at a steady pace with arrow keys ramps CPU load to maximum. This is a showstopper: simply blitting the scaled up image into the window at a high enough rate to make basic animation possible is a crippling load even without the additional work of actually drawing each new frame. This doesn’t sit right with me. This should be fast. It should be practically free. I have to fix this before I go any farther with Qt; if I can’t fix it I’ll have to try something completely different, maybe PyGTK.
The crippling blit GUI prototype: qtinky20100706.py
Well, the thing should be windowed. It’s not going to be compelling enough or interactive enough to justify full-screen. The most common mode of operation should resemble the QBasic program: no frills, just fish and explosions, no menu bar or status bar or anything like that. The title bar can probably stay; it serves important functions like moving and closing the window which would have to be implemented in other ways if the title bar were absent. Maybe I’ll implement those alternate methods anyway and then make hiding the title bar optional.
With no menu bar or anything, there must be a right-click-anywhere context menu, and it must be slick. The most prominent feature of the context menu will be a minimap type of thing. The thing is, for evolution to work properly there needs to be a lot of fish, and that means the field of play has to be very large, larger than a modest window can display. Since the window can only show a portion of the action there needs to be a variety of ways to pan that viewport. A readily accessible minimap is essential. Left-clicking and dragging in the main window, also useful. Throw in arrow keys and that should about cover it. There should also be an automatic panning function which intelligently pans the view to include interesting action.
The window will be resizable but with special restrictions. Since the window contents will be drawn with extra large zoomed-in pixels, window resizing needs to be limited to a coarse granularity to avoid partial pixel rows and columns at the edges. There’s also a potential issue with very large windows: it’s possible the window could be wider or taller than the fish tank. Optimally the window would just refuse to be oversized, but if that’s not possible the next best thing would be to automatically increase the zoom level enough to fill it.
The QBasic program is relatively simple as far as the rules of the fish-world go. Fish can be one of a dozen or so colors, and the torpedoes they fire at each other only destroy fish which are differently-colored. When a fish swims off screen or is destroyed it is replaced by a random size and color of fish swimming in from the sides of the screen. When the fish swim from side to side they tend to swim toward other fish with the same color. These simple rules lead to a power struggle among fish colors. Certain colors of fish can come to dominate the population and eventually fall from power as other colors score lucky hits.
I want something much more sophisticated now. I want genetics. I want survival of the fittest rather than purely survival of the lucky. To make things interesting the fish must have many more characteristics than simple scale and color, and those characteristics must have fine precision and subtle consequences. For example, fish and weapon colors should be expressed as a real value representing hue. This means that the simple friendly fire rules in the original program won’t work any more. The damage done by a particular color of weapon to a particular color of fish should be scaled depending on exactly how dissimilar those hues are. Weapons that are more or less discriminating should also be possible, so the color of weapon that a given fish weilds needs to be defined by two values which are effectively mean and variance as in a normal distribution.
Fish must also bear offspring which are slightly mutated, as in nature. What this all means is that the Gaussian function will play a very big part. Sampling a normal distribution is a very common method of mutating real values in an evolutionary algorithm: most mutations are very slight, but large changes are still possible.
The real trick to introducing evolution into this fish combat simulation is maintaining diversity. Obviously the whole point of this thing is to be looked at, and a wide variety of fish is best for that. I expect convergent evolution to cause problems. If there is a single best design and strategy for survival in the fishwars universe, then the entire population will tend toward it, making the ecosystem homogeneous and uninteresting.
Fishwars is the most complex QBasic gizmo I’ve ever made. It’s over 1,000 lines of code, and it’s been through more revision and enhancement than any other. I like staring at it enough that I’ve decided to lift it out of the QBasic world and into the 21st century.
Python is my weapon of choice for the programming language. It’s obviously powerful enough to implement a fish tank, and it will make the program more or less platform agnostic. There are enough user interface APIs with Python bindings that I shouldn’t have any trouble selecting one which does what I need and maintains cross-platform compatibility. I also appreciate the similarity of form to QBasic, namely that the program can take the form of a single monolithic text file.
Graphically, I want a connection to the simplicity of the original. I don’t want to go 3D; I don’t want to use OpenGL. Beyond being strictly 2-dimensional, I want giant pixels. I like pixels. The original QBasic program uses a 320×200 pixel graphics mode. Clearly, restricting the new version to such a small space is taking things too far, but modern displays have very fine dot pitch, so the graphics will have to be at least doubled in size. Antialiasing is out; smoothing diagonal lines and camouflaging pixels is obviously antithetical to making a feature out of low resolution graphics. Precisely how large to make these pixels, I’m not sure. That decision will have to wait until I’m far enough along to be rendering things so I can see the actual results. I may make this zoom level a user configurable option, but even in that case 200% would be the minimum. Giant pixels is a primary style decision here, it is not optional.
At this point I’m feeling good about 200%. 300% could work. 400% I think is too much; I do want some fine detail.