fbdccce9a4
* menuetlibc: fix printf, sprintf * menuetlibc: alias clock() to sysfn 26.9. Not exactly what is required by POSIX, but better than zero * autobuild games/checkers git-svn-id: svn://kolibrios.org@5123 a494cfbc-eb01-0410-851d-a64ba20cac60
397 lines
10 KiB
C++
397 lines
10 KiB
C++
#ifndef _HEADER_HISTORY_H
|
|
#define _HEADER_HISTORY_H
|
|
|
|
#include "position.h"
|
|
#include "hash.h"
|
|
#include "sysproc.h"
|
|
|
|
class THistory
|
|
{
|
|
#ifndef NO_FILES
|
|
public:
|
|
static char FileName[1024];
|
|
#endif
|
|
public:
|
|
THistory(int id = 0) {if (id >= 0) Hid = NHid++; else Hid = id;}
|
|
|
|
int GetId() const {return Hid;}
|
|
static int GetNId() {return NHid;}
|
|
|
|
int Start(const Position &pos) const;
|
|
int Move(const Position &pos, const unsigned char mv[], int nmove) const;
|
|
int Play(const PlayWrite &play) const;
|
|
|
|
#ifndef NO_FILES
|
|
static int InitHFile(char *dname = 0);
|
|
static int HRead(FILE *f, PlayWrite *&play);
|
|
protected:
|
|
int Print(const char *str) const;
|
|
#endif
|
|
|
|
int Hid;
|
|
|
|
static int NHid;
|
|
protected:
|
|
struct TStr
|
|
{
|
|
TStr(const char *ss = 0) : s(0) {(*this) = ss;}
|
|
TStr(const TStr &ss) : s(0) {(*this) = ss.s;}
|
|
~TStr() {(*this) = 0;}
|
|
|
|
TStr &operator=(const char *ss);
|
|
TStr &operator=(const TStr &ss) {return (*this) = ss.s;}
|
|
|
|
operator char*() {return s;}
|
|
operator const char*() const {return s;}
|
|
char &operator*() {return *s;}
|
|
const char &operator*() const {return *s;}
|
|
char &operator[](int i) {return s[i];}
|
|
const char &operator[](int i) const {return s[i];}
|
|
void Extend(int n);
|
|
|
|
friend int operator==(const TStr &s1, const TStr &s2)
|
|
{return strcmp(s1, s2) == 0;}
|
|
friend int operator==(const char *s1, const TStr &s2)
|
|
{return strcmp(s1, s2) == 0;}
|
|
friend int operator==(const TStr &s1, const char *s2)
|
|
{return strcmp(s1, s2) == 0;}
|
|
friend int operator!=(const TStr &s1, const TStr &s2)
|
|
{return strcmp(s1, s2) != 0;}
|
|
friend int operator!=(const char *s1, const TStr &s2)
|
|
{return strcmp(s1, s2) != 0;}
|
|
friend int operator!=(const TStr &s1, const char *s2)
|
|
{return strcmp(s1, s2) != 0;}
|
|
friend int operator>=(const TStr &s1, const TStr &s2)
|
|
{return strcmp(s1, s2) >= 0;}
|
|
friend int operator>=(const char *s1, const TStr &s2)
|
|
{return strcmp(s1, s2) >= 0;}
|
|
friend int operator>=(const TStr &s1, const char *s2)
|
|
{return strcmp(s1, s2) >= 0;}
|
|
friend int operator<=(const TStr &s1, const TStr &s2)
|
|
{return strcmp(s1, s2) <= 0;}
|
|
friend int operator<=(const char *s1, const TStr &s2)
|
|
{return strcmp(s1, s2) <= 0;}
|
|
friend int operator<=(const TStr &s1, const char *s2)
|
|
{return strcmp(s1, s2) <= 0;}
|
|
friend int operator>(const TStr &s1, const TStr &s2)
|
|
{return strcmp(s1, s2) > 0;}
|
|
friend int operator>(const char *s1, const TStr &s2)
|
|
{return strcmp(s1, s2) > 0;}
|
|
friend int operator>(const TStr &s1, const char *s2)
|
|
{return strcmp(s1, s2) > 0;}
|
|
friend int operator<(const TStr &s1, const TStr &s2)
|
|
{return strcmp(s1, s2) < 0;}
|
|
friend int operator<(const char *s1, const TStr &s2)
|
|
{return strcmp(s1, s2) < 0;}
|
|
friend int operator<(const TStr &s1, const char *s2)
|
|
{return strcmp(s1, s2) < 0;}
|
|
|
|
char *s;
|
|
};
|
|
|
|
class THash
|
|
{
|
|
public:
|
|
void init(int _m);
|
|
int operator()(const TStr &str) const;
|
|
protected:
|
|
int m;
|
|
int K[16];
|
|
};
|
|
|
|
struct TTableItem
|
|
{
|
|
TTableItem(const char *s = 0, int k = 0) : str(s), k(k) {}
|
|
TTableItem(const TStr &s, int k = 0) : str(s), k(k) {}
|
|
TTableItem(const TTableItem &t) : str(t.str), k(t.k) {}
|
|
|
|
operator TStr&() {return str;}
|
|
operator const TStr&() const {return str;}
|
|
|
|
TStr str;
|
|
int k;
|
|
};
|
|
};
|
|
|
|
#ifndef NO_FILES
|
|
char THistory::FileName[1024] = "history.che";
|
|
#endif
|
|
int THistory::NHid = 0;
|
|
|
|
#ifndef NO_FILES
|
|
int THistory::Print(const char *str) const
|
|
{
|
|
char *line = new char[30 + strlen(str)];
|
|
if (!line) return 0;
|
|
unsigned long pr_id = GetProcessId();
|
|
if (Hid == -1) sprintf(line, "%lu %s\n", pr_id, str);
|
|
else if (Hid < 0) sprintf(line, "%lu%c %s\n", pr_id, (char)Hid, str);
|
|
else sprintf(line, "%lu:%d %s\n", pr_id, Hid, str);
|
|
FILE *f = fopen(FileName, "at");
|
|
if (!f)
|
|
{
|
|
clock_t cc = clock();
|
|
do {f = fopen(FileName, "at");}
|
|
while(!f && (clock() - cc) <= 0.05 * CLOCKS_PER_SEC);
|
|
}
|
|
if (!f) {delete[] line; return 0;}
|
|
fputs(line, f);
|
|
fclose(f);
|
|
delete[] line;
|
|
return 1;
|
|
}
|
|
#endif
|
|
|
|
int THistory::Start(const Position &pos) const
|
|
{
|
|
#ifndef NO_FILES
|
|
char str[20 + NUM_CELL] = "Start ";
|
|
if (!pos.Write(str + strlen(str), 1)) return 0;
|
|
if (!Print(str)) return 0;
|
|
#endif
|
|
return 1;
|
|
}
|
|
|
|
int THistory::Move(const Position &pos, const unsigned char mv[], int nmove) const
|
|
{
|
|
#ifndef NO_FILES
|
|
char *str = new char[15 + pos.GetLenMvEx(mv, 11)];
|
|
if (!str) return 0;
|
|
sprintf(str, "%d.%s ", (nmove + 1) / 2, (nmove % 2 == 0) ? ".." : "");
|
|
pos.WriteMvEx(mv, str + strlen(str), 11);
|
|
if (!Print(str)) {delete[] str; return 0;}
|
|
delete[] str;
|
|
#endif
|
|
return 1;
|
|
}
|
|
|
|
int THistory::Play(const PlayWrite &play) const
|
|
{
|
|
if (play.GetN() <= 0) return 0;
|
|
Position pos;
|
|
if (play.GetPos(pos, 0) < 0) return 0;
|
|
if (!Start(pos)) return 0;
|
|
int i;
|
|
unsigned char mv[NUM_CELL];
|
|
for (i = 1; i < play.GetN(); i++)
|
|
{
|
|
if (play.GetPos(pos, i - 1) < 0) return 0;
|
|
if (play.GetMove(mv, i) < 0) return 0;
|
|
if (!Move(pos, mv, i)) return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
#ifndef NO_FILES
|
|
int THistory::InitHFile(char *dname)
|
|
{
|
|
if (dname && dname[0])
|
|
{
|
|
char fnm[1024];
|
|
strcpy(fnm, dname);
|
|
int i;
|
|
for (i = strlen(fnm) - 1; i >= 0; i--)
|
|
{
|
|
if (fnm[i] == DIR_SEPARATOR) break;
|
|
}
|
|
if (i >= 0)
|
|
{
|
|
strcpy(fnm + i + 1, FileName);
|
|
strcpy(FileName, fnm);
|
|
}
|
|
}
|
|
int e = 1;
|
|
FILE *f = fopen(FileName, "rt");
|
|
if (f) {e = feof(f); fclose(f);}
|
|
if (!e) return 0;
|
|
f = fopen(FileName, "wt");
|
|
if (!f) return -1;
|
|
fputs("checkers-history_1.1\n", f);
|
|
fclose(f);
|
|
return 1;
|
|
}
|
|
#endif
|
|
|
|
THistory::TStr &THistory::TStr::operator=(const char *ss)
|
|
{
|
|
if (s) delete[] s;
|
|
if (ss)
|
|
{
|
|
s = new char[strlen(ss) + 1];
|
|
strcpy(s, ss);
|
|
}
|
|
else s = 0;
|
|
return *this;
|
|
}
|
|
|
|
void THistory::TStr::Extend(int n)
|
|
{
|
|
if (n <= 0) {(*this) = 0; return;}
|
|
char *ss = s;
|
|
s = new char[n+1];
|
|
if (ss)
|
|
{
|
|
strncpy(s, ss, n);
|
|
s[n] = 0;
|
|
delete[] ss;
|
|
}
|
|
else s[0] = 0;
|
|
}
|
|
|
|
void THistory::THash::init(int _m)
|
|
{
|
|
m = _m;
|
|
for (int i = 0; i < 16; i++)
|
|
{
|
|
K[i] = (2*random(32767) + 1) & ((1 << m) - 1);
|
|
}
|
|
}
|
|
|
|
int THistory::THash::operator()(const TStr &str) const
|
|
{
|
|
int i, r = 0;
|
|
const char *s = str;
|
|
for (i = 0; *s; i = (i+1) & 15) r += *(s++) * K[i];
|
|
r &= (1 << m) - 1;
|
|
return r;
|
|
}
|
|
|
|
#ifndef NO_FILES
|
|
int THistory::HRead(FILE *f, PlayWrite *&play)
|
|
{
|
|
const int MAX_INP_WORD = 100;
|
|
int nplay = 0, mplay = 10;
|
|
play = new PlayWrite[mplay];
|
|
THashTable<TTableItem, TStr, THash> table;
|
|
TStr word;
|
|
char inp_word[MAX_INP_WORD + 1];
|
|
int r, maxword = 0;
|
|
unsigned char ch;
|
|
int i, k = 0, kind = 0, wasspace = 1, nmove = 0;
|
|
for (;;)
|
|
{
|
|
r = (fread(&ch, 1, 1, f) == 1);
|
|
if (!r || isspace(ch))
|
|
{
|
|
if (!wasspace)
|
|
{
|
|
if (kind == 0) kind = 1;
|
|
else if (kind == 2)
|
|
{
|
|
for (i = 0; inp_word[i]; i++)
|
|
{
|
|
inp_word[i] = (char)tolower((unsigned char)inp_word[i]);
|
|
}
|
|
if (strcmp(inp_word, "start") == 0) kind = 5;
|
|
else kind = -1;
|
|
inp_word[0] = 0; k = 0;
|
|
}
|
|
else if (kind == 3)
|
|
{
|
|
nmove *= 2;
|
|
if (k <= 1) nmove--;
|
|
inp_word[0] = 0;
|
|
k = 0; kind = 4;
|
|
}
|
|
else if (kind == 4)
|
|
{
|
|
TTableItem *n_pl = table.find(word);
|
|
if (!n_pl) kind = -1;
|
|
else if (nmove < 1 || nmove > play[n_pl->k].GetN()) kind = -1;
|
|
else
|
|
{
|
|
PlayWrite::PMv pmv;
|
|
if (play[n_pl->k].GetPos(pmv.pos, nmove - 1) < 0) kind = -1;
|
|
else if (!pmv.pos.ReadMv(pmv.mv, inp_word, 1)) kind = -1;
|
|
else
|
|
{
|
|
play[n_pl->k].ClearFrom(nmove);
|
|
if (play[n_pl->k].Add(pmv.mv) != 0) kind = -1;
|
|
else {k = n_pl->k; kind = 11;}
|
|
}
|
|
}
|
|
}
|
|
else if (kind == 5)
|
|
{
|
|
Position pos;
|
|
pos.Read(inp_word, 1);
|
|
if (pos.IsNull()) kind = -1;
|
|
else
|
|
{
|
|
TTableItem *n_pl = table.find(word);
|
|
if (!n_pl)
|
|
{
|
|
table.push(TTableItem(word, nplay));
|
|
n_pl = table.find(word);
|
|
}
|
|
if (!n_pl) kind = -1;
|
|
else
|
|
{
|
|
if (nplay >= mplay)
|
|
{
|
|
PlayWrite *play0 = play;
|
|
int mplay0 = mplay;
|
|
mplay = 2*nplay + 3;
|
|
play = new PlayWrite[mplay];
|
|
if (play0)
|
|
{
|
|
for (i = 0; i < mplay0; i++) play[i] = play0[i];
|
|
delete[] play0;
|
|
}
|
|
}
|
|
n_pl->k = nplay++;
|
|
play[n_pl->k].Add(0, pos);
|
|
k = n_pl->k; kind = 12;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (!r || ch == '\n' || ch == '\r')
|
|
{
|
|
k = 0;
|
|
kind = 0;
|
|
if (!r) break;
|
|
}
|
|
wasspace = 1;
|
|
}
|
|
else
|
|
{
|
|
if (kind == 0)
|
|
{
|
|
if (k >= maxword) word.Extend(2*k + 3);
|
|
word[k++] = ch;
|
|
word[k] = 0;
|
|
}
|
|
else if (kind == 1)
|
|
{
|
|
if (isdigit(ch)) {nmove = ch - '0'; k = 0; kind = 3;}
|
|
else
|
|
{
|
|
inp_word[0] = ch;
|
|
inp_word[1] = 0;
|
|
k = 1; kind = 2;
|
|
}
|
|
}
|
|
else if (kind == 2 || kind == 4 || kind == 5)
|
|
{
|
|
if (k < MAX_INP_WORD)
|
|
{
|
|
inp_word[k++] = ch;
|
|
inp_word[k] = 0;
|
|
}
|
|
}
|
|
else if (kind == 3)
|
|
{
|
|
if (k == 0 && isdigit(ch)) nmove = 10 * nmove + ch - '0';
|
|
else if (ch == '.') k++;
|
|
else kind = -1;
|
|
}
|
|
wasspace = 0;
|
|
}
|
|
}
|
|
return nplay;
|
|
}
|
|
#endif
|
|
|
|
#endif //_HEADER_HISTORY_H
|