I have a WIP of this game, last updated in July 2008.
(There should in fact be a newer WIP too, but I don't remember which server I ran it on, and I cannot therefore acquire the file...)
However, due to
bit-rot, I have been
unable to continue the TAS. The program which I used to calculate the solutions, simply does not work anymore. I don't know why, and I don't have time or energy to fix it. It uses multiple instances of FCEU in parallel (multicore), and it is not written in LUA. Now it just crashes right after starting, due to problems with communication with the (virtual) X server or something.
I am therefore releasing my current WIP. My hope is that someone finishes it (continuing the same awesome quality of course) and credits me as a co-author.
It is a .fcm file. Apologies for that, too. I don't know whether it converts nicely to fm2.
This TAS aims for fastest absolute time. I.e. it also counts score tallying as a slow-down factor and attempts to minimize that. Hence it gets the "perfect!" congratulation only once so far.
33 stages have been completed, out of the 60. The movie length is
12:45.42 according to Microstorage. It ends after the first shot in stage 34.
http://dehacked.2y.net/microstorage.php/info/1597220526/lunarball-wip-34up-bisqwit.fcm
Note: This WIP does
not have the PAL mistake I mentioned earlier. It is proper all the way.
[img_left]
http://bisqwit.iki.fi/kala/snap/lunarball.png[/img_left] The
playing algorithm can be explained with the following list of definitions:
* Each round starts at the first possible frame when the cursor can be moved, and ends right before the next round. This ensures that the score tallying times are counted.
* Whether the cursor can be moved, is determined by pressing nothing, undoing it, pressing the R button, undoing it, pressing the L button, undoing it, and checking whether the cursor had reacted in expected ways (R and L must produce a different change and 'nothing' must produce no change).
* Changing the cursor distance (up and down buttons) has no effect to the game.
* To repeat a shot in deterministic manner, you only need to know the angle and the power meter value to make a shot. The bot can tell you these; you can manipulate the cursor distance manually in whatever artistic way you prefer. Indeed, the bot produces a list of angle and power meter pairs for the
player to perform. It does record movie autonomously, but a frame-advance and RAM watch equipped player can redo the movie entirely by simply following that list which can be transmitted in a text file or written on paper. It contains simply two numbers for each shot.
* RAM addresses of significance (all numbers hex): 3C0=angle (0..FF); 3A0=velocity (12..FF at steps of 3); 187=stage; 18E=shot number; 300+n=ball status (n=0..10; 1,2=live; 0,3,83=not ball); 370+n=ball x, 330+n=ball y, 600+y*1A+x=board map
* Algorithm always calculates the result of two shots in a row. For the second shot, all combinations are tested (256 angles, 80 velocities); for the first shot, just 256*15; sometimes less. I often varied these numbers manually between rounds. If the first shot pockets any balls, the second shot is omitted. (The purpose of the first shot, "pre-shot", is to position the cue ball in a better position and to reset the
rating that makes score tallying go haywire.) If neither shot manages to pocket any balls, or it takes too many frames until either the cursor is controllable again or the stage ends, the aim is considered a failure.
* The first shot is chosen that results in the best state after the second shot.
* State wellness is calculated with the following approximate formula: ((number of balls pocketed) + (prospects)) / (the length of the round in frames). Prospects is a numeric value 0..1 that is inversely proportional to the average distance of the remaining balls to the pockets nearest to them.
* A genetic algorithm is used to refine the set of remaining choices.
* The search space is limited by using a frame count limit. Pocketing the cue ball also counts as a blunder.
* Given a pair of angle and velocity, aiming and shooting is a matter of aiming first, then waiting until the velocity counter is at exactly the right value, and then shooting (B button for 1 frame).
* Aiming is accomplished by first determining whether the R button or the L button results in faster aiming. (Simple subtractive mathematics.) After that, determining whether mashing the button results in faster aiming than just holding the button. By mashing, a change of N units requires 2*N-1 frames. By holding, a change of N units requires 15+N frames.
EDIT:
Surprise and terror ― the bot runs properly on my server again. Looks like the TAS is going to be continued after all. After a pause of 1,5 years!
Here we go...
bisqwit@chii:~/nes$ egrep 'Beginning|Pocketed' lunarball.log
Beginning new shot. Table=34, remaining balls=7
Record: Pocketed 1 (time 548, angle 0, velo 255, preangle 0, prevelo 42) (ball at 6D,AD)
Record: Pocketed 1 (time 532, angle 7, velo 255, preangle 7, prevelo 210) (ball at 39,74)
Record: Pocketed 1 (time 487, angle 7, velo 255, preangle 7, prevelo 252) (ball at 7A,AE)
Record: Pocketed 2 (time 600, angle 8, velo 255, preangle 8, prevelo 75) (ball at B2,B9)
Record: Pocketed 2 (time 595, angle 8, velo 255, preangle 8, prevelo 84) (ball at B4,B9)
Record: Pocketed 2 (time 579, angle 11, velo 255, preangle 11, prevelo 99) (ball at 2E,A6)
Ignoring: Pocketed 1 (time 479, angle 17, velo 255, preangle 17, prevelo 150) (ball at 78,A5)
Ignoring: Pocketed 1 (time 462, angle 17, velo 255, preangle 17, prevelo 153) (ball at 91,A5)
Ignoring: Pocketed 1 (time 387, angle 17, velo 255, preangle 17, prevelo 210) (ball at 47,7B)
Record: Pocketed 2 (time 572, angle 18, velo 255, preangle 18, prevelo 147) (ball at 7A,C8)
Ignoring: Pocketed 1 (time 364, angle 13, velo 255, preangle 13, prevelo 213) (ball at 8E,5C)
Record: Pocketed 2 (time 570, angle 18, velo 255, preangle 18, prevelo 159) (ball at 7A,C8)
Record: Pocketed 2 (time 506, angle 14, velo 255, preangle 14, prevelo 24) (ball at A6,80)
Record: Pocketed 2 (time 487, angle 30, velo 255, preangle 30, prevelo 87) (ball at 9B,B5)
Record: Pocketed 2 (time 487, angle 24, velo 255, preangle 24, prevelo 54) (ball at 8A,A1)
Ignoring: Pocketed 1 (time 320, angle 29, velo 255, preangle 29, prevelo 177) (ball at A6,93)
Record: Pocketed 2 (time 408, angle 24, velo 255, preangle 24, prevelo 237) (ball at 8C,A2)
Ignoring: Pocketed 1 (time 320, angle 36, velo 255, preangle 36, prevelo 141) (ball at A4,81)
Ignoring: Pocketed 1 (time 226, angle 39, velo 255, preangle 39, prevelo 111) (ball at AD,9C)
Ignoring: Pocketed 1 (time 226, angle 39, velo 255, preangle 39, prevelo 114) (ball at AD,9C)
Record: Pocketed 3 (time 466, angle 43, velo 255, preangle 43, prevelo 210) (ball at C5,57)
Ignoring: Pocketed 2 (time 367, angle 50, velo 255, preangle 50, prevelo 207) (ball at 6B,A2)
Ignoring: Pocketed 2 (time 346, angle 50, velo 255, preangle 50, prevelo 210) (ball at 6B,A3)
Ignoring: Pocketed 2 (time 346, angle 50, velo 255, preangle 50, prevelo 213) (ball at 6B,A3)
Record: Pocketed 3 (time 461, angle 43, velo 255, preangle 43, prevelo 255) (ball at C5,5D)
bisqwit@chii:~/nes$ ps w
PID TTY STAT TIME COMMAND
13730 pts/5 Ss 0:00 -bash
14022 pts/5 S+ 0:02 fceu/fce -readwrite 1 -bisqbot 1 -state 4 -play tmp2.fcm lunarball.nes
14023 pts/5 S+ 0:00 tee lunarball.log
14304 pts/5 R+ 0:43 fceu/fce -readwrite 1 -bisqbot 1 -state 4 -play tmp2.fcm lunarball.nes
14305 pts/5 R+ 0:30 fceu/fce -readwrite 1 -bisqbot 1 -state 4 -play tmp2.fcm lunarball.nes
14308 pts/5 R+ 0:21 fceu/fce -readwrite 1 -bisqbot 1 -state 4 -play tmp2.fcm lunarball.nes
14311 pts/5 R+ 0:19 fceu/fce -readwrite 1 -bisqbot 1 -state 4 -play tmp2.fcm lunarball.nes
14322 pts/6 R+ 0:00 ps w
bisqwit@chii:~/nes$ uptime
21:59:09 up 3:11, 9 users, load average: 4.12, 3.73, 2.23
bisqwit@chii:~/nes$ sensors|egrep 'temp[12]|Core'
temp1: +50.0C (high = -43.0C, hyst = -3.0C) ALARM sensor = thermistor
temp2: +55.0C (high = +80.0C, hyst = +75.0C) sensor = diode
Core 0: +66.0C (high = +82.0C, crit = +100.0C)
Core 1: +67.0C (high = +82.0C, crit = +100.0C)
Core 2: +62.0C (high = +82.0C, crit = +100.0C)
Core 3: +62.0C (high = +82.0C, crit = +100.0C)
bisqwit@chii:~/nes$ tail -f lunarball.log
k[7518]ang[ 93]vel[255]preang[ 93]prevel[252]: p=1, f=292(ef=0), 577.5488,
k[7517]ang[ 93]vel[255]preang[ 93]prevel[249]: p=1, f=293(ef=0), 573.0179,
k[7515]ang[ 93]vel[255]preang[ 93]prevel[243]: p=1, f=221(ef=0), 761.1994,
Ignoring: Pocketed 1 (time 221, angle 93, velo 255, preangle 93, prevelo 243) (ball at 4D,B7)
k[7513]ang[ 93]vel[255]preang[ 93]prevel[237]: p=1, f=222(ef=0), 757.7706,
k[7511]ang[ 93]vel[255]preang[ 93]prevel[231]: p=1, f=224(ef=0), 751.0048,
k[7510]ang[ 93]vel[255]preang[ 93]prevel[228]: p=1, f=225(ef=0), 747.6669,
k[7507]ang[ 93]vel[255]preang[ 93]prevel[219]: p=1, f=228(ef=0), 737.8292,
k[7506]ang[ 93]vel[255]preang[ 93]prevel[216]: p=1, f=229(ef=0), 734.6072,
k[7287]ang[ 91]vel[255]preang[ 91]prevel[ 39]: p=0, f=665(ef=0), 74.6126,
k[7281]ang[ 91]vel[255]preang[ 91]prevel[ 21]: p=1, f=642(ef=0), 0.0000, pessimistic
I'm recalculating the first shot of stage 34 just in case the one produced in the fcm was not yet a final decision.