static struct LunarFilenameManager
{
char LockFileName[512];
char StateFileName[512];
LunarFilenameManager()
{
int botfrontpid = getpid();
sprintf(LockFileName, "/tmp/botfront-lock-%d", botfrontpid);
sprintf(StateFileName, "botfrontL-middle-%d", botfrontpid);
}
} LunarFilenames;
namespace LunarballLaunchManager2ns{
unsigned MAX_FRAMES;
unsigned BALLS_REMAINING;
}
struct LunarballMethods
{
struct Winner
{
/* number pocketed
best time
best_model
savestate
*/
static void GetBest(unsigned& best_pocketed, unsigned& best_time)
{
best_pocketed=0; best_time=65535;
int flags = O_RDWR | O_CREAT;
int fd = open(LunarFilenames.LockFileName, flags, 0600);
if(fd < 0) return;
struct autocloser { int f;
autocloser(int fd):f(fd){}
~autocloser(){close(f);} }
au(fd);
flock(fd, LOCK_EX);
char Buf[sizeof(unsigned)*30];
if(pread(fd, Buf, sizeof(Buf), 0) < sizeof(Buf)) return;
unsigned* const best_pptr = (unsigned*)&Buf[0];
unsigned* const best_tptr = (unsigned*)&Buf[sizeof(unsigned)*10];
//unsigned* const best_mptr = (unsigned*)&Buf[sizeof(unsigned)*20];
best_pocketed = best_pptr[0];
best_time = best_tptr[0];
}
static unsigned GetBestTimeWithPocketCount(unsigned count, unsigned& model)
{
int flags = O_RDWR | O_CREAT;
int fd = open(LunarFilenames.LockFileName, flags, 0600);
if(fd < 0) return 65535;
struct autocloser { int f;
autocloser(int fd):f(fd){}
~autocloser(){close(f);} }
au(fd);
flock(fd, LOCK_EX);
char Buf[sizeof(unsigned)*30];
if(pread(fd, Buf, sizeof(Buf), 0) < sizeof(Buf)) return 65535;
//unsigned* const best_pptr = (unsigned*)&Buf[0];
unsigned* const best_tptr = (unsigned*)&Buf[sizeof(unsigned)*10];
unsigned* const best_mptr = (unsigned*)&Buf[sizeof(unsigned)*20];
model = best_mptr[count];
return best_tptr[count];
}
static bool SetBest(unsigned num_pocketed, unsigned nframes, unsigned model, bool Force=true)
{
using namespace LunarballLaunchManager2ns;
int flags = O_RDWR | O_CREAT; // | (Force ? O_TRUNC : 0);
int fd = open(LunarFilenames.LockFileName, flags, 0600);
struct autocloser { int f;
autocloser(int fd):f(fd){}
~autocloser(){close(f);} }
au(fd);
flock(fd, LOCK_EX);
char Buf[sizeof(unsigned)*30] = { 0 };
pread(fd, Buf, sizeof(Buf), 0);
unsigned* const best_pptr = (unsigned*)&Buf[0]; // 0x00
unsigned* const best_tptr = (unsigned*)&Buf[sizeof(unsigned)*10]; // 0x28
unsigned* const best_mptr = (unsigned*)&Buf[sizeof(unsigned)*20]; // 0x50
unsigned& best_pocketed = best_pptr[0];
unsigned& best_time = best_tptr[0];
unsigned& best_model = best_mptr[0];
if(!Force)
{
double ratio_now = num_pocketed / (double)(nframes ?nframes:1);
double ratio_old = best_pocketed / (double)(best_time?best_time:1);
/* If the "now" is subpar, reject it. */
if(ratio_now < ratio_old || ratio_now == 0.0)
{
/* If regardless we got a better number of pocketing */
if(num_pocketed != best_pocketed && num_pocketed > 0)
{
unsigned& ref_pocketed = best_pptr[num_pocketed];
unsigned& ref_time = best_tptr[num_pocketed];
unsigned& ref_model = best_mptr[num_pocketed];
double ratio_ref = ref_pocketed / (double)(ref_time?ref_time:1);
if(ratio_now >= ratio_ref || ref_pocketed == 0 || ratio_ref == 0.0)
{
unsigned cur = model%65536;
unsigned pre = model/65536;
fprintf(stderr, "Ignoring: Pocketed %u (time %u, angle %u, velo %u, preangle %u, prevelo %u) (ball at %02X,%02X)\n",
num_pocketed, nframes, cur%256, cur/256,
pre%256, pre/256,
RAM[0x370], RAM[0x330]
);
ref_pocketed = num_pocketed;
ref_time = nframes;
ref_model = model;
pwrite(fd, Buf, sizeof(Buf), 0);
}
}
return false;
}
/*
if(num_pocketed < best_pocketed) return false;
if(num_pocketed == best_pocketed && nframes >= best_time) return false;
*/
if(ratio_now == ratio_old)
{
unsigned now_cur = model%65536, old_cur = best_model%65536;
unsigned now_pre = model/65536, old_pre = best_model/65536;
unsigned now_cur_vel = now_cur/256, old_cur_vel = old_cur/256;
unsigned now_pre_vel = now_pre/256, old_pre_vel = old_pre/256;
if(num_pocketed < best_pocketed) return false;
if(now_cur_vel < old_cur_vel) return false;
if(now_pre_vel > old_pre_vel) return false;
if(model == best_model) return false;
}
}
best_pocketed = num_pocketed;
best_time = nframes;
best_model = model;
best_pptr[best_pocketed] = best_pocketed;
best_tptr[best_pocketed] = best_time;
best_mptr[best_pocketed] = best_model;
pwrite(fd, Buf, sizeof(Buf), 0);
if(!Force)
{
unsigned cur = model%65536;
unsigned pre = model/65536;
fprintf(stderr, "Record: Pocketed %u (time %u, angle %u, velo %u, preangle %u, prevelo %u) (ball at %02X,%02X)\n",
best_pocketed, best_time, cur%256, cur/256,
pre%256, pre/256,
RAM[0x370], RAM[0x330]
);
//beststate.Create();
FCEUSS_Save(LunarFilenames.StateFileName);
if(RAM[0x18E] == 0) // no remaining balls
MAX_FRAMES = std::min(MAX_FRAMES, best_time+3);
}
return true;
}
static void Compete(unsigned num_pocketed, unsigned nframes, unsigned model)
{
SetBest(num_pocketed, nframes, model, false);
ContestLimit(num_pocketed, nframes);
}
static void Load()
{
FCEUSS_Load(LunarFilenames.StateFileName);
}
static void LoadLimit()
{
int flags = O_RDONLY;
int fd = open(LunarFilenames.LockFileName, flags, 0600);
if(fd < 0) return;
struct autocloser { int f;
autocloser(int fd):f(fd){}
~autocloser(){close(f);} }
au(fd);
flock(fd, LOCK_EX);
char Buf[sizeof(unsigned)*30];
if(pread(fd, Buf, sizeof(Buf), 0) < sizeof(Buf)) return;
//unsigned* const best_pptr = (unsigned*)&Buf[0];
unsigned* const best_tptr = (unsigned*)&Buf[sizeof(unsigned)*10];
//unsigned* const best_mptr = (unsigned*)&Buf[sizeof(unsigned)*20];
for(unsigned num_pocketed=1; num_pocketed<10; ++num_pocketed)
{
unsigned nframes = best_tptr[num_pocketed];
ContestLimit(num_pocketed, nframes);
}
}
static void ContestLimit(unsigned num_pocketed, unsigned nframes)
{
if(!num_pocketed || !nframes) return;
using namespace LunarballLaunchManager2ns;
unsigned max_hope = nframes * BALLS_REMAINING
/ num_pocketed;
if(num_pocketed < BALLS_REMAINING)
max_hope += 7 + 160;
else
max_hope += 3;
if(max_hope < MAX_FRAMES)
{
fprintf(stderr, "[%d]Setting max frame limit to %u\n",
(int)getpid(), max_hope);
MAX_FRAMES = max_hope;
}
}
private:
Winner();
};
#undef RunFrameWith
#define RunFrameWith(k) do { CurInput = (k); FCEUI_FrameAdvance(); scrReturn(1); if(!Active)goto EndLoop; } while(0)
struct AimingMethod
{
unsigned char key;
bool autofire;
public:
AimingMethod() : key(0),autofire(false) { }
void Decide(unsigned wanted, unsigned orig_angle = RAM[0x3C0])
{
unsigned plus_dist = (wanted+256-orig_angle) & 255;
unsigned minus_dist = (orig_angle+256-wanted) & 255;
int dir = plus_dist > minus_dist ? -1 : 1;
unsigned dist = std::min(plus_dist, minus_dist);
key = dir == 1 ? K_R : K_L;
unsigned elapse_norepeat = 16 + (dist-1)*1;
unsigned elapse_repeat = 1 + 2*(dist-1);
autofire = elapse_repeat <= elapse_norepeat;
}
bool operator==(const AimingMethod& b) const
{ return key==b.key && autofire==b.autofire; }
bool operator!=(const AimingMethod& b) const
{ return !operator==(b); }
bool operator<(const AimingMethod& b) const
{ return (key*2+autofire) < (b.key*2+b.autofire); }
int Run(unsigned angle, unsigned frno, unsigned& frames_begin, unsigned maxframes)
{
scrBegin;
// autofire: repeatedly press to the direction
// !autofire: hold button until angle is right
while(RAM[0x3C0] != angle)
{
RunFrameWith( (autofire && (frno&1)) ? 0x00 : key);
if(++frames_begin >= maxframes) goto Fail;
}
EndLoop: Fail: ;
scrFinish(0);
}
};
static bool DoAiming(unsigned angle, unsigned& frames_begin, unsigned maxframes)
{
//fprintf(stderr, "#1 aiming for angle %u, f(%u)\n", angle, frames_begin);
scrBegin;
//fprintf(stderr, "#2 aiming for angle %u, f(%u)\n", angle, frames_begin);
static AimingMethod method;
method.Decide(angle);
/*fprintf(stderr, "#M aiming for angle %u, f(%u): %02X,%s, 0x3C0=%u\n",
angle, frames_begin,
method.key, method.autofire?"true":"false", RAM[0x3C0]);*/
static unsigned frno; frno=0;
while(method.Run(angle, frno++, frames_begin, maxframes)) scrReturn(1);
//fprintf(stderr, "#3 aiming for angle %u, f(%u)\n", angle, frames_begin);
scrFinish(0);
}
static bool DoFiring(unsigned& frames_begin, unsigned maxframes)
{
scrBegin;
if(!frames_begin) RunFrameWith(0);
do { RunFrameWith(K_B); ++frames_begin;
// If white was pocketed or maxframes hit
if(WhitePocketed() || frames_begin > maxframes) goto Fail;
} while(RAM[0x3D0] == 0 || RAM[0x3D0] == 0xFF);
EndLoop: Fail:;
scrFinish(0);
}
static bool DoWaitBallsStop(unsigned& nframes, const char*& failreason, unsigned maxframes)
{
scrBegin;
while(RAM[0x3D0] != 0 && RAM[0x3D0] != 0xFF)
{
if(RAM[0x3A0] == 0)
{
// If white ball is not moving, check if all other balls
// have been pocketed
for(unsigned b=1; b<16; ++b)
if(RAM[0x310+b] < 0x10
&& RAM[0x300+b] >= 4) goto NotAllPocketed;
break; // yay!
NotAllPocketed:;
}
if(WhitePocketed()) { failreason="blunder"; goto Fail; }
if(++nframes > maxframes) { failreason="timeout"; goto Fail; }
//if(frames_begin+nframes >= 300) goto Fail;
RunFrameWith(0);
}
EndLoop: Fail:;
scrFinish(0);
}
static bool DoWaitNextShotPossible(
unsigned& nframes_total,
unsigned& nframes_transition,
bool verbose, unsigned maxframes)
{
/* After the shot has been shot, wait
until the cursor can be moved again.
*/
scrBegin;
static SaveState tmp;
static unsigned was_angle, became_angle1, became_angle2;
static bool transition;
transition = false;
if(verbose) fprintf(stderr, "Waiting until next shot can be shot\n");
NextFrameWaiter:
if(!transition && RAM[0x18F] == 0) transition = true;
if(nframes_total-nframes_transition > maxframes) goto Fail;
tmp.Create();
was_angle = RAM[0x3C0];
RunFrameWith(K_R); ++nframes_total; if(transition) ++nframes_transition;
became_angle1 = RAM[0x3C0];
if(became_angle1 == was_angle) goto NextFrameWaiter;
// K_R affected angle, check if K_L also affects angle
tmp.Load(); --nframes_total; if(transition) --nframes_transition;
RunFrameWith(K_L); ++nframes_total; if(transition) ++nframes_transition;
became_angle2 = RAM[0x3C0];
if(became_angle2 == was_angle) goto NextFrameWaiter;
// K_R and K_L both worked
// If Right and Left produced the *same* change, reject this action
if(became_angle1 == became_angle2) goto NextFrameWaiter;
// K_R and K_L produced different changes, so we're ready.
tmp.Load(); --nframes_total; if(transition) --nframes_transition;
EndLoop: Fail: ;
scrFinish(0);
}
static bool DoProfileShot(unsigned& num_pocketed, unsigned& nframes,
const char*& failreason,
unsigned maxframes)
{
scrBegin;
failreason = 0;
// Count how many balls were pocketed before playing
static unsigned pocketed_begin;
pocketed_begin = GetPocketedCount();
// Fire (and wait until it registers the action)
while(DoFiring(nframes, maxframes)) scrReturn(1);
// Wait until all balls stop moving.
while(DoWaitBallsStop(nframes, failreason, maxframes)) scrReturn(1);
if(failreason) goto Fail;
num_pocketed = GetPocketedCount();
if(WhitePocketed()) { failreason="blunder2"; goto Fail; }
/*fprintf(stderr, "frame %u, pocketed %u, begin %u\n",
(unsigned)framecount,
num_pocketed,
pocketed_begin);
*/
if(num_pocketed >= pocketed_begin)
num_pocketed -= pocketed_begin; else num_pocketed=0;
/*EndLoop:*/ Fail: ;
scrFinish(0);
}
static unsigned GetPocketedCount()
{
unsigned result = 0;
for(unsigned a=1; a<16;++a)
{
if(RAM[0x300+a]==0xC0) break;
if((RAM[0x300+a] & 15) != 2 && (RAM[0x300+a] & 15) != 1)
{
//fprintf(stderr, "%u:%02X;", a,RAM[0x300+a]);
++result;
}
}
++result;
return result;
}
static bool WhitePocketed()
{
return (RAM[0x300] & 15) != 2
&& (RAM[0x300] & 15) != 1;
}
static double CalculateProspects()
{
/* Returns a value 0..1 describing how good the board state is */
static std::vector<double> pocket_x;
static std::vector<double> pocket_y;
static int LastInitPockets=-1;
static double maximumdistance;
if(LastInitPockets != RAM[0x187])
{
/* To save CPU time, only regenerate
* the pocket list when the table changes
*/
pocket_x.clear(); pocket_y.clear();
LastInitPockets = RAM[0x187];
for(unsigned y=1; y<19; ++y)
{
for(unsigned x=1; x<25; ++x)
{
unsigned char c = RAM[0x600 + y*26+x];
//fprintf(stderr, "%02X ", c);
#define b(n) (((n)&~0xCC)==0)
if((c & 0x10) == 0x10 // this indicates a pocket-type tile
&& (b(RAM[0x600 + (y-1)*26+x]) // must be next to field
|| b(RAM[0x600 + (y+1)*26+x])
|| b(RAM[0x600 + y*26+(x-1)])
|| b(RAM[0x600 + y*26+(x+1)])))
{
pocket_x.push_back(x);
pocket_y.push_back(y);
//fprintf(stderr, "\npocket at %u,%u", x,y);
}
#undef b
}
//fprintf(stderr, "\n");
}
maximumdistance=0;
unsigned n_pockets = pocket_x.size();
for(unsigned y=1; y<19; ++y)
for(unsigned x=1; x<25; ++x)
{
double mindist=9e39;
for(unsigned p=0; p<n_pockets; ++p)
{
double xd = x-pocket_x[p], yd = y-pocket_y[p];
double dist = std::sqrt(xd*xd+yd*yd);
if(dist < mindist) mindist=dist;
}
if(mindist>maximumdistance) maximumdistance=mindist;
}
}
unsigned n_pockets = pocket_x.size();
double result = 0;
for(unsigned n=1; n<16; ++n)
{
unsigned char BallState = RAM[0x300+n];
if(RAM[0x310+n] == 0 || RAM[0x310+n] >= 0x10) continue; // non-balls
if(BallState == 0x83 || BallState == 0x03 || BallState == 0x00)
{
// The ball does not exist anymore. Perfect.
//result += 1.0;
continue;
}
#if 1
// Find out how close the ball is to the nearest pocket
double ballx = int(RAM[0x370 + n] / 8) - 3;
double bally = int(RAM[0x330 + n] / 8) - 8;
double mindistsquared = 9e39;
for(unsigned p=0; p<n_pockets; ++p)
{
double xdist = ballx - pocket_x[p], ydist = bally - pocket_y[p];
double distsquared = xdist*xdist + ydist*ydist;
if(distsquared < mindistsquared) mindistsquared = distsquared;
}
double dist = std::sqrt(mindistsquared);
/*
fprintf(stderr, "\n[%u/%u][0:%02X 3:%02X 7:%02X] ballx=%6.3f bally=%6.3f dist=%6.3f, maxdist=%g;",
n, framecount,
RAM[0x300+n], RAM[0x330+n], RAM[0x370+n],
ballx,bally,dist, maximumdistance);*/
result += (1.0 - (dist / maximumdistance)) * 0.5;
#endif
}
//fprintf(stderr, "\n");
return result / 6.0;
}
};
struct LunarballLaunchManager2: public LunarballMethods
{
// Define the range of parameters to be passed to Mutate().
static const double MINMUTAATIO = 0.0010; // Minimum value, given to first candidates
static const double MAXMUTAATIO = 0.7800; // Maximum value, given to last candidates
// This controls the curve of mutation distribution
// (larger value = more big mutations, smaller value = more small mutations).
// 1 = even distribution
static const double LOGMUTAATIO = 0.243;
static const unsigned WINNERS = 16; // number of winners to take each round for refinement
// number of variations per each generation (PLUS WINNERS, which are preserved for competition)
// larger number increases the chances of finding the Right Thing
static const unsigned NUM_VARIATIONS = 1900;
// number of entirely randomly generated candidates to add each round
static const unsigned NUM_RANDOMS = 300;
// accept rule when no improvements have been found within this number of generations
// larger number decreases the chances of missing the Right Thing
static const unsigned min_generations_no_improvement = 80;
static const unsigned NUM_FORKS = 4;
static const unsigned NUM_CANDIDATES_PER_PROCESS = 200;
static const unsigned INIT_MAX_FRAMES = 600+3;
static const bool DO_DUALSHOT = true;
/*
Input:
For all
angle
preangle
try some: prevelocity
try all: velocity
find best, then refine it
genetically
*/
struct CandidateType
{
private:
float angle;
float velocity;
float preangle;
float prevelocity;
public:
float scoring;
uint_least16_t confidence;
bool dummy_first;
uint_least32_t key;
public:
CandidateType(): angle(0),velocity(0),preangle(0),prevelocity(0),
scoring(0),confidence(0),dummy_first(false), key(0)
{ }
void Mutate(double mut_amount)
{
retry: ;
unsigned r = lrand48();
unsigned which = 1+(r%15), change = r/15;
unsigned oldmodel = GetModel();
if(dummy_first) { which = 12 + 1+(r%3); change = r/3;}
if(which & 1) angle = fmod(angle + 1 + ((change&1)?1:-1)*mut_amount, 1);
if(which & 2) velocity = fmod(velocity + 1 + ((change&2)?1:-1)*mut_amount, 1);
if(which & 4) preangle = fmod(preangle + 1 + ((change&4)?1:-1)*mut_amount, 1);
if(which & 8) prevelocity = fmod(prevelocity + 1 + ((change&8)?1:-1)*mut_amount, 1);
if(dummy_first)
{
if((GetModel() >> 16) == (oldmodel >> 16))
{
if(mut_amount < 0.4) mut_amount += 0.1;
goto retry;
}
dummy_first = false;
}
}
unsigned GetModel() const
{
unsigned char ang = (unsigned)(angle*256)& 0xFF;
unsigned char vel = (unsigned)(velocity*256) & 0xFF;
unsigned char preang = (unsigned)(preangle*256) & 0xFF;
unsigned char prevel = (unsigned)(prevelocity*256) & 0xFF;
/* Game's velocity gauge goes in increments of 3 */
vel = (vel/3)*3; if(vel < 0x12) vel = 0x12;
prevel = (prevel/3)*3; if(prevel < 0x12) prevel = 0x12;
return (ang + vel*256U) + 65536U * (preang + prevel*256U);
}
CandidateType& SetModel(unsigned model, bool dummy=false)
{
unsigned cur = model%65536;
unsigned pre = model/65536;
angle = (cur%256) / 256.0; preangle = (pre%256) / 256.0;
velocity = (cur/256) / 256.0; prevelocity = (pre/256) / 256.0;
dummy_first = dummy;
return *this;
}
void CreateRandom()
{
angle = drand48(); preangle = drand48();
velocity = drand48(); prevelocity = drand48();
}
static unsigned CountSeeds() { return 0; }
CandidateType& SetSeed(unsigned)
{
return *this;
}
bool BetterScoring(const CandidateType& b) const
{
return scoring > b.scoring;
}
void SaveRes()
{
char candfn[128]; sprintf(candfn, "/tmp/cand%u.res", key);
FILE*fp = fopen(candfn, "wt");
if(fp)
{
fprintf(fp, "%g,%d,%d\n",
scoring,
confidence,
(int)dummy_first);
fclose(fp);
}
else
perror(candfn);
}
void LoadRes()
{
char candfn[128]; sprintf(candfn, "/tmp/cand%u.res", key);
if(FILE*fp = fopen(candfn, "rt"))
{
int dummy, cf;
double sc;
fscanf(fp, "%lg,%d,%d",
&sc,
&cf,
&dummy);
scoring=sc;
confidence=cf;
dummy_first = dummy;
fclose(fp);
}
else
{
perror(candfn);
}
unlink(candfn);
}
};
typedef
std::map<unsigned char/*preang*/,
std::map<unsigned char/*prevel*/,
std::map<unsigned char/*ang*/,
std::map<unsigned char/*vel*/, CandidateType*>
> > > CandidateOptMapType;
struct LunarState: public SaveState
{
unsigned nmp, nmf, *nmpp, *nmfp;
LunarState(): SaveState(), nmp(0), nmf(0), nmpp(&nmp), nmfp(&nmf) { }
void Save(unsigned& np, unsigned& nf)
{ SaveState::Create(); nmp=*(nmpp=&np); nmf=*(nmfp=&nf); }
void Load()
{ SaveState::Load(); *nmpp=nmp; *nmfp=nmf; }
};
bool AnalyzeShotResult(CandidateType& cand, unsigned& num_pocketed, unsigned& nframes,
unsigned ang,unsigned vel,unsigned preang,unsigned prevel)
{
using namespace LunarballLaunchManager2ns;
scrBegin;
static const char* failreason; failreason = 0;
static unsigned nframes_transition; nframes_transition = 0;
#if 1 /* Don't do this, it hinders the finding of good candidates */
if(num_pocketed > 0)
{ unsigned dummy_model, best_time_this_pocket_count
= Winner::GetBestTimeWithPocketCount(num_pocketed,dummy_model);
if(best_time_this_pocket_count > 0
&& best_time_this_pocket_count*5/3 < nframes)
{
/*fprintf(stderr, "estimated %u, got %u\n",
best_time_this_pocket_count, nframes);*/
failreason = "pessimistic";
goto Fail;
}
}
#endif
static double prospects; prospects = CalculateProspects();
if(num_pocketed > 0)
{
while(DoWaitNextShotPossible(
nframes, nframes_transition, false, MAX_FRAMES)) scrReturn(1);
if(nframes-nframes_transition >= MAX_FRAMES) goto Fail;
}
static unsigned nframes_scoring;
nframes_scoring = nframes - nframes_transition;
cand.scoring = num_pocketed;
cand.scoring += prospects;
cand.scoring /= (double)(nframes_scoring ? nframes_scoring : 0);
cand.scoring *= 131072; // arbitrary number to make the scores more readable
++cand.confidence;
goto EndLoop;
Fail:
if(!failreason && nframes-nframes_transition >= MAX_FRAMES) failreason="timeout";
if(!failreason && !num_pocketed) failreason="dummy";
if(!failreason) failreason="fail";
if(nframes < nframes_transition) nframes_transition=0;
EndLoop: ;
// confidence is not increased if a fail happens
fprintf(stderr, "\rk[%04d]ang[%3d]vel[%3d]preang[%3u]prevel[%3u]: p=%u, f=%u(ef=%u), %2.4f, %-18s%s",
cand.key,
ang,vel,preang,prevel,
num_pocketed, nframes-nframes_transition, nframes_transition,
cand.scoring,
failreason ? failreason : "",
(/*num_pocketed &&*/ !failreason) ? "\n" : ""
);
if(!failreason && num_pocketed > 0)
Winner::Compete(num_pocketed,
nframes_scoring,
cand.GetModel());
fflush(stderr);
scrFinish(0);
}
typedef CandidateOptMapType i1s; typedef i1s::iterator i1; // i, preang -> *prevel
typedef i1::value_type::second_type i2s; typedef i2s::iterator i2; // j, prevel -> *ang
typedef i2::value_type::second_type i3s; typedef i3s::iterator i3; // k, ang -> *vel
typedef i3::value_type::second_type i4s; typedef i4s::iterator i4; // l, vel -> *candidate
typedef i4::value_type::second_type i5s;
template<typename VelT, typename NextT>
int RunCandidates_VelocityLoop(VelT& list, NextT& j,
unsigned& num_pocketed, unsigned& nframes, unsigned& vel)
{
using namespace LunarballLaunchManager2ns;
scrBegin;
/* For each prevelocity / velocity */
while(!list.empty())
{
for(;;)
{
vel = RAM[0x3A0];
j = list.find(vel);
if(j != list.end()) break;
if(nframes++ >= MAX_FRAMES) goto Fail;
RunFrameWith(0x00);
}
/* got vel and j */
/* Shoot */
static LunarState shot_state; shot_state.Save(num_pocketed,nframes);
scrReturn(2);
list.erase(j);
if(!list.empty()) shot_state.Load();
}
EndLoop: Fail: ;
scrFinish(0);
}
template<typename AngT, typename NextT>
int RunCandidates_AngleLoop(AngT& list, NextT& k,
unsigned& num_pocketed, unsigned& nframes, unsigned& ang)
{
using namespace LunarballLaunchManager2ns;
scrBegin;
/* For each preangle / angle. */
/* Divide the angles into groups per what kind of input
* is needed to create them. After that, for each group,
* one just need to extend one frame at time until the
* desired frame is met.
*/
typedef std::map<unsigned/*angle*/, NextT> AimList;
typedef std::map<AimingMethod, AimList> AimMap;
static AimMap aims;
aims.clear();
for(k = list.begin(); k != list.end(); ++k)
{
AimingMethod method;
method.Decide(k->first);
aims[method][k->first] = k;
}
static LunarState begin; begin.Save(num_pocketed,nframes);
static typename AimMap::iterator ai;
for(ai = aims.begin(); ai != aims.end(); ++ai)
{
static unsigned aim_framecount; aim_framecount = 0;
if(ai != aims.begin()) begin.Load();
while(!ai->second.empty())
{
for(;;)
{
ang = RAM[0x3C0];
{ typename AimList::iterator j = ai->second.find(ang);
if(j != ai->second.end())
{
k = j->second;
ai->second.erase(j);
break;
} }
if(nframes++ >= MAX_FRAMES) goto FailAim;
RunFrameWith( (ai->first.autofire && (aim_framecount++&1))
? 0x00
: ai->first.key );
}
/* got ang and k */
/* Now each velocity for this angle.... */
static LunarState aim_state; aim_state.Save(num_pocketed,nframes);
scrReturn(2);
if(!ai->second.empty()) aim_state.Load();
}
FailAim: ;
}
EndLoop: ;
scrFinish(0);
}
/* Actual shot */
bool RunCandidates_4do(unsigned& num_pocketed, unsigned& nframes,
i5s& lsecond, unsigned preang, unsigned prevel,
unsigned ang, unsigned vel)
{
using namespace LunarballLaunchManager2ns;
scrBegin;
static const char* failreason; failreason = 0;
while(DoProfileShot(num_pocketed, nframes,
failreason, MAX_FRAMES)) scrReturn(1);
if(failreason)
{
/*fprintf(stderr, "ang[%3d]vel[%3d]preang[%3u]prevel[%3u]: %s\n",
ang,vel,preang,prevel,
failreason);*/
goto Fail;
}
while(AnalyzeShotResult(*lsecond, num_pocketed, nframes,
ang,vel,preang,prevel)) scrReturn(1);
/*EndLoop:*/ Fail: ;
scrFinish(0);
}
/* Preshot */
bool RunCandidates_2do(unsigned& num_pocketed, unsigned& nframes,
i3s& jsecond, unsigned preang, unsigned prevel)
{
using namespace LunarballLaunchManager2ns;
scrBegin;
static const char* failreason; failreason = 0;
while(DoProfileShot(num_pocketed, nframes,
failreason, MAX_FRAMES)) scrReturn(1);
if(failreason)
{
/* Second shot is irrelevant now */
for(i3 k = jsecond.begin(); k != jsecond.end(); ++k)
for(i4 l = k->second.begin(); l != k->second.end(); ++l)
l->second->dummy_first = true;
/*fprintf(stderr, "ang[*]vel[*]preang[%3u]prevel[%3u]: %s\n",
preang,prevel,
failreason);*/
goto Fail;
}
if(num_pocketed > 0)
{
/* Second shot is irrelevant now */
/* Just analyze the shot, put it in one of the candidates
* and mark all of them as "dummy_first" */
static bool first; first=true;
static i3 k;
static i4 l;
for(k = jsecond.begin(); k != jsecond.end(); ++k)
for(l = k->second.begin(); l != k->second.end(); ++l)
{
l->second->dummy_first = true;
if(first)
{
static unsigned ang, vel;
ang = k->first;
vel = l->first;
while(AnalyzeShotResult(*l->second, num_pocketed, nframes,
ang,vel,preang,prevel)) scrReturn(1);
first = false;
}
}
goto EndLoop;
}
if(!DO_DUALSHOT) goto EndLoop;
static unsigned nextshot; nextshot = 0;
while(DoWaitNextShotPossible(nframes, nextshot,
false, MAX_FRAMES)) scrReturn(1);
// ignore transition time, it shouldn't transition here
if(nextshot) { failreason="??transition"; goto Fail; }
/* actual shot */
while(RunCandidates_3(num_pocketed, nframes, jsecond, preang, prevel)) scrReturn(1);
EndLoop: Fail: ;
scrFinish(0);
}
/* Velocity loop */
bool RunCandidates_4(unsigned& num_pocketed, unsigned& nframes,
i4s& ksecond, unsigned preang, unsigned prevel, unsigned ang)
{
scrBegin;
for(;;)
{
static i4 l;
static unsigned vel;
{ int c = RunCandidates_VelocityLoop(ksecond, l, num_pocketed, nframes, vel);
if(!c) break;
if(c == 1) goto Idle;
}
/* got vel and l */
/* Shoot the shot */
while(RunCandidates_4do(num_pocketed, nframes, l->second, preang, prevel, ang, vel)) scrReturn(1);
continue;
Idle: scrReturn(1);
}
scrFinish(0);
}
/* Angle loop */
bool RunCandidates_3(unsigned& num_pocketed, unsigned& nframes,
i3s& jsecond, unsigned preang, unsigned prevel)
{
scrBegin;
for(;;)
{
static i3 k;
static unsigned ang;
{ int c = RunCandidates_AngleLoop(jsecond, k, num_pocketed, nframes, ang);
if(!c) break;
if(c == 1) goto Idle;
}
/* got ang and k */
/* Check each velocity */
while(RunCandidates_4(num_pocketed, nframes, k->second, preang, prevel, ang)) scrReturn(1);
continue;
Idle: scrReturn(1);
}
scrFinish(0);
}
/* Prevelocity */
bool RunCandidates_2(unsigned& num_pocketed, unsigned& nframes,
i2s& isecond, unsigned preang)
{
scrBegin;
for(;;)
{
static i2 j;
static unsigned prevel;
{ int c = RunCandidates_VelocityLoop(isecond, j, num_pocketed, nframes, prevel);
if(!c) break;
if(c == 1) goto Idle;
}
/* got prevel and j */
/* Shoot the preshot -> Check each angle */
while(RunCandidates_2do(num_pocketed, nframes, j->second, preang, prevel)) scrReturn(1);
continue;
Idle: scrReturn(1);
}
scrFinish(0);
}
/* Preangle */
bool RunCandidates_1(unsigned& num_pocketed, unsigned& nframes,
i1s& optmap)
{
scrBegin;
for(;;)
{
static i1 i;
static unsigned preang;
{ int c = RunCandidates_AngleLoop(optmap, i, num_pocketed, nframes, preang);
if(!c) break;
if(c == 1) goto Idle;
}
/* got preang and i */
/* Check each velocity */
while(RunCandidates_2(num_pocketed, nframes, i->second, preang)) scrReturn(1);
continue;
Idle: scrReturn(1);
}
scrFinish(0);
}
bool RunCandidates(const SaveState& itbegin_state, CandidateOptMapType& optmap)
{
scrBegin;
itbegin_state.Load();
static unsigned num_pocketed; num_pocketed = 0;
static unsigned nframes; nframes = 0;
while(RunCandidates_1(num_pocketed, nframes, optmap)) scrReturn(1);
scrFinish(0);
}
typedef std::deque<CandidateType> CandidateListType;
void WaitCandidate(CandidateListType& CandidateList,
std::map<int, unsigned>& pid_list,
bool block)
{
for(int waitflag = block ? 0 : WNOHANG; ; waitflag = WNOHANG)
{
int status = 0;
int pid = waitpid(-1, &status, waitflag);
if(pid <= 0) break;
std::map<int, unsigned>::iterator i = pid_list.find(pid);
if(i == pid_list.end())
{
fprintf(stderr, "Unknown child %d died\n", pid);
}
else
{
const unsigned first_candno = i->second;
unsigned candno = first_candno;
unsigned end_candno = candno + NUM_CANDIDATES_PER_PROCESS;
if(end_candno > CandidateList.size()) end_candno = CandidateList.size();
for(; candno < end_candno; ++candno)
{
CandidateType* candit = &CandidateList[candno];
candit->LoadRes();
}
pid_list.erase(i);
}
if(WIFSIGNALED(status))
{
fprintf(stderr, "Child died at signal %d\n",
WTERMSIG(status));
}
}
}
int LaunchNCandidates
(CandidateListType& CandidateList, const unsigned first_candno,
std::map<int, unsigned>& pid_list,
const SaveState& itbegin)
{
scrBegin;
if(true) // parent
{
Winner::LoadLimit();
int pid;
fflush(stdout);
fflush(stderr);
pid = fork();
if(pid > 0)
{
pid_list[pid] = first_candno;
fprintf(stderr, "."); fflush(stderr);
goto end;
}
if(pid < 0)
{
perror("fork");
goto end;
}
}
// child
MovieForkIntoTempFiles();
static unsigned end_candno;
end_candno = first_candno + NUM_CANDIDATES_PER_PROCESS;
if(end_candno > CandidateList.size())
end_candno = CandidateList.size();
static CandidateOptMapType optmap;
optmap.clear();
for(unsigned candno=first_candno; candno < end_candno; ++candno)
{
CandidateType* candit = &CandidateList[candno];
unsigned model = candit->GetModel();
unsigned cur = model%65536, ang=cur%256, vel=cur/256;
unsigned pre = model/65536, preang=pre%256, prevel=pre/256;
optmap[preang][prevel][ang][vel] = candit;
candit->scoring = 0; // Assume it does really badly
}
while(RunCandidates(itbegin, optmap)) scrReturn(1);
optmap.clear();
for(unsigned candno=first_candno; candno < end_candno; ++candno)
CandidateList[candno].SaveRes();
// terminate child
fflush(stdout);
fflush(stderr);
_exit(0);
end:;
scrFinish(0);
}
int RunCandidates(CandidateListType& CandidateList,
const SaveState& itbegin)
{
scrBegin;
static std::map<int, unsigned> pid_list;
pid_list.clear();
BotFrontDisableVideo=2;
// Sort the new-round candidates, in an order that
// is fastest to execute
// std::sort(CandidateList.begin(), CandidateList.end(),
// std::mem_fun_ref(&CandidateType::SimilarityCompare));
/* As much as I'd like to use OpenMP here, it wouldn't work because it's
* not compatible with coroutines: an omp loop may only have one exit.
*/
static unsigned candno;
for(candno=0; candno<CandidateList.size(); candno+=NUM_CANDIDATES_PER_PROCESS)
{
do {
WaitCandidate(CandidateList, pid_list, pid_list.size() >= NUM_FORKS);
} while(pid_list.size() >= NUM_FORKS);
while(LaunchNCandidates(CandidateList, candno, pid_list, itbegin)) scrReturn(1);
}
BotFrontDisableVideo=1;
while(!pid_list.empty())
{
WaitCandidate(CandidateList, pid_list, true);
}
scrFinish(0);
}
int Run()
{
scrBegin;
static CandidateListType WinnerList;
static std::bitset<0x100000000ULL> covered_candidates;
static std::bitset<0x10000> covered_preshots;
BeginNewShot:
Winner::SetBest(0,0,0);
LunarballLaunchManager2ns::MAX_FRAMES = INIT_MAX_FRAMES;
LunarballLaunchManager2ns::BALLS_REMAINING = RAM[0x18E];
WinnerList.clear();
covered_candidates.reset();
covered_preshots.reset();
/* Append the seed group to this list */
for(unsigned a=0; a< CandidateType::CountSeeds(); ++a)
WinnerList.push_back(CandidateType().SetSeed(a));
#define CreateSeed(pok,tim,ang,vel,preang,prevel, is_oneshot) \
/*if(preang >= 110 && preang < 0x90)*/ \
/*if(preang >= 0x75 && ang >= 0x75)*/ \
/*if(ang < 40 || ang > 170)*/ \
do{ \
CandidateType tmp; \
unsigned model = (ang+vel*256U) + (preang + prevel*256U)*65536U; \
if(!covered_preshots[model >> 16] \
&& !covered_candidates[model]) \
{ \
WinnerList.push_back(tmp.SetModel(model, is_oneshot)); \
if(is_oneshot) covered_preshots[model >> 16] = true; \
covered_candidates[model] = true; \
} \
/*Winner::SetBest(pok,tim,model);*/ \
}while(0)
fprintf(stderr, "Beginning new shot. Table=%u, remaining balls=%u\n",
RAM[0x187], RAM[0x18E]);
if(RAM[0x187] == 1 && RAM[0x18E] == 6)
{
CreateSeed(4, 488, 228,240, 208,138, false);
}
if(RAM[0x187] == 1 && RAM[0x18E] == 2)
{
CreateSeed(2, 346, 56,177, 36,0, false);
CreateSeed(1, 225, 59,174, 35,6, false);
}
if(RAM[0x187] == 2 && RAM[0x18E] == 6)
{
CreateSeed(4, 550, 51,255, 44, 54, false);
CreateSeed(3, 378, 97,252, 102, 36, false);
CreateSeed(1, 127, 245, 60, 33,222, false);
CreateSeed(1, 156, 246,241, 105,141, true);
CreateSeed(2, 276, 0,255, 100,144, true);
CreateSeed(2, 250, 0,255, 161,252, true);
CreateSeed(2, 245, 46,204, 26, 45, false);
}
if(RAM[0x187] == 2 && RAM[0x18E] == 2)
{
CreateSeed(1, 286, 0x9D,0x60,0x81,0x99, false);
CreateSeed(1, 251, 157,189, 34,153, true);
CreateSeed(2, 420, 0x61,0xCC,0x48,0x30, false);
CreateSeed(2, 420, 97,204,77,27, false);
CreateSeed(2, 420, 97,207,77,27, false);
CreateSeed(2, 422, 97,204,77,33, false);
CreateSeed(2, 422, 97,207,72,24, false);
CreateSeed(2, 422, 97,222,72,48, false);
CreateSeed(2, 422, 97,225,77,21, false);
CreateSeed(2, 430, 97,225,77,42, false);
CreateSeed(2, 439, 24,177,40,18, false);
CreateSeed(2, 439, 53,234,13,27, false);
CreateSeed(2, 439, 98,225,53,27, false);
CreateSeed(2, 439, 107,210,57,39, false);
CreateSeed(2, 439, 95,234,97,30, false);
CreateSeed(2, 464, 102,246,109,33, false);
CreateSeed(2, 464, 26,225,14,24, false);
}
if(RAM[0x187] == 4 && RAM[0x18E] == 5)
{/*
CreateSeed(3, 373, 134, 243, 126, 33, false);
CreateSeed(3, 373, 134, 243, 126, 36, false);
CreateSeed(3, 373, 134, 243, 126, 48, false);
CreateSeed(3, 373, 134, 243, 127, 45, false);*/
}
if(RAM[0x187] == 5 && RAM[0x18E] == 7)
{
CreateSeed(1, 191, 84,255, 84,207, true);
CreateSeed(2, 301, 80,255, 80,213, true);
CreateSeed(3, 372, 86,255, 86,255, true);
}
if(RAM[0x187] == 5 && RAM[0x18e] == 4)
{
CreateSeed(3,534,155,255,155,45,false);
CreateSeed(3,506,155,255,155,42,false);
CreateSeed(3,506,155,255,155,39,false);
CreateSeed(3,506,155,255,155,27,false);
CreateSeed(1,303,168,255,168,243,false);
CreateSeed(2,324,170,255,170,249,false);
CreateSeed(3,446,169,255,169,240,false);
CreateSeed(1,303,168,255,168,243,false);
CreateSeed(2,324,170,255,170,249,false);
CreateSeed(4,696,136,246,76,18,false);
CreateSeed(4,693,136,240,76,21,false);
CreateSeed(4,693,136,243,76,21,false);
CreateSeed(4,693,136,240,76,24,false);
CreateSeed(4,693,136,243,76,24,false);
CreateSeed(4,692,136,237,76,45,false);
CreateSeed(4,641,74,246,81,51,false);
CreateSeed(4,572,68,240,88,18,false);
CreateSeed(4,569,68,237,88,21,false);
CreateSeed(4,568,67,243,93,45,false);
CreateSeed(4,568,67,246,93,45,false);
CreateSeed(4,567,67,240,93,51,false);
CreateSeed(4,553,63,210,95,42,false);
}
if(RAM[0x187] == 6 && RAM[0x18E] == 6)
{
CreateSeed(0,400,7,255,7,174,false);
CreateSeed(0,398,7,255,7,168,false);
CreateSeed(0,373,12,255,12,132,false);
CreateSeed(0,364,12,255,12,129,false);
CreateSeed(0,364,12,255,12,126,false);
CreateSeed(0,302,15,255,15,192,false);
CreateSeed(0,302,15,255,15,189,false);
CreateSeed(0,294,15,255,15,174,false);
CreateSeed(0,422,21,255,21,210,false);
CreateSeed(0,379,46,255,46,228,false);
CreateSeed(0,420,40,255,40,255,false);
CreateSeed(0,273,45,255,45,231,false);
CreateSeed(0,264,45,255,45,141,false);
CreateSeed(0,378,47,255,47,228,false);
CreateSeed(0,298,50,255,50,216,false);
CreateSeed(0,241,62,255,62,132,false);
CreateSeed(0,240,62,255,62,117,false);
CreateSeed(0,132,64,255,64,177,false);
CreateSeed(0,365,81,255,81,216,false);
CreateSeed(0,345,81,255,81,243,false);
CreateSeed(0,291,229,255,229,255,false);
CreateSeed(0,132,64,255,64,177,false);
CreateSeed(0,291,229,255,229,255,false);
}
if(RAM[0x187] == 7 && RAM[0x18E] == 7)
{
CreateSeed(0,0, 45,252, 73,24,false);
CreateSeed(0,0, 45,255, 73,24,false);
CreateSeed(0,0, 45,252, 73,27,false);
CreateSeed(0,0, 45,255, 73,27,false);
CreateSeed(0,0, 59,234, 56,42,false);
CreateSeed(0,0, 59,237, 56,42,false);
}
if(RAM[0x187] == 7 && RAM[0x18E] == 2)
{
CreateSeed(2,325, 30,207,39,30, false);
CreateSeed(2,325, 30,207,39,33, false);
CreateSeed(2,325, 30,204,39,45, false);
CreateSeed(1,154, 48,255,48,240, false);
}
if(RAM[0x187] == 8 && RAM[0x18E] == 3)
{
CreateSeed(1,125, 50,255,50,171, false);
CreateSeed(2,203, 33,255,33,234, false);
CreateSeed(3,389, 15,237,27,24, false);
CreateSeed(3,389, 15,240,27,33, false);
CreateSeed(3,389, 15,237,27,33, false);
CreateSeed(3,389, 15,240,27,36, false);
CreateSeed(3,389, 15,237,27,36, false);
CreateSeed(3,389, 15,240,28,21, false);
CreateSeed(3,389, 15,237,28,33, false);
CreateSeed(3,389, 15,240,28,33, false);
}
if(RAM[0x187] == 9 && RAM[0x18E] == 6)
{
CreateSeed(3,263, 7,255, 7,84, false);
CreateSeed(3,263, 246,240, 10,54, false);
//CreateSeed(3,250, 64,210, 34,30, false);
}
/**/
/*
CreateSeed(0,0, 0x9A,0xFC, 0x9F,0x1B, false);
CreateSeed(0,0, 0x9A,0xFF, 0x9F,0x1B, false);
CreateSeed(0,0, 0x9A,0xFC, 0x9F,0x1E, false);
CreateSeed(0,0, 0x9A,0xFF, 0x9F,0x1E, false);
CreateSeed(0,0, 140,237, 57,255, false);*/
/* This ensures that all pre-shots are covered. */
/**/
for(unsigned preang=0; preang<256; preang += 1)
for(unsigned prevel=18; prevel<256; prevel += 3)
CreateSeed(0,0, preang,255, preang, prevel, false);
/**/
static SaveState itbegin;
itbegin.Create();
static unsigned generationno;
static unsigned n_generations_no_improvement;
n_generations_no_improvement = 0;
static double best_scoring;
static CandidateType best_candidate;
best_scoring = -999999;
/* Generate NUM_VARIATIONS for as many generations as needed */
for(generationno=1; Active; ++generationno)
{
static CandidateListType CandidateList;
/* generate new scorings from models */
if(generationno == 2)
{
if(DO_DUALSHOT)
{
/* Each plausible two-shot combination... */
for(unsigned preang=0; preang<256; preang += 1)
for(unsigned prevel=18; prevel<=63; prevel += 3)
{
/*
if(preang==51) preang=52;
if(preang==164) preang=168;
*/
static const int dists[] = {
50,1,/*
20,1,
34,2,
40,3,
70,5,
100,7,
128,14,*/ -1};
int prevdist=0;
for(int n=0; dists[n*2] != -1; ++n)
{
int dist = dists[n*2];
int skip = dists[n*2+1];
for(; prevdist <= dist; prevdist += skip)
{
const unsigned minvel = 198; //171;
for(unsigned vel=minvel; vel<256; vel+=3)
CreateSeed(0, 0, ((preang-prevdist)&255), vel, (preang&0xFF), prevel, false);
if(prevdist != 0)
for(unsigned vel=minvel; vel<256; vel+=3)
CreateSeed(0, 0, ((preang+prevdist)&255), vel, (preang&0xFF), prevel, false);
}
prevdist = dist;
}
}
}
}
CandidateList = WinnerList;
if(generationno > 2) CandidateList.clear();
/*
for(unsigned candno=0; candno<CandidateList.size(); ++candno)
while(candno < CandidateList.size()
&& !covered_candidates[candno])
{
CandidateList.erase(CandidateList.begin() + candno);
}
*/
if(DO_DUALSHOT && !WinnerList.empty() && generationno > 2)
for(unsigned a=0; a<NUM_VARIATIONS; ++a)
{
// Take one of the best as a model, and mutate a copy of it
unsigned randi = lrand48() % WinnerList.size();
CandidateType tmp = WinnerList[randi];
static const double minlog = pow(LOGMUTAATIO, MINMUTAATIO);
static const double maxlog = pow(LOGMUTAATIO, MAXMUTAATIO);
const double logpos = minlog + (a*(maxlog-minlog))/(NUM_VARIATIONS-1);
const double mut_amount = log(logpos) / log(LOGMUTAATIO);
//if(generationno == 1)
// fprintf(stderr, "Gen %u: mut_amount=%g\n", a, mut_amount);
tmp.confidence = 0;
tmp.Mutate(mut_amount);
unsigned c = tmp.GetModel();
if(covered_candidates[c]) continue;
if(covered_preshots[c >> 16]) continue;
covered_candidates[c] = true;
CandidateList.push_back(tmp);
}
//if(generationno==1){Active=0;}
if(DO_DUALSHOT &&
(CandidateList.empty() || n_generations_no_improvement > 0 /*|| generationno==1*/))
for(unsigned a=0; a<NUM_RANDOMS; ++a)
{
CandidateType tmp; tmp.CreateRandom();
unsigned c = tmp.GetModel();
if(covered_candidates[c]) continue;
if(covered_preshots[c >> 16]) continue;
covered_candidates[c] = true;
CandidateList.push_back(tmp);
}
++n_generations_no_improvement;
{
fprintf(stderr, "Generation %u: %u candidates based on %u; %u on 16-bit donelist, %lu on 32-bit\n",
generationno, (unsigned)CandidateList.size(), (unsigned)WinnerList.size(),
(unsigned) covered_preshots.count(),
(unsigned long) covered_candidates.count() );
}
WinnerList.clear(); // list of old winners not needed at this point
for(unsigned candno=0; candno<CandidateList.size(); ++candno)
CandidateList[candno].key = candno;
// Run the candidates
while(RunCandidates(CandidateList, itbegin)) scrReturn(1);
fprintf(stderr, "\r");
for(unsigned candno=0; candno<CandidateList.size(); ++candno)
{
CandidateType* candit = &CandidateList[candno];
candit->key = candno;
if(candit->dummy_first)
{
covered_preshots[candit->GetModel() >> 16] = true;
}
}
// Sort the new-round candidates, best-first
std::sort(CandidateList.begin(), CandidateList.end(),
std::mem_fun_ref(&CandidateType::BetterScoring));
/* Check if there was a new improvement */
for(unsigned candno=0; candno<CandidateList.size(); ++candno)
{
static CandidateType* candit;
candit = &CandidateList[candno];
if(candit->scoring > best_scoring)
{
// reset the no-progress counter
best_scoring = candit->scoring;
best_candidate = *candit;
n_generations_no_improvement = 0;
}
break; // No need to check further, as they're sorted by score
}
/* Always add the winners of each kind for seeding the next breed */
for(unsigned n=1; n<10; ++n)
{
unsigned model, time = Winner::GetBestTimeWithPocketCount(n, model);
if(time == 0) continue;
CandidateType tmp;
tmp.SetModel(model, false); // don't know if it's a dummy_first or not
tmp.confidence = 1;
tmp.scoring = (n / (double)time);
tmp.scoring += n * 1e-5;
tmp.scoring += ((model/0x100)&0xFF) * (1e-5/256.0);
tmp.scoring += ((model/0x1000000)&0xFF) * (1e-5/65536.0);
tmp.scoring *= 131072;
WinnerList.push_front(tmp);
}
for(unsigned n=0; n<WINNERS; ++n)
{
static const double minlog = pow(LOGMUTAATIO, MINMUTAATIO);
static const double maxlog = pow(LOGMUTAATIO, MAXMUTAATIO);
const double logpos = minlog + (n*(maxlog-minlog))/(WINNERS-1);
const double mut_amount = log(logpos) / log(LOGMUTAATIO);
unsigned sel_winner = (unsigned)(mut_amount * CandidateList.size());
sel_winner %= CandidateList.size();
WinnerList.push_back(CandidateList[sel_winner]);
}
Averaging quality_all, quality_best;
for(CandidateListType::const_iterator i=WinnerList.begin();
i!=WinnerList.end(); ++i)
quality_best.Cumulate(i->scoring);
for(CandidateListType::const_iterator i=CandidateList.begin();
i!=CandidateList.end(); ++i)
quality_all.Cumulate(i->scoring);
fprintf(stderr, "Average quality of generation %u: %g (all), %g (inheritors)\n",
generationno,
quality_all.GetValue(), quality_best.GetValue());
if(n_generations_no_improvement >= min_generations_no_improvement
|| !DO_DUALSHOT) break;
} // proceed to next generation
#undef CreateSeed
Winner::Load();
if(true)
{
fprintf(stderr, "Beginning new shot\n");
FCEUSS_Save("botfrontL-newshotbegin");
goto BeginNewShot;
}
/*EndLoop: ; */
BotFrontEnd();
Winner::Load();
FCEUSS_Save("botfrontL-final");
scrFinish(0);
}
#undef RunFrameWith
};