commit c0bbce87fd9843954fda9c9e7fae1366cf090234 Author: Matthew Faltys Date: Thu Nov 7 17:45:33 2019 +0000 Add initial commit with test score file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e660fd9 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +bin/ diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..a665cf9 --- /dev/null +++ b/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/ diff --git a/deps/reclist.c b/deps/reclist.c new file mode 100644 index 0000000..d461b62 --- /dev/null +++ b/deps/reclist.c @@ -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 +#include +#include +#include +#if defined(__MSDOS__)||defined(__OS2__) + #include +#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'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) || (num0 && 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= 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 && (*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 (scorestrlen(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 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 (scorestrlen(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]!=',')&&(pos254) { /* 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); +} diff --git a/deps/record b/deps/record new file mode 100644 index 0000000..63f78ab --- /dev/null +++ b/deps/record @@ -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 diff --git a/nethack-score.go b/nethack-score.go new file mode 100644 index 0000000..62cfef4 --- /dev/null +++ b/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) +}