Download bot.cppLanguage: cpp
#include <stdio>
#include <stdarg>
#include <stdlib>
#include <string>
#include <time>
#include <algorithm>
#include <sstream>
#include <vector>
#include <bitset>
#include <deque>
#include <map>
#include <sys> /* For file locking */
#include <sys>
#include <sys>
#include <unistd>
#include <boost>
#include <boost>
#include <boost>
static boost::mt19937 rng;
template<unsigned>
static inline unsigned GetRnd()
{
static boost::uniform_int<int> g(0, mod-1);
return g(rng);
}
static inline unsigned GetRnd(unsigned mod)
{
return boost::uniform_int<int> (0,mod-1) (rng);
}
static class BitSet16bit
{
unsigned* data;
unsigned setcount;
enum { bitsper = 8*sizeof(unsigned) };
enum { max = 0x10000U / bitsper };
public:
BitSet16bit() : data(0)
{
reset();
}
void reset()
{
delete[] data;
data = new unsigned[ max ];
for(size_t a=0; a<max; ++a) data[a] = 0;
setcount = 0;
}
void set(size_t index)
{
if(!operator[] (index))
{
data[ offset(index) ] |= mask(index);
++setcount;
}
}
inline bool operator[] (size_t index) const
{
return data [ offset(index) ] & mask(index);
}
inline size_t count() const
{
return setcount;
}
private:
BitSet16bit(const BitSet16bit&);
void operator=(const BitSet16bit&);
size_t offset(size_t index) const { return index / (bitsper); }
size_t mask(size_t index) const { return 1UL << (index % bitsper); }
} covered_preshots;
static class BitSet32bit
{
unsigned* data;
unsigned setcount;
enum { bitsper = 8*sizeof(unsigned) };
enum { max = (0x10000000U / bitsper)*0x10U };
public:
BitSet32bit() : data(0)
{
reset();
}
void reset()
{
delete[] data;
data = new unsigned[ max ];
for(size_t a=0; a<max; ++a) data[a] = 0;
setcount = 0;
}
void set(size_t index)
{
if(!operator[] (index))
{
data[ offset(index) ] |= mask(index);
++setcount;
}
}
inline bool operator[] (size_t index) const
{
return data [ offset(index) ] & mask(index);
}
inline size_t count() const
{
return setcount;
}
private:
BitSet32bit(const BitSet32bit&);
void operator=(const BitSet32bit&);
size_t offset(size_t index) const { return index / bitsper; }
size_t mask(size_t index) const { return 1UL << (index % bitsper); }
} covered_candidates;
static class deathset
{
int fd;
public:
deathset()
{
fd = open("/tmp/endcueset.dat", O_RDWR | O_CREAT | O_TRUNC, 0644);
reset();
}
void reset()
{
unsigned buf[0x10000];
for(size_t n=0; n<0x10000; ++n) buf[n] = 0;
flock(fd, LOCK_EX);
pwrite(fd, buf, 0x10000*sizeof(unsigned), 0);
flock(fd, LOCK_UN);
}
unsigned operator[] (size_t n) const
{
unsigned b = 0;
flock(fd, LOCK_EX);
pread(fd, &b, sizeof(unsigned), n*sizeof(unsigned));
flock(fd, LOCK_UN);
return b;
}
void set(size_t n, unsigned nframes)
{
unsigned b=0;
flock(fd, LOCK_EX);
pread(fd, &b, sizeof(unsigned), n*sizeof(unsigned));
if(b == 0 || nframes < b)
pwrite(fd, &nframes, sizeof(unsigned), n*sizeof(unsigned));
fdatasync(fd);
flock(fd, LOCK_UN);
}
size_t count() const
{
size_t result = 0;
unsigned buf[0x10000];
for(size_t n=0; n<0x10000; ++n) buf[n] = 0;
flock(fd, LOCK_EX);
pread(fd, buf, 0x10000*sizeof(unsigned), 0);
flock(fd, LOCK_UN);
for(size_t n=0; n<0x10000>= 9899 /* fceux */
# include "../../../types.h"
# include "../../../state.h"
# include "../../../movie.h"
# include "../../../utils/memorystream.h"
# include "../../../fceu.h"
# include "../../../driver.h"
# include "../coroutine.h"
#else
extern "C" {
# include "types.h"
# include "state.h"
# include "movie.h"
# include "fceu.h"
}
# include "coroutine.h"
#endif
#define BISQBOT_INTERNAL
#include "bisqbotdefs.hh"
#include "bisqbot.hh"
int BotFrontDisableVideo=0;
#define INCLUDE_BOT_IN_SAVESTATE 0
struct SaveState
{
std::vector<unsigned> Data;
#if INCLUDE_BOT_IN_SAVESTATE
const BisqBotStateBase* Bot;
SaveState() : Bot(0) { }
~SaveState() { delete Bot; }
#endif
void Create()
{
#if INCLUDE_BOT_IN_SAVESTATE
delete Bot;
#endif
#if defined(FCEU_VERSION_NUMERIC) && FCEU_VERSION_NUMERIC >= 9899 /* fceux */
memorystream ms;
FCEUSS_SaveMS(&ms, 0);
Data.resize(ms.size());
memcpy(&Data[0], ms.buf(), Data.size());
#else
FILE*fp = tmpfile();
FCEUSS_SaveFP(fp);
rewind(fp);
fseek(fp,0,SEEK_END);
long pos = ftell(fp);
rewind(fp);
SaveState result;
Data.resize(pos);
fread(&Data[0], 1, pos, fp);
fclose(fp);
#endif
#if INCLUDE_BOT_IN_SAVESTATE
Bot = BisqBotNewSaveState();
#endif
}
void Load() const
{
#if defined(FCEU_VERSION_NUMERIC) && FCEU_VERSION_NUMERIC >= 9899 /* fceux */
memorystream ms;
ms.write( (const char*) &Data[0], Data.size());
ms.seekg(0);
FCEUSS_LoadFP(&ms, SSLOADPARAM_NOBACKUP);
#else
FILE*fp = tmpfile();
fwrite(&Data[0], 1, Data.size(), fp);
rewind(fp);
FCEUSS_LoadFP(fp, 0);
fclose(fp);
#endif
#if INCLUDE_BOT_IN_SAVESTATE
BisqBotLoadState(Bot);
#endif
}
#if INCLUDE_BOT_IN_SAVESTATE
private:
SaveState(const SaveState& b);
void operator=(const SaveState& b);
#endif
};
static unsigned char CurInput;
static bool Active=false;
static void DisplaySeq(const std::vector<unsigned>& Seq)
{
std::stringstream msg;
for(unsigned a=0; a<Seq.size(); )
{
unsigned c=1, ch=Seq[a];
while(a+c < Seq.size() && Seq[a+c] == ch) ++c;
#if 1
if(c) {
msg << "d(" << c << ",";
unsigned b=0;
if(ch & K_A) { if(b++)msg << '|'; msg << ("K_A"); }
if(ch & K_B) { if(b++)msg << '|'; msg << ("K_B"); }
if(ch & K_SE) { if(b++)msg << '|'; msg << ("K_SE"); }
if(ch & K_ST) { if(b++)msg << '|'; msg << ("K_ST"); }
if(ch & K_U) { if(b++)msg << '|'; msg << ("K_U"); }
if(ch & K_D) { if(b++)msg << '|'; msg << ("K_D"); }
if(ch & K_L) { if(b++)msg << '|'; msg << ("K_L"); }
if(ch & K_R) { if(b++)msg << '|'; msg << ("K_R"); }
if(!b) msg << "0";
msg << (") ");
a += c;
}
#else
msg << c; msg << '*';
unsigned b=0;
if(ch & K_A) { if(b++) msg << ("_"); msg << ("A"); }
if(ch & K_B) { if(b++) msg << ("_"); msg << ("B"); }
if(ch & K_SE) { if(b++) msg << ("_"); msg << ("SE"); }
if(ch & K_ST) { if(b++) msg << ("_"); msg << ("ST"); }
if(ch & K_U) { if(b++) msg << ("_"); msg << ("U"); }
if(ch & K_D) { if(b++) msg << ("_"); msg << ("D"); }
if(ch & K_L) { if(b++) msg << ("_"); msg << ("L"); }
if(ch & K_R) { if(b++) msg << ("_"); msg << ("R"); }
if(!b) msg << ("00");
msg << (", ");
a += c;
#endif
}
BotFrontMsg("%s", msg.str().c_str());
}
static std::vector<unsigned> GlobalSeqSample;
void BisqBotDisplaySeqSample()
{
DisplaySeq(GlobalSeqSample);
BotFrontMsg("\n");
}
void BotFrontMsg(const char*fmt, ...)
{
FILE*fp;
va_list ap;
va_start(ap,fmt);
vfprintf(stderr, fmt, ap);
va_end(ap);
fflush(stderr);
fp = fopen("rockman.log", "at");
if(!fp) fp = fopen("rockman.log", "wt");
if(!fp) return;
flock(fileno(fp), LOCK_EX);
fseek(fp,0,SEEK_END);
va_start(ap,fmt);
fprintf(fp, "[%d]", (int)getpid());
vfprintf(fp, fmt, ap);
va_end(ap);
fclose(fp);
}
void BotFrontStart()
{
fprintf(stderr,"BotFrontStart\n");
BotFrontDisableVideo=1;
Active=true;
}
void BotFrontEnd()
{
fprintf(stderr,"BotFrontEnd\n");
BisqBotReset();
BotFrontDisableVideo=0;
Active=false;
}
void BotFrontToggle()
{
if(!Active)
BotFrontStart();
else
BotFrontEnd();
}
int BotFrontActive()
{
return Active ? 1 : 0;
}
unsigned BotFrontGetKeymask()
{
if(Active)
{
//fprintf(stderr, "CurInput: %02X\n", CurInput);
return CurInput;
}
//fprintf(stderr, "Nul keymask\n");
return 0;
}
/*
FCEU main loop:
1. Emulate frame
2. Read keyboard input (BotFrontIter)
3. Update joypad input (BotFrontGetKeymask)
It is actually possible for keyboard input to be
read multiple times per frame... Currently we hope
this don't happen.
*/
#if defined(FCEU_VERSION_NUMERIC) && FCEU_VERSION_NUMERIC >= 9899 /* fceux */
# define framecount currFrameCounter
#else
extern "C" { extern uint32_t framecount; }
#endif
#include "averages.hh"
#include "mutation.hh"
#include <tr1>
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) { model=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 double GenRatio(double num_pocketed, double nframes)
{
if(nframes < 1) nframes = 1;
return num_pocketed / (nframes+6);
}
static bool SetBest(unsigned num_pocketed, unsigned nframes, unsigned model, bool Force)
{
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 = GenRatio(num_pocketed, nframes);
double ratio_old = GenRatio(best_pocketed, best_time);
/* If the "now" is subpar, reject it. */
if(ratio_now <ratio_old> 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 = GenRatio(ref_pocketed, ref_time);
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>= 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> 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 + 230;
else
max_hope += 3;
if(max_hope <MAX_FRAMES> 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>= 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>= 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 unsigned n_attempts;
static bool transition;
transition = false;
if(verbose) fprintf(stderr, "Waiting until next shot can be shot\n");
n_attempts = 0;
NextFrameWaiter:
if(++n_attempts >= 1000) goto Fail;
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";*/ num_pocketed=0; 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>maximumdistance) maximumdistance=mindist;
}
}
unsigned n_pockets = pocket_x.size();
double result = 0;
for(unsigned n=1; n<16>= 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>> 16) == (oldmodel >> 16))
{
if(mut_amount <0> 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> 0)
{
if(IGNORE_SCORE_DELAYS)
{
static unsigned nf1, nf2; nf1=nf2=0;
while(DoWaitNextShotPossible(
nf1, nf2, false, 9999)) scrReturn(1);
}
else
{
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> 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>
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>
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> AimList;
typedef std::map<AimingMethod> 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(WhitePocketed() || 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(DO_DEATH && WhitePocketed())
{
unsigned model = preang + prevel*256;
fprintf(stderr, "!"); fflush(stderr);
deathset.set(model, nframes == 0 ? 1 : nframes);
num_pocketed = 0;
}
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->first_shot_is_enough = 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 "first_shot_is_enough" */
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->first_shot_is_enough = 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;
if(IGNORE_SCORE_DELAYS)
{
static unsigned nf1; nf1=0;
while(DoWaitNextShotPossible(nf1, nextshot,
false, 9999)) scrReturn(1);
}
else
{
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>& 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>::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>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>& 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> CandidateList.size())
end_candno = CandidateList.size();
static CandidateOptMapType optmap;
optmap.clear();
for(unsigned candno=first_candno; candno <end_candno>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;
if(CandidateList.empty()) return 0;
static std::map<int> 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>= 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);
}
static void CreateSeedF(
CandidateListType& WinnerList,
unsigned model,
bool is_oneshot)
{
/*if(preang >= 110 && preang <0x90>= 0x75 && ang >= 0x75)*/
/*if(ang <40> 170)*/
CandidateType tmp;
bool skip = false;
if(covered_preshots[model >> 16])
(skip=true)/*,fprintf(stderr, "model %08X skipped because of covered_preshots[%04X]\n",
(unsigned)model, (unsigned)(model >> 16))*/;
if(covered_candidates[model])
(skip=true)/*,fprintf(stderr, "model %08X skipped because of covered_candidates[%08X]\n",
(unsigned)model, (unsigned)(model))*/;
if(deathset[model >> 16] && deathset[model & 0xFFFF])
(skip=true)/*,fprintf(stderr, "model %08X skipped because of deathset[%04X]\n",
(unsigned)model, (unsigned)(model & 0xFFFF))*/;
if(!skip)
{
WinnerList.push_back(tmp.SetModel(model, is_oneshot));
if(is_oneshot) covered_preshots.set(model >> 16);
covered_candidates.set(model);
}
/*Winner::SetBest(pok,tim,model);*/
}
int Run()
{
scrBegin;
static CandidateListType WinnerList;
BeginNewShot:
Winner::SetBest(0,0,0, true);
LunarballLaunchManager2ns::MAX_FRAMES = INIT_MAX_FRAMES;
LunarballLaunchManager2ns::BALLS_REMAINING = RAM[0x18E];
WinnerList.clear();
covered_candidates.reset();
covered_preshots.reset();
deathset.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) \
CreateSeedF(WinnerList, \
(ang+vel*256U) + (preang + prevel*256U)*65536U, is_oneshot);
fprintf(stderr, "Beginning new shot. Table=%u, remaining balls=%u; %u on 16-bit donelist, %lu on 32-bit, %u deaths\n",
RAM[0x187], RAM[0x18E],
(unsigned) covered_preshots.count(),
(unsigned long) covered_candidates.count(),
(unsigned) deathset.count()
);
static SaveState itbegin;
itbegin.Create();
/**/
/* This ensures that all pre-shots are covered. */
/**/
{unsigned n_skipped = 0;
for(unsigned preang=0; preang<256; preang += 1)
for(unsigned prevel=18; prevel<256; prevel += 3)
{
size_t a = WinnerList.size();
CreateSeed(0,0, preang,255, preang, prevel, false);
n_skipped += WinnerList.size() == a;
// This will also map out single-shots that cause "death" (cue ball pocketings)
}
if(n_skipped)
fprintf(stderr, "- %u CHOICES SKIPPED??\n", (unsigned)n_skipped);}
/**/
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(DO_DEATH && generationno == 2)
{
for(unsigned angle_begin = 0; angle_begin < 256; angle_begin += 32)
{
std::vector< std::pair<unsigned> > deaths;
for(unsigned d=0; d<65536; ++d)
{
if((d%256) <angle_begin>= angle_begin+32) continue;
if(deathset[d])
deaths.push_back( std::pair<unsigned>
( deathset[d], d ) );
}
std::sort(deaths.begin(), deaths.end());
fprintf(stderr, "Chosen deaths:\n");
for(unsigned dn=0; dn<deaths.size() && dn < 4; ++dn)
{
unsigned d = deaths[dn].second;
fprintf(stderr, "- angle %u, velocity %u, %u frames\n",
d%256, d/256, deaths[dn].first);
for(unsigned ang=0; ang<256; ang += 1)
for(unsigned vel=18; vel<256> 250)
LunarballLaunchManager2ns::MAX_FRAMES = 250;
if(DO_DUALSHOT)
{
/* Each plausible two-shot combination... */
for(unsigned preang=0; preang<256; preang += 1)
for(unsigned prevel=18; prevel<=140; prevel += 3)
{
if(RAM[0x187] == 23 && RAM[0x18E] == 4)
if(preang < 156)continue;
if(RAM[0x187] == 26 && RAM[0x18E] == 4)
if(preang < 9)continue;
if(RAM[0x187] == 30 && RAM[0x18E] == 3)
if(preang < 193)continue;
/*
if(preang==51) preang=52;
if(preang==164) preang=168;
*/
//if(preang==200) preang=247;
static const int dists[] = {
35,1,
50,2,
90,4,
128,6, -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 = 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> 2) CandidateList.clear();
/*
for(unsigned candno=0; candno<CandidateList.size(); ++candno)
while(candno <CandidateList> 2)
for(unsigned a=0; a<NUM_VARIATIONS>> 16]) continue;
covered_candidates.set(c);
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>> 16]) continue;
covered_candidates.set(c);
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, %u deaths, frame limit %u\n",
generationno, (unsigned)CandidateList.size(), (unsigned)WinnerList.size(),
(unsigned) covered_preshots.count(),
(unsigned long) covered_candidates.count(),
(unsigned) deathset.count(),
LunarballLaunchManager2ns::MAX_FRAMES );
}
if(CandidateList.empty())
{
fprintf(stderr, "Out of candidates, ending this generation\n");
break;
}
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>key = candno;
if(candit->first_shot_is_enough)
{
covered_preshots.set( candit->GetModel() >> 16 );
}
}
// 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>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=0;
unsigned time = Winner::GetBestTimeWithPocketCount(n, model);
if(time == 0 || time == 65535) continue;
CandidateType tmp;
tmp.SetModel(model, false); // don't know if it's a first_shot_is_enough 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>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
};
void BotFrontIter()
{
scrBegin;
Restart:
while(!Active)
{
BisqBotEvaluate(0);
BisqBotReset();
scrReturnV;
}
struct timeval tv;
gettimeofday(&tv,NULL);
rng.seed(tv.tv_usec + tv.tv_sec*1000000ULL);
if(!FCEUI_EmulationPaused()) FCEUI_ToggleEmulationPause();
#if 1 /* LUNAR BALL PLAYER */
static LunarballLaunchManager2 code;
while(Active) { code.Run(); scrReturnV; }
BotFrontEnd();
FCEUSS_Save("botfrontL-end");
goto Restart;
#endif
goto Restart;
scrFinishV;
}
Edited by feos: Replaced with a regular code embed. Original code is backed up at https://files.tasvideos.org/2095/archives/injection.7z