I've recently added some code that should fix virtually every crash I've encountered so far. Well, not quite 'fix', but stop the program from crashing, report the error, and keep going.
I noticed that most crashes occur in the core of the program: deciding which move to make. This is because TM evaluates so many positions that it exercises most of its code each battle. The deeper the search, the more likely a crash is, because it's evaluating more and more obscure positions. However, to allow a particular optimization to work, TM already evaluated to a depth of 1 first, then 2, then 3, etc., until it hit the maximum depth it was told to search. My crashes could almost entirely be traced back to two lines of code (but with over a million ways to arrive there, I think). Therefore, I put in some code around those two lines that detects an error and aborts the current search. The program recognizes that its search was not completed successfully, and thus only uses the results from the previous depth of search.
To make this work in all cases (what happens if it encounters a bug at depth = 1?), I added a new mode of play for TM: depth = 0. At depth = 0, TM will make a random legal move. This should also prove useful later as part of a stress test for TM, once I get the obvious bugs worked out.
Due to these changes, Technical Machine is much more stable than it has ever been.
Recently, I was also in fixing up some old code to simply be organized more logically ("refactoring"). The goal of this was not to make TM run any faster, but while doing so, I noticed a few cases where TM was needlessly wasting memory. Most of TM's time is spent writing to memory (it has to make a lot of copies of Pokemon in its search), which means that for me, memory efficiency is speed efficiency -- there is very little of a trade off in this department. Those minor changes proved to make TM work much faster.
How much faster? Previously, a depth=3 search took about 20-30 seconds in most cases, and some of the time would take around a minute per turn. Now it takes approximately 5 seconds or less in nearly all cases (and the rest of the time should be well under 10 seconds). A depth=4 search took so long I was never able to get data to measure it (it couldn't even finish evaluating a single response to a single move before I gave up or it crashed). Now depth=4 takes about 15-20 minutes to complete.
This is obviously too long for most 'real' play. However, it opens up a few options. First of all, I can play TM as a sort of background process. I can be reading something, and PO alerts me when TM has made a move. This will allow me to see how TM plays at its highest level so far (I have not yet done more than about 7 turns vs. TM on depth=4, so I can't tell how much stronger it is). I could also do a TM vs. TM battle and let it run overnight, which should be interesting to watch the replay of (a 40 turn battle would take about 10 hours for a single TM, but since they have to share memory since I only have one fast computer, it would take longer. However, the final turns of a match go much faster than the first, because there are fewer options, so 10 hours is probably still an accurate estimate).
Most importantly, I know I can do better. While I was refactoring, I had an idea for how to dramatically improve TM's speed. The speed ups I made recently are minor in comparison. This change may be able to do give me something along the lines of a 1 minute turn. From there, a few incremental changes should bring depth=4 down into the reasonable realm of 20-30 seconds per turn, which I think is tolerable in live play.
Unfortunately, TM appears to be unable to play in battles in which it is the challenger. I'll have to look into why this is the case.