Browse Source

Add initial commit with test score file

master
Matthew Faltys 5 years ago
commit
c0bbce87fd
  1. 1
      .gitignore
  2. 28
      Makefile
  3. 929
      deps/reclist.c
  4. 13
      deps/record
  5. 79
      nethack-score.go

1
.gitignore vendored

@ -0,0 +1 @@
bin/

28
Makefile

@ -0,0 +1,28 @@
GOC=go build
GOFLAGS=-a -ldflags '-s'
CGOR=CGO_ENABLED=0
all: stat reclist
run:
go run \
nethack-score.go
stat:
mkdir -p bin/
$(CGOR) $(GOC) $(GOFLAGS) -o bin/nethack-score nethack-score.go
reclist:
mkdir -p bin/
gcc -static deps/reclist.c -o bin/reclist
test:
bin/nethack-score -recordlocation deps/record -reclistlocation bin/reclist
dependencies:
go get github.com/gorilla/mux
go get github.com/unixvoid/glogger
clean:
rm -rf bin/

929
deps/reclist.c vendored

@ -0,0 +1,929 @@
/* List the Nethack record file and the average score.
Nethack 3.4-3.6, 3.3, 3.2 and 3.1 RECORD file formats supported
Output: stdout
RECLIST [-f filename] [-u playername] [-p profession] [-r race]
[-n number] [-ns score] [-np interval] [-d]
[-ds date since] [-du date until]
[-g version]
Check out the comments in the #defines and #includes before compiling
*/
/* J. Lahtinen 8/1993 - 12/2015 internet: jslahtin@gmail.com
Some parts of this program are copied from the Nethack 3.3.0 sources,
the rest I wrote myself.
Tell me if you find any bugs.. including the program telling the death
reason differently from "nethack -s".
When reporting bugs, please include the line in your record file that
reclist doesn't treat properly (copy & paste is just fine).
2015-12-12 - confirmed compatibility to Nethack 3.6.0
- removed the warning of "too" modern game version
2015-02-21 confirmed the compatibility to the leaked Nethack 3.5.0 version
2003-01-20 changed the parameters -ds and -du from long to char* to allow
comparisons of partial date (for example -ds 2003 to list games
finished after the beginning of year 2003)
2002-03-29 v3.4.0
2001-04-07 increased the size of the line buffer to fit Lethe patch scores
2000-09-06 removed the level number from escaping scores
2000-08-21 removed the level number from ascensions
anything in curly braces shown even if the "death" string isn't
(for the move counter added with my moves patch)
2000-08-12 v3.3.1
2000-07-30 added option -ns to show only scores above some limit
2000-01-23 added option -a to select by alignment
2000-01-02 v3.3
updated to understand the file format of game version 3.3.0
removed old compile options NHPLUS and ENDGAME
980731 v3.2.2
removed false append of "with the Amulet" on escaped games
980108 options -ds and -du to delimit game ending dates
971130 v3.2.1
changed the endgame level names to those used by the game
version 3.2.2
970525 don't split lines between [] (the game version 3.2 itself doesn't).
give an error message from parameter -f without a filename..
961124 v3.2, fixed the endgame level indexes changed from game ver.3.1
960628 option -g to select scores of a certain game version
960505 Both 3.1 and 3.2 game versions handled
960421 v3.0: File format changed for game version 3.2
950524 don't print "in the endgame" when the exact end level is shown
I noticed the NhPlus class U missing from the helps, added it there..
950523 2.3
fixed a difference between this and nethack -s in splitting the line
when the death string is 55 characters
made the more accurate endgame level names optional (#define ENDGAME)
950521 alternative definition of getch for unix users
950228 2.2c
"Escaped with the Amulet" added
950117 a negative number in -npX stops display only at the end
-npX always stops at the end
941117 option -npX pauses after about every X lines
940601 -l option reads the logfile.
Option -help works like -?
940517 added the missing "." in the ascensions
940509 "Crushed" and "escaped" ends specified
940406 Make the difference between demigod and demigoddess
940322 2.2a
Endgame- levels specified
940224 the parameter -d displays the date also with the scores on a single
display line
940110 2.2
the parameter -fs may be used to read stdin
931203 2.1b
conio.h headers removed for better operating system compatibility
extra space on "quit" scores removed
uid longer than 1 character caused some errors in unix, fixed
*/
#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#if defined(__MSDOS__)||defined(__OS2__)
#include <conio.h>
#else
#define getch() getc(stdin)
/* getch() reads one character without waiting for CR in MSDOS and OS/2
and it's currently the only thing in conio.h I use */
#endif
/* The functions strnicmp and stricmp are not ANSI, according to the BorlandC
help files. If your compiler doesn't understand them, you can replace them
with the standard functions (and lose the case insensitivity..) with these
following defines (or use suitable case-insensitive functions that your
compiler understands):
#define strnicmp strncmp
#define stricmp strcmp
*/
/* NOTE: With unix you may prefer defining them as strncasecmp and strcasecmp
as below..
*/
#if defined(__unix__)
#define RECORD "/usr/games/lib/nethackdir/record"
#define LOGFILE "/usr/games/lib/nethackdir/logfile"
#ifndef stricmp
#define strnicmp strncasecmp
#define stricmp strcasecmp
#endif
#else
#define RECORD "record"
#define LOGFILE "logfile"
#endif
/* Some defines taken from game sources (src/topten.c) */
#define NAMSZ 10 /* max length of player name */
#define DTHSZ 100 /* max length of "killer" string */
#define ROLESZ 3 /* role length */
#define BUFSZ 256 /* text buffer size */
char *eos(char *p);
char *ver="3.6.0"; /* version number */
/* command line parameters */
int pause=0, /* pause interval */
plin=0, /* lines printed without pause */
pnum=0, /* how many scores to show */
day=0; /* !0 : show dying dates */
long low=0; /* minimum score to show */
char *begdate=NULL, /* date boundaries */
*enddate=NULL,
*name=NULL, /* if only one username is to be listed */
*prole=NULL, /* character class */
*prace=NULL,
*pver=NULL, /* if only scores of a certain version are to be listed */
*pgend=NULL, /* list only this gender */
*palign=NULL; /* only this alignment */
/* other globals used everywhere */
int num=0; /* number of scores listed */
int main(int argc, char *argv[])
{
FILE *inp;
int i;
long sum=0,
score=0;
char line[BUFSZ],
fver[6];
char *filename=RECORD;
long chkline(char *line);
void help(char *prog,char *par,char *explain);
/* Command line parameters */
for (i=1; i<argc; i++) {
if (argv[i][0]=='/')
argv[i][0]='-';
if ((argv[i][0]=='?') || (argv[i][1]=='?') ||
(!stricmp(argv[i],"-help")) || (!stricmp(argv[i],"--help")))
help(argv[0],NULL,NULL);
else if (!(strnicmp(argv[i],"-f",2))) { /* filename */
if (strlen(argv[i])==2) {
i++;
if (i==argc) {
printf("The -f parameter requires a filename.\n");
exit(1);
}
filename=argv[i]; }
else
filename=argv[i]+2;
if (!stricmp(filename,"s"))
filename=NULL; /* stdio */
}
else if (!(stricmp(argv[i],"-l"))) /* logfile */
filename=LOGFILE;
else if (!(strnicmp(argv[i],"-u",2))) /* player name */
if (strlen(argv[i])==2) {
i++;
name=argv[i]; }
else
name=argv[i]+2;
else if (!(strnicmp(argv[i],"-g",2))) { /* game version */
if (strlen(argv[i])==2) {
i++;
pver=argv[i]; }
else
pver=argv[i]+2;
if (!isdigit(pver[0]) || pver[0]>'3') {
sprintf(line,"-g %s",pver);
help(argv[0],line,"Versions later than 3.x.x not supported yet");
}
}
else if (!(strnicmp(argv[i],"-np",3))) /* pause interval, MUST be */
if (strlen(argv[i])==3) { /* checked before -n */
i++;
pause=atoi(argv[i]); }
else
pause=atoi(argv[i]+3);
else if (!(strnicmp(argv[i],"-ns",3))) /* minimum score to list */
if (strlen(argv[i])==3) { /* MUST be checked before -n */
i++;
low=atol(argv[i]); }
else
low=atol(argv[i]+3);
else if (!(strnicmp(argv[i],"-n",2))) /* number */
if (strlen(argv[i])==2) {
i++;
pnum=atoi(argv[i]); }
else
pnum=atoi(argv[i]+2);
else if (!(strnicmp(argv[i],"-ds",3))) /* dates since.. */
if (strlen(argv[i])==3) { /* MUST be checked before -d */
i++;
begdate=argv[i]; }
else
begdate=argv[i]+3;
else if (!(strnicmp(argv[i],"-du",3))) /* dates until.. */
if (strlen(argv[i])==3) { /* MUST be checked before -d */
i++;
enddate=argv[i]; }
else
enddate=argv[i]+3;
else if (argv[i][1]=='d') /* display the dates */
day=1;
else if (!(strnicmp(argv[i],"-p",2))) /* role */
if (strlen(argv[i])==2) {
i++;
prole=argv[i]; }
else
prole=argv[i]+2;
else if (!(strnicmp(argv[i],"-r",2))) { /* race */
if (strlen(argv[i])==2) {
i++;
prace=argv[i]; }
else
prace=argv[i]+2;
}
else if (!(strnicmp(argv[i],"-a",2))) { /* alignment */
if (strlen(argv[i])==2) {
i++;
palign=argv[i]; }
else
palign=argv[i]+2;
if (strlen(palign)>3) {
sprintf(line,"-a %s",palign);
help(argv[0],line,"Alignment should be one of law/neu/cha/l/n/c");
}
}
}
/* Command line ok, let's process the file */
if (!filename)
inp=stdin; /* I don't know if this works on all compilers.. */
else
if (!(inp=fopen(filename,"r"))) {
printf ("File %s not found\n",filename);
exit (2);
}
/* Actually I now think there's no point in this.
* You'll probably see if the output looks funny anyway,
* so you might consider the warning more annoying than helpful. 12.12.2015
fgets(line,sizeof(line),inp);
sscanf(line,"%5s",fver);
if (fver[1]=='.' && fver[3]=='.' && strncmp(fver,"3.6",3) > 0) {
printf ("The file seems to be from a game version newer than me.\n");
printf ("The output may be incorrect.\n\n");
} else if (fver[1] != '.') {
printf ("Not sure about which game version the file is from.\n");
printf ("Version 3.1 assumed for unknown lines.\n\n");
}
*/
printf("%-71s%s"," No Points Name","Hp [max]\n");
/* HERE is the main loop */
while ((!(feof(inp))) && ((!pnum) || (num<pnum))) {
score = chkline(line);
if (pause>0 && plin >= pause) {
printf ("--more--\r");
if (tolower(getch()=='n'))
pnum = num;
printf (" \r");
plin = 0;
}
if (score == -9) {
printf ("\nInvalid file format!\n");
return 1;
}
sum += score;
fgets(line,sizeof(line),inp);
}
if (num)
printf("\n%d scores. Average score %ld\n",num,sum/(long)num);
else
printf("\nNo games found.\n");
if (inp!=stdin)
fclose (inp);
if (pause) getch();
return 0;
}
/* Interpret one line of the file. Check for the game version. */
long chkline
(char *line)
{
long line33 (char *line,int rank);
long line32 (char *line);
long line31 (char *line);
int ver_major, ver_minor, patchlevel,
n;
static int rank=0;
/* Which game version did this score come from? Are we interested in
scores of that version? */
n = sscanf (line, "%d.%d.%d", &ver_major, &ver_minor, &patchlevel);
if (!pver || !strncmp(pver,line,strlen(pver)) || !strncmp(pver,"3.1",3)) {
rank++;
if ((ver_major == 3 && ver_minor >= 3) || ver_major > 3)
return line33(line,rank);
else if (ver_major == 3 && ver_minor == 2)
return line32(line);
else
return line31(line);
}
else if (pver) /* We didn't care about that version */
return 0;
else
return -9; /* I don't understand this version. Newer than 3.3 */
}
/* Interpret a line of game version 3.3 format */
long line33
(char *line, int rank)
{
#define ENDGAMBR 7
#define EGMIN -5 /* last endgame level */
#define EGMAX -1 /* first endgame level */
#define DUNMIN 0 /* minimum dungeonbranch number */
#define DUNMAX 7 /* maximum dungeonbranch number */
long score;
int dtype, dlev, maxlev, hp, maxhp;
char bdate[BUFSZ], /* 9 should be enough in VALID files, but we don't */
ddate[BUFSZ], /* want to crash on invalid entries either.. */
role[ROLESZ+1],
race[ROLESZ+1],
gender[ROLESZ+1],
align[ROLESZ+1],
plname[NAMSZ+1],
death[DTHSZ+1];
char linebuf[BUFSZ],
prtbuf[81],
hpbuf[6];
char *start, *end;
int n,
secline=1,
dayshown=0,
par=0,
hppos;
static char *eglev[] ={"Plane of Earth", /* -1 */
"Plane of Air", /* -2 */
"Plane of Fire", /* -3 */
"Plane of Water", /* -4 */
"Astral Plane"}; /* -5 */
static char *dungeon[] ={"The Dungeons of Doom", /* 0 */
"Gehennom", /* 1 */
"The Gnomish Mines", /* 2 */
"The Quest", /* 3 */
"Sokoban", /* 4 */
"Fort Ludios", /* 5 */
"Vlad's Tower", /* 6 */
"the endgame"}; /* 7 */
/* Fields in the line: version(3.3.n, ignored here), score, dungeon branch,
dlevel, max dlevel, hp, max hp, number of deaths (ignored), birthdate,
death date, uid (ignored), role, race, gender, alignment, name, reason */
n = sscanf(line,
"%*d.%*d.%*d %ld %d %d %d %d %d %*d %s %s %*d %s %s %s %s %[^,],%[^\n\r]",
&score,&dtype,&dlev,&maxlev,&hp,&maxhp,&ddate,&bdate,
role, race, gender, align, plname, death);
if (n<14) /* Invalid line, not all fields found */
return 0L;
/* Are we interested in this playername, role, alignment and deathdate? */
if (role[0]<'A')
return 0L;
if ((prole) && (strnicmp(prole,role,strlen(prole))))
return 0L;
if ((name) && (strcmp(name,plname)))
return 0L;
if ((prace) && (strnicmp(prace,race,strlen(prace))))
return 0L;
if ((palign) && (strnicmp(palign,align,strlen(palign))))
return 0L;
if ((begdate != NULL && strcmp(ddate,begdate) < 0) ||
(enddate != NULL && strcmp(ddate,enddate) > 0))
return 0L;
if (score<low)
return 0L;
sprintf(linebuf,"%3d %10ld %.10s-%s-%s-%s-%s ",
rank,score,plname,role,race,gender,align);
if (!strncmp(death,"escaped",7)) {
strcat(linebuf,"escaped the dungeon");
if (strstr(death,"Amulet"))
strcat(linebuf," with the Amulet");
if (dtype!=ENDGAMBR) /* not in the Endgame */
sprintf(eos(linebuf)," [max level %d]",maxlev);
secline=0;
}
else if (!strncmp(death,"ascended",8)) {
sprintf(eos(linebuf),"ascended to demigod%s-hood",
gender[0] == 'F'? "dess": "");
secline=0;
}
else if (!strncmp(death,"quit",4)) {
strcat(linebuf,"quit");
secline=0;
}
else if (!strncmp(death,"starv",5)) {
strcat(linebuf,"starved to death");
secline=0;
}
else if (!strncmp(death,"choked",6)) {
sprintf(eos(linebuf),"choked on h%s food",
gender[0] == 'F'? "er": "is");
secline=0;
}
else if (!strncmp(death,"poisoned",8))
strcat(linebuf,"was poisoned");
else if (!strncmp(death,"crushed",7))
strcat(linebuf,"was crushed to death");
else if (!strncmp(death,"petrified",9))
strcat(linebuf,"turned to stone");
else
strcat(linebuf,"died");
if (dtype == ENDGAMBR) { /* Endgame levels */
if (strncmp(death,"ascend",6)) { /* No level number for ascensions */
if (dlev >= EGMIN && dlev <= EGMAX) {
sprintf(eos(linebuf)," on the %s",eglev[-dlev - 1]);
} else {
strcat(linebuf," on the Plane of Void");
}
}
} else if (strncmp(death,"escape",6)) {
sprintf(eos(linebuf), " in %s on level %d",
(dtype >= DUNMIN && dtype < DUNMAX)? dungeon[dtype]:
"an unknown dungeon branch",
dlev);
if (dlev != maxlev)
sprintf(eos(linebuf), " [max %d]", maxlev);
}
strcat(linebuf,".");
if (secline)
sprintf(eos(linebuf)," %c%s.",toupper(*death),death+1);
else if (start=strchr(death,'{'))
/* Anyway, print what may be in curly braces. I have the game duration
* appended there with my topten patch
* (http://www.clinet.fi/~walker/nethack.html#moves) */
sprintf(eos(linebuf)," %s",start);
start=linebuf;
if (hp>0)
sprintf(hpbuf,"%d",hp);
else
sprintf(hpbuf,"-");
hppos=70;
while (strlen(start)>=hppos) { /* split the line as necessary */
for (end=start; end<start+hppos-1; end++) {
if (*end == '[')
par=1;
else if (*end == ']')
par=0;
}
while (end > start && (*end != ' ' || par)) {
if (*end == '[')
par=0;
else if (*end == ']')
par=1;
end--;
}
if (end == start) /* There was no good split position, shouldn't happen */
end=start+hppos;
strncpy(prtbuf, start, end-start);
prtbuf[end-start]=0;
printf("%s\n",prtbuf); /* This display line */
plin++;
if (day && !dayshown) {
printf(" %-15s", ddate);
dayshown=1;
} else
printf("%16s"," ");
start=end+1; /* Start position of the NEXT line */
hppos= 57 - strlen(hpbuf);
}
end=eos(start);
while (end < start+hppos && end < linebuf+sizeof(linebuf))
*end++=' ';
sprintf(end,hpbuf);
printf("%s",start);
sprintf(hpbuf,"[%d]",maxhp);
printf("%6s\n",hpbuf);
plin++;
if (day && !dayshown) {
printf(" %-15s\n",ddate);
plin++;
}
num++;
return score;
}
/* Interpret a line of game version 3.2 format */
long line32
(char *line)
{
int n,
pos,
pos2,
dtype,
dlev,
maxlev,
hp,
maxhp,
par=0,
dayshown=0;
static int nro=0;
long score;
char pvm1[BUFSZ],
pvm2[BUFSZ],
chclass,
sex,
sver[10],
death[150]={""},
ascend=0;
char *reason;
long line31(char *line);
static char *dungeon[]={"The Dungeons of Doom",/* 0 */
"Gehennom", /* 1 */
"The Gnomish Mines", /* 2 */
"The Quest", /* 3 */
"Fort Ludios", /* 4 */
"Vlad's Tower", /* 5 */
"the endgame"}; /* 6 */
static char *eglev[]={"Plane of Earth", /* -1 */
"Plane of Air", /* -2 */
"Plane of Fire", /* -3 */
"Plane of Water", /* -4 */
"Astral Plane"}; /* -5 */
nro++;
/* First there is the game version (3.2.something), then comes the actual
score. Then the dungeon branch of death and dlevel, maxdlevel, hp, maxhp,
number of deaths(ignored), dates when the game ended and started,
userid(ignored, character class&sex, name and reason for game ending
*/
n = sscanf (line,"%9s %ld %d %d %d %d %d %*d %s %s %*d %c%c",
&sver,&score,&dtype,&dlev,&maxlev,&hp,&maxhp,
&pvm1,&pvm2,&chclass,&sex);
if (chclass<'A')
return (0L); /* The VMS version may have end line with 'class' < A */
if ((prole) && (prole[0]!=chclass))
return (0L); /* Some other class specified on the command line */
if (score<low)
return 0L;
/* Find the player name. I didn't know how to use sscanf for it without
stopping in the comma after the name when I wrote this.. Anyway,
the name is after the character class and sex, which are the first
alphabets on the line and can never be before position 38.
*/
for (pos=38;!isalpha(line[pos]);pos++); /* pos points to class now */
if (pos>strlen(line)) /* Never is in a valid file. But we don't want */
return (0L); /* to crash just if the file is invalid, do we.. */
pos += 3;
for (pos2=0;(line[pos]!=',')&&(pos2<sizeof(death));pos2++,pos++)
death[pos2]=line[pos];
death[pos2]=0;
if ((name) && (strcmp(name,death)))
return (0L); /* We are only interested in somebody else's scores */
if ((begdate != NULL && strcmp(pvm1,begdate) < 0) ||
(enddate != NULL && strcmp(pvm1,enddate) > 0))
return (0L); /* We're not intrested in dates this early /late */
printf("%3d%11ld ",nro,score);
reason= &line[pos+1];
strcat(death,"-"); /* The name is already there in the beginnin of string */
pos=strlen(death);
death[pos]=chclass; death[pos+1]=0;
/* Here is the part where I'm not yet totally sure everything still works
as it should.. but this worked on the 3.1 files */
if (!(strncmp(reason,"poisoned",8)))
strcat(death," was poisoned");
else if (!(strncmp(reason,"petrified",9)))
strcat(death," turned to stone");
else if (!(strncmp(reason,"starv",5)))
strcat(death," starved to death");
else if (!(strncmp(reason,"quit",4)))
strcat(death," quit");
else if (!(strncmp(reason,"crushed",7)))
strcat(death," was crushed to death");
else if (!(strncmp(reason,"choked",6)))
if (sex=='M')
strcat(death," choked on his food");
else
strcat(death," choked on her food");
else if (!(strncmp(reason,"ascended",8))) {
if (sex=='F')
strcat(death," ascended to demigoddess-hood.");
else
strcat(death," ascended to demigod-hood.");
ascend=1;
}
else if (!(strncmp(reason,"escaped",7))) {
sprintf (death+strlen(death)," escaped the dungeon [max level %d].",maxlev);
ascend=1; /* Not an ascension, but also listed in a special way. */
}
else
strcat(death," died");
if (!ascend) {
pos=strlen(death);
if (dtype==6) /* The endgame */
{
if (dlev < -5 || dlev > -1) {
printf ("Invalid endgame level number %d in file!\n", dlev);
return (0L);
}
sprintf (death+pos," on the %s. ",eglev[-1-dlev]);
/* dlev is negative if you die in the endgame, from -1 to -5 */
}
else {
sprintf (death+pos," in %s",dungeon[dtype]);
if (dtype!=6)
sprintf(death+strlen(death)," on level %d",dlev);
if (maxlev!=dlev)
sprintf(death+strlen(death)," [max %d]. ",maxlev);
else strcat(death,". ");
}
}
if ((strncmp(reason,"quit",4)) && (strncmp(reason,"starvation",10))
&& (!ascend))
{ reason[0]=toupper(reason[0]);
for (pos2=0; reason[pos2]>=' '; pos2++);
reason[pos2++]='.';
reason[pos2]=0;
pos=strlen(death);
strcat(death,reason);
}
pos=strlen(death);
if (pos>54) { /* split line at the last space before position 54 */
while (pos>54 || death[pos]!=' ' || (par>0 && pos>30)) {
if(death[pos]==']') /* but we don't split between [] */
par++;
if(death[pos]=='[')
par--;
pos--;
}
death[pos]=0;
if (day && !dayshown) {
printf ("%s\n %s\t\t",death,pvm1);
dayshown=1;
}
else {
printf("%s\n\t\t",death);
}
plin++;
pos++;
pos2=0;
while (death[pos]) {
death[pos2]=death[pos];
pos++;
pos2++; }
death[pos2]=0;
}
printf("%-54s",death);
pos=0;
if (hp>0)
printf("%3d",hp);
else
printf(" -");
sprintf(death,"[%d]\n",maxhp);
printf("%7s",death);
plin++;
if (day && !dayshown) {
printf (" %s\n",pvm1);
plin++;
}
num++;
return (score);
}
/* Show some help about the arguments */
void help(char *prog, char *par, char *explain)
{
printf ("RECLIST, version %s\n",ver);
printf ("List Nethack 3.1 - 3.5 record file and calculate the average score.\n");
printf("\nUsage: %s [-f file / -fs / -l] [-u name] [-p role] [-r race]\n",
prog);
printf(" [-a alignment] [-g ver] [-nX] [-nsX] [-npX] [-d]");
printf(" [-ds date] [-du date]\n\n");
printf("The options are:\n");
printf("\t-u name list only the scores of player 'name'\n");
printf("\t-p profession list only the scores for one character class\n");
printf("\t-r race race to be listed (since 3.3: hum/dwa/elf/gno/orc)\n");
printf("\t-a alignment list only this alignment (since 3.3: l/n/c)\n");
printf("\t-f filename read the scores from the file 'filename'\n");
printf("\t-fs read the scorelist from stdin\n");
printf("\t-l read the logfile from %s\n", LOGFILE);
printf("\t-g ver list only scores of game version ver\n");
printf("\t-n X list only X scores\n");
printf("\t-np X pause after every X lines\n");
printf("\t-ns X don't list scores below X points\n");
printf("\t-d show also the dates\n");
printf("\t-ds date games ended at or after date\n");
printf("\t-du date games ended at or before date\n");
printf("\nYou may specify the options in any order.\n");
printf("The space after -f, -g, -u, -r, -a, -n, -ns or -np is optional.\n");
if (par)
printf ("\nUnknown parameter %s\n",par);
if (explain)
printf("%s\n", explain);
exit (1);
}
/* If it wasn't a 3.2 record file, we may still check if it makes sense
in the 3.1 format */
long line31 (char *line)
{
int pos, pos2,
dtype, dlev, maxlev,
hp, maxhp,
dayshown=0;
static int nro=0,
plin=1;
long score,
pvm;
char chclass, sex,
death[70]={""},
ascend=0;
char *reason;
static char *dungeon[]={"The Dungeons of Doom",/* 0 */
"Gehennom", /* 1 */
"The Gnomish Mines", /* 2 */
"The Quest", /* 3 */
"Fort Ludios", /* 4 */
"Vlad's Tower", /* 5 */
"the endgame"}; /* 6 */
static char *eglev[]={"Earth", /* 0 */
"Air", /* -1*/
"Fire", /* -2*/
"Water", /* -3*/
"Astral"}; /* -4*/
nro++;
/* The date uses positions 0-5 (YYMMDD), uid (starts) in 7.
After that, the dungeon branch of death and dlevel, maxdlevel, hp, maxhp,
score, character class&sex, name and reason for game ending
*/
sscanf (line,"%ld %*d %d %d %d %d %d %ld %c%c",
&pvm,&dtype,&dlev,&maxlev,&hp,&maxhp,&score,&chclass,&sex);
if (chclass<'A')
return (0); /* The VMS version may have end line with class < A */
if ((prole) && (prole[0]!=chclass))
return (0);
if (score<low)
return 0L;
/* Player name */
for (pos=20;!isalpha(line[pos]);pos++); /* pos points to class now */
if (pos>strlen(line)) /* Never is in a valid file. But we don't want */
return (0); /* to crash just if the file is invalid.. */
pos += 3;
for (pos2=0;(line[pos]!=',')&&(pos2<sizeof(death));pos2++,pos++)
death[pos2]=line[pos];
death[pos2]=0;
if ((name) && (strcmp(name,death)))
return (0);
printf("%3d%11ld ",nro,score);
reason= &line[pos+1];
strcat(death,"-"); /* The name is already there in the beginnin of string */
pos=strlen(death);
death[pos]=chclass; death[pos+1]=0;
if (!(strncmp(reason,"poisoned",8)))
strcat(death," was poisoned");
else if (!(strncmp(reason,"petrified",9)))
strcat(death," turned to stone");
else if (!(strncmp(reason,"starv",5)))
strcat(death," starved to death");
else if (!(strncmp(reason,"quit",4)))
strcat(death," quit");
else if (!(strncmp(reason,"crushed",7)))
strcat(death," was crushed to death");
else if (!(strncmp(reason,"choked",6)))
if (sex=='M')
strcat(death," choked on his food");
else
strcat(death," choked on her food");
else if (!(strncmp(reason,"ascended",8))) {
if (sex=='F')
strcat(death," ascended to demigoddess-hood.");
else
strcat(death," ascended to demigod-hood.");
ascend=1;
}
else if (!(strncmp(reason,"escaped",7))) {
strcat (death+strlen(death)," escaped the dungeon ");
if (!strcmp(reason+7," (with the Amulet)"))
sprintf (death+strlen(death),"[max level %d].",maxlev);
else
strcat (death,"with the Amulet");
ascend=1; /* Not an ascension, but also listed in a special way. */
}
else
strcat(death," died");
if (!ascend) {
pos=strlen(death);
if (dtype==6) /* The endgame */
sprintf (death+pos," in %s on the %s Plane.",
dungeon[dtype],eglev[-dlev]);
/* dlev is negative if you die in the endgame */
else {
sprintf (death+pos," in %s",dungeon[dtype]);
if (dtype!=6)
sprintf(death+strlen(death)," on level %d",dlev);
if (maxlev!=dlev)
sprintf(death+strlen(death)," [max %d].",maxlev);
else strcat(death,".");
}
if (strlen(death)>54) { /* split the line before the death reason */
death[pos]=0;
if (day && !dayshown) {
printf ("%s\n %ld\t\t",death,pvm);
plin++;
dayshown=0;
}
else {
printf("%s\n\t\t",death);
plin++;
}
pos++;
pos2=0;
while (death[pos]) {
death[pos2]=death[pos];
pos++;
pos2++; }
death[pos2]=0;
}
/* if (strlen(death)==55)
death[54]=0; */
}
printf("%-54s",death);
if (hp>0)
printf("%3d",hp);
else
printf(" -");
sprintf(death,"[%d]",maxhp);
printf("%6s\n",death);
plin++;
if ((strncmp(reason,"quit",4)) && (strncmp(reason,"starvation",10)) && (!ascend))
{ reason[0]=toupper(reason[0]);
reason[strlen(reason)-1]=0; /* remove the linefeed at the end */
if (day && !dayshown) {
printf(" %ld",pvm);
dayshown=0;
}
printf("\t\t%s.\n",reason);
plin++;
}
if (day && !dayshown) {
printf (" %ld\n",pvm);
plin++;
}
num++;
if (pause>0 && plin >= pause) {
printf ("--more--\r");
if (tolower(getch()=='n'))
pnum = num;
printf (" \r");
plin = 0;
}
return (score);
}
char *eos(char *p)
{
while (*p)
p++;
return (p);
}

13
deps/record vendored

@ -0,0 +1,13 @@
3.4.3 15420 2 10 10 -2 109 1 20191104 20191103 5 Val Dwa Fem Law squid,killed by a Green-elf, while frozen by a monster's gaze
3.4.3 9966 0 8 8 -1 63 1 20191101 20191101 5 Ran Elf Mal Cha jonathon,killed by a lizard, while frozen by a potion
3.4.3 5770 2 8 8 -1 77 1 20191105 20191105 5 Val Dwa Fem Law squid,killed by a fall onto poison spikes
3.4.3 4856 2 5 5 0 55 1 20191025 20191025 5 Ran Hum Mal Cha jonathon,killed by a killer bee, while praying
3.4.3 2469 0 4 5 -4 63 1 20191107 20191105 5 Val Dwa Fem Law squid,killed by a water moccasin, while helpless
3.4.3 1847 0 4 4 0 27 1 20191101 20191101 5 Pri Elf Mal Cha squid,killed by a falling rock
3.4.3 1526 2 7 7 0 43 1 20191101 20191101 5 Mon Hum Fem Neu jonathon,killed by a giant spider
3.4.3 1332 0 3 4 -2 40 1 20191106 20191106 5 Ran Elf Mal Cha jonathon,killed by an incubus
3.4.3 1132 2 5 6 -2 57 1 20191106 20191106 5 Val Dwa Fem Law jonathon,killed by a rothe, while helpless
3.4.3 1020 2 4 4 0 47 1 20191101 20191101 5 Cav Hum Fem Law squid,killed by a gnome lord, while praying
3.4.3 968 0 4 4 47 47 0 20191105 20191105 5 Sam Hum Mal Law squid,quit
3.4.3 901 0 4 4 -1 25 1 20191025 20191025 5 Tou Hum Mal Neu jonathon,killed by a kitten
3.4.3 852 2 4 4 -3 25 1 20191105 20191105 5 Arc Gno Mal Neu squid,killed by a werejackal

79
nethack-score.go

@ -0,0 +1,79 @@
package main
import (
"flag"
"fmt"
"io/ioutil"
"log"
"net/http"
"os"
"os/exec"
"github.com/gorilla/mux"
"github.com/unixvoid/glogger"
)
var (
loglevel string
recordlocation string
reclistlocation string
port = 8080
)
func main() {
// init runtime args
flag.StringVar(&loglevel, "loglevel", "debug", loglevel)
flag.StringVar(&recordlocation, "recordlocation", "record", recordlocation)
flag.StringVar(&reclistlocation, "reclistlocation", "reclist", reclistlocation)
flag.Parse()
// init config file and logger
initLogger(loglevel)
// make sure record file exists
if _, err := os.Stat(recordlocation); os.IsNotExist(err) {
glogger.Info.Printf("record file not found in %s\n", recordlocation)
fmt.Printf("%s\n", err)
os.Exit(1)
}
// make sure reclist bin exists
if _, err := os.Stat(reclistlocation); os.IsNotExist(err) {
glogger.Info.Printf("reclist binary not found in %s\n", reclistlocation)
fmt.Printf("%s\n", err)
os.Exit(1)
}
// router
router := mux.NewRouter()
router.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
gethighscore(w, r)
}).Methods("GET")
glogger.Info.Println("nethack-score-api running http on", port)
log.Fatal(http.ListenAndServe(fmt.Sprintf(":%d", port), router))
}
func initLogger(loglevel string) {
// init logger
if loglevel == "debug" {
glogger.LogInit(os.Stdout, os.Stdout, os.Stdout, os.Stderr)
} else if loglevel == "cluster" {
glogger.LogInit(os.Stdout, os.Stdout, ioutil.Discard, os.Stderr)
} else if loglevel == "info" {
glogger.LogInit(os.Stdout, ioutil.Discard, ioutil.Discard, os.Stderr)
} else {
glogger.LogInit(ioutil.Discard, ioutil.Discard, ioutil.Discard, os.Stderr)
}
}
func gethighscore(w http.ResponseWriter, r *http.Request) {
// run script
output, err := exec.Command(reclistlocation,
"-f",
recordlocation).CombinedOutput()
if err != nil {
fmt.Printf("%s\n", err)
}
fmt.Fprintf(w, "%s\n", output)
}
Loading…
Cancel
Save