Bisqwit / Source / Bots / Lunar Ball
<<
Bisqwit / Source / Bots<<
Bisqwit / Source<<
Bisqwit
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