localframe=0;-- number of frames (current value is current frame count, incremented at the start of a new frame)
localattempt=1;-- number of attempts (current value is current attempt, incremented after the end of an attempt)
localsegment=1;-- number of segments (current value is current segment, incremented after the end of a segment)
localokattempts=0;-- number of successfull attempts (including rollback)
localfailattempts=0;-- number of failed attempts (including rollback)
localsegments={};-- table that holds every segment, each segment is another table that consists of the score, ties, savestate (begin of segment), lastkeys, keys pressed, etc.
segments[1]={};-- initialize the first segment, we initialize the savestate right after the before code has ran
-- these dont have to be used, but it makes it easier to control here
localmaxframes=400;
localmaxattempts=200;
localmaxsegments=100;
localplayingbest=false;-- when going to the next segment, we need to play the best segment to record, this indicates when we're doing so
localkeyrecording1={};-- every key pressed for player 1 is put in here
localkeyrecording2={};-- every key pressed for player 2 is put in here
-- some constants/macro's/whatever to make source easier to read
localpress=maxvalue;
localrelease=minvalue;
localyes=maxvalue;
localmaybe=maxvalue/2;-- 50%
localno=minvalue;
-- static constants, will be used by the frontend later
localX=95;
localY=30;
localZ=0;
localP=0;
localQ=0;
localvars={};-- variable table. each cell holds a variable. variables are remembered accross segments
-- user defined functions
localfunctiongetScore()-- score of current attempt
localresult=no;
-- SCORE
returnresult;
end;
localfunctiongetTie1()-- tie breaker of current attempt in case score is equal
localresult=no;
-- TIE1
returnresult;
end;
localfunctiongetTie2()-- second tie breaker
localresult=no;
-- TIE2
returnresult;
end;
localfunctiongetTie3()-- third tie breaker
localresult=no;
-- TIE3
returnresult;
end;
localfunctiongetTie4()-- fourth tie breaker
localresult=no;
-- TIE4
returnresult;
end;
localfunctionisRunEnd()-- gets called 3x! twice in the main loop (every frame). determines whether the bot should quit.
localresult=no;
-- ISRUNEND
returnresult;
end;
localfunctionmustRollBack()-- drop back to previous segment? called at the end of a segment
localresult=no;
-- MUSTROLLBACK
returnresult;
end;
localfunctionisSegmentEnd()-- end of current segment? (usually just x frames or being really stuck (to rollback))
localresult=no;
-- ISSEGMENTEND
returnresult;
end;
localfunctionisAttemptOk()-- is current run ok? like, did you die? (then the run is NOT ok... :). return no for no and yes for yes or be left by chance.
localresult=yes;
-- ISATTEMPTOK
returnresult;
end;
localfunctionisAttemptEnd()-- end of current attempt? (like when you die or reach a goal)
localresult=no;
-- ISATTEMPTEND
returnresult;
end;
-- the next 2x8 functions determine whether a button should be pressed for player 1 and 2
-- return yes or no for absolute values, return anything between minvalue and maxvalue
-- to set a chance of pressing that button.
localfunctionpressKeyA1()
localresult=no;
-- bA1
returnresult;
end;
localfunctionpressKeyB1()
localresult=no;
-- bB1
returnresult;
end;
localfunctionpressKeyStart1()
localresult=no;
-- START1
returnresult;
end;
localfunctionpressKeySelect1()
localresult=no;
-- SELECT1
returnresult;
end;
localfunctionpressKeyUp1()
localresult=no;
-- UP1
returnresult;
end;
localfunctionpressKeyDown1()
localresult=no;
-- DOWN1
returnresult;
end;
localfunctionpressKeyLeft1()
localresult=no;
-- LEFT1
returnresult;
end;
localfunctionpressKeyRight1()
localresult=no;
-- RIGHT1
returnresult;
end;
localfunctionpressKeyA2()
localresult=no;
-- bA2
returnresult;
end;
localfunctionpressKeyB2()
localresult=no;
-- bB2
returnresult;
end;
localfunctionpressKeyStart2()
localresult=no;
-- START2
returnresult;
end;
localfunctionpressKeySelect2()
localresult=no;
-- SELECT2
returnresult;
end;
localfunctionpressKeyUp2()
localresult=no;
-- UP2
returnresult;
end;
localfunctionpressKeyDown2()
localresult=no;
-- DOWN2
returnresult;
end;
localfunctionpressKeyLeft2()
localresult=no;
-- LEFT2
returnresult;
end;
localfunctionpressKeyRight2()
localresult=no;
-- RIGHT2
returnresult;
end;
localfunctionpressKeyA3()
localresult=no;
-- bA3
returnresult;
end;
localfunctionpressKeyB3()
localresult=no;
-- bB3
returnresult;
end;
localfunctionpressKeyStart3()
localresult=no;
-- START3
returnresult;
end;
localfunctionpressKeySelect3()
localresult=no;
-- SELECT3
returnresult;
end;
localfunctionpressKeyUp3()
localresult=no;
-- UP3
returnresult;
end;
localfunctionpressKeyDown3()
localresult=no;
-- DOWN3
returnresult;
end;
localfunctionpressKeyLeft3()
localresult=no;
-- LEFT3
returnresult;
end;
localfunctionpressKeyRight3()
localresult=no;
-- RIGHT3
returnresult;
end;
localfunctionpressKeyA4()
localresult=no;
-- bA4
returnresult;
end;
localfunctionpressKeyB4()
localresult=no;
-- bB4
returnresult;
end;
localfunctionpressKeyStart4()
localresult=no;
-- START4
returnresult;
end;
localfunctionpressKeySelect4()
localresult=no;
-- SELECT4
returnresult;
end;
localfunctionpressKeyUp4()
localresult=no;
-- UP4
returnresult;
end;
localfunctionpressKeyDown4()
localresult=no;
-- DOWN4
returnresult;
end;
localfunctionpressKeyLeft4()
localresult=no;
-- LEFT4
returnresult;
end;
localfunctionpressKeyRight4()
localresult=no;
-- RIGHT4
returnresult;
end;
-- now follow the "events", one for the start and end of a frame, attempt, segment and whole bot. none of them need to return anything
localfunctiononStart()-- this code should run before the bot starts, for instance to start the game from power on and get setup the game
-- ONSTART
end;
localfunctiononFinish()-- code ran after the bot finishes
-- ONFINISH
end;
localfunctiononSegmentStart()-- code ran after initializing a new segment, before onAttemptStart(). framecount is always one fewer then actual frame!
-- ONSEGMENTSTART
end;
localfunctiononSegmentEnd()-- code ran after a segment finishes, before cleanup of segment vars
-- ONSEGMENTEND
end;
localfunctiononAttemptStart()-- code ran after initalizing a new attempt, before onInputStart(). not ran when playing back. framecount is always one fewer then actual frame!
-- ONATTEMPTSTART
end;
localfunctiononAttemptEnd(wasOk)-- code ran after an attempt ends before cleanup code, argument is boolean true when attempt was ok, boolean false otherwise. not ran when playing back
-- ONATTEMPTEND
end;
localfunctiononInputStart()-- code ran prior to getting input (keys are empty). not ran when playing back
-- ONINPUTSTART
end;
localfunctiononInputEnd()-- code ran after getting input (lastkey are still valid) (last function before frame ends, you can still manipulate the input here!). not ran when playing back
segments[segment].prev.ok=rand_if(isAttemptOk());-- this is the check whether this attempt was valid or not. if not, it cannot become the best attempt.
-- update ok/failed attempt counters
if(segments[segment].prev.ok)then
okattempts=okattempts+1;
onAttemptEnd(true);
else
failattempts=failattempts+1;
onAttemptEnd(false);
end;
-- if this attempt was better then the previous one, replace it
-- its a long IF, but all it checks (lazy eval) is whether the current
-- score is better then the previous one, or if its equal and the tie1
-- is better then the previous tie1 or if the tie1 is equal to the prev
-- etc... for all four ties. Only tie4 actually needs to be better, tie1
-- through tie3 can be equal as well, as long as the next tie breaks the
-- load the segment savestate to go back to the start of this segment
if(segments[segment].savestate)then-- load segment savestate and try again :)
savestate.load(segments[segment].savestate);
else
fceu.crash();-- this crashes because fceu is a nil table :) as long as gui.popup() doesnt work... we're crashing because no save state exists..? it should never happen.
end;
if(rand_if(isRunEnd()))thenbreak;end;-- if end of run, break out of main loop and run in end loop.
if(notplayingbest)thenonAttemptStart();end;-- only call this when not playing back best attempt. has decreased frame counter!
onFinish();-- allow user cleanup before starting the final botloop
-- now enter an endless loop displaying the results of this run.
while(true)do
if(segments[segment].best)thengui.text(30,100,"end: max attempt ["..segment.."] had score = "..segments[segment].best.score);
elseif(segment>1andsegments[segment-1].best)thengui.text(30,100,"end: no best attempt ["..segment.."]\nPrevious best score: "..segments[segment-1].best.score);
elsegui.text(30,100,"end: no best attempt ["..segment.."] ...");end;
FCEU.frameadvance();
end;
-- i dont think it ever reaches this place... perhaps it should, or some event or whatever...
segments=nil;
collectgarbage();-- collect the segment data... anything else is probably not worth it...