This makes sense to me and keeps the game usable in principle without needing to do silly things. ok beck@
Related is that reading threaded mail in gmail and seeing "Theo" on them so you open them up is insidious. I think I need to change my name to "Theo" as well... On Thu, Nov 26, 2015 at 1:17 AM, Theo Buehler <t...@math.ethz.ch> wrote: > On Wed, Nov 25, 2015 at 03:22:27PM -0500, Ted Unangst wrote: >> Theo Buehler wrote: >> > >> > If you're not going to maintain a list of high scores of the user, you >> > could still simplify snscore() further: The score file will just >> > contain the user's score as a short, so you could get the user name >> > using getlogin(2) instead of doing getuid() getpwuid(), etc. >> > Keeping the struct player also seems unnecessary. >> >> I think this is the way to go, throughout games. The only things we need to >> know are username (getlogin() or even $USER) and home dir ($HOME). Digging >> the >> home dir out of passwd seems unnecessary (or even wrong, if i've set HOME to >> be something else). >> >> For that matter, I'm not sure to prefer USER or getlogin. I'm leaning towards >> USER precisely *because* it allows overriding. If I want to keep a set of >> scores for different users by setting USER that seems reasonable to me. > > I like this. I do prefer probing the environment and not touch the > password file at all. So here's an implementation of a highscore file > with ten top scores and entry customizable via $USER. The logic is > simplistic: you only get an entry in the hall of fame if you strictly > beat a previous score. If $USER is unset, default to "???". > > This is loosely based on Ricardo's patch and what was done in tetris. > > Index: Makefile > =================================================================== > RCS file: /cvs/src/games/snake/Makefile,v > retrieving revision 1.11 > diff -u -p -r1.11 Makefile > --- Makefile 25 Nov 2015 23:18:11 -0000 1.11 > +++ Makefile 26 Nov 2015 08:15:52 -0000 > @@ -2,7 +2,7 @@ > # @(#)Makefile 8.1 (Berkeley) 5/31/93 > > PROG= snake > -SRCS= snake.c snscore.c > +SRCS= snake.c > MAN= snake.6 > DPADD= ${LIBM} ${LIBCURSES} > LDADD= -lm -lcurses > Index: pathnames.h > =================================================================== > RCS file: pathnames.h > diff -N pathnames.h > --- pathnames.h 3 Jun 2003 03:01:41 -0000 1.2 > +++ /dev/null 1 Jan 1970 00:00:00 -0000 > @@ -1,38 +0,0 @@ > -/* $OpenBSD: pathnames.h,v 1.2 2003/06/03 03:01:41 millert Exp $ */ > -/* $NetBSD: pathnames.h,v 1.3 1995/04/22 08:34:33 cgd Exp $ */ > - > -/* > - * Copyright (c) 1989, 1993 > - * The Regents of the University of California. All rights reserved. > - * > - * Redistribution and use in source and binary forms, with or without > - * modification, are permitted provided that the following conditions > - * are met: > - * 1. Redistributions of source code must retain the above copyright > - * notice, this list of conditions and the following disclaimer. > - * 2. Redistributions in binary form must reproduce the above copyright > - * notice, this list of conditions and the following disclaimer in the > - * documentation and/or other materials provided with the distribution. > - * 3. Neither the name of the University nor the names of its contributors > - * may be used to endorse or promote products derived from this software > - * without specific prior written permission. > - * > - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND > - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE > - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE > - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE > - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL > - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS > - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) > - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT > - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY > - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF > - * SUCH DAMAGE. > - * > - * @(#)pathnames.h 8.1 (Berkeley) 5/31/93 > - */ > - > -#define _PATH_RAWSCORES "/var/games/snakerawscores" > -#ifdef LOGGING > -#define _PATH_LOGFILE "/var/games/snake.log" > -#endif > Index: snake.6 > =================================================================== > RCS file: /cvs/src/games/snake/snake.6,v > retrieving revision 1.11 > diff -u -p -r1.11 snake.6 > --- snake.6 13 Nov 2009 21:50:12 -0000 1.11 > +++ snake.6 26 Nov 2015 08:15:52 -0000 > @@ -114,13 +114,18 @@ which appears after the game is worth a > To see who wastes time playing snake, run > .Nm snake > .Fl s . > +.Sh ENVIRONMENT > +.Bl -tag -width Ds > +.It Ev USER > +Name displayed in high score file. > +.El > .Sh FILES > -.Bl -tag -width /var/games/snakerawscores -compact > -.It Pa /var/games/snakerawscores > +.Bl -tag -width $HOME/.snake.scores -compact > +.It Pa $HOME/.snake.scores > database of personal bests > -.\".It Pa /var/games/snake.log > -.\"log of games played > .El > +.\".It Pa $HOME/.snake.log > +.\"log of games played > .Sh BUGS > When playing on a small screen, > it's hard to tell when you hit the edge of the screen. > Index: snake.c > =================================================================== > RCS file: /cvs/src/games/snake/snake.c,v > retrieving revision 1.16 > diff -u -p -r1.16 snake.c > --- snake.c 16 Nov 2014 04:49:49 -0000 1.16 > +++ snake.c 26 Nov 2015 08:15:52 -0000 > @@ -46,9 +46,10 @@ > > #include <curses.h> > #include <err.h> > +#include <errno.h> > #include <fcntl.h> > +#include <limits.h> > #include <math.h> > -#include <pwd.h> > #include <signal.h> > #include <stdio.h> > #include <stdlib.h> > @@ -57,8 +58,6 @@ > #include <time.h> > #include <unistd.h> > > -#include "pathnames.h" > - > #ifdef DEBUG > #define cashvalue (loot-penalty)/25 > #else > @@ -79,7 +78,7 @@ struct point { > #define TREASURE '$' > #define GOAL '#' > > -#define TOPN 3 /* top scores to print if you lose */ > +#define TOPN 10 /* top scores to print if you lose */ > > #define pchar(point, c) mvaddch((point)->line + 1, (point)->col + 1, > (c)) > /* Can't use terminal timing to do delay, in light of X */ > @@ -96,14 +95,25 @@ int moves; > int fast = 1; > > int rawscores; > +FILE *sf; > +char scorepath[PATH_MAX]; > + > +#define SCORES_ENTRIES (TOPN + 1) > + > +struct highscore { > + char name[LOGIN_NAME_MAX]; > + short score; > +} scores[SCORES_ENTRIES]; > +int nscores; > + > #ifdef LOGGING > FILE *logfile; > +char logpath[PATH_MAX]; > #endif > > int lcnt, ccnt; /* user's idea of screen size */ > int chunk; /* amount of money given at a time */ > > -void snscore(int, int); > > void chase(struct point *, struct point *); > int chk(struct point *); > @@ -112,9 +122,11 @@ void length(int); > void mainloop(void); > int post(int, int); > int pushsnake(void); > +int readscores(int); > void setup(void); > void snrand(struct point *); > void snap(void); > +void snscore(int, int); > void spacewarp(int); > void stop(int); > int stretch(struct point *); > @@ -123,6 +135,7 @@ void suspend(void); > void win(struct point *); > void winnings(int); > > + > #ifdef LOGGING > void logit(char *); > #endif > @@ -132,20 +145,18 @@ int wantstop; > int > main(int argc, char *argv[]) > { > - int ch, i; > - struct sigaction sa; > - gid_t gid; > + struct sigaction sa; > + int ch, i; > + > + if (pledge("stdio rpath wpath cpath tty", NULL) == -1) > + err(1, "pledge"); > > - /* don't create the score file if it doesn't exist. */ > - rawscores = open(_PATH_RAWSCORES, O_RDWR, 0664); > #ifdef LOGGING > - logfile = fopen(_PATH_LOGFILE, "a"); > + snprintf(logpath, sizeof(logpath), "%s/%s", getenv("HOME"), > + ".snake.log"); > + logfile = fopen(logpath, "a"); > #endif > > - /* revoke privs */ > - gid = getgid(); > - setresgid(gid, gid, gid); > - > while ((ch = getopt(argc, argv, "hl:stw:")) != -1) > switch ((char)ch) { > case 'w': /* width */ > @@ -155,7 +166,10 @@ main(int argc, char *argv[]) > lcnt = atoi(optarg); > break; > case 's': /* score */ > - snscore(rawscores, 0); > + if (readscores(0)) > + snscore(rawscores, 0); > + else > + printf("no scores so far\n"); > exit(0); > break; > case 't': /* slow terminal */ > @@ -169,6 +183,7 @@ main(int argc, char *argv[]) > exit(1); > } > > + readscores(1); > penalty = loot = 0; > initscr(); > #ifdef KEY_LEFT > @@ -493,35 +508,49 @@ snrand(struct point *sp) > int > post(int iscore, int flag) > { > - short score = iscore; > + struct highscore tmp; > + int rank = nscores; > short oldbest = 0; > - uid_t uid = getuid(); > > /* I want to printf() the scores for terms that clear on endwin(), > * but this routine also gets called with flag == 0 to see if > * the snake should wink. If (flag) then we're at game end and > * can printf. > */ > - if (rawscores == -1) { > - if (flag) > - warnx("Can't open score file %s", _PATH_RAWSCORES); > - return(1); > + if (flag == 0) > + return (iscore > scores[0].score); > + > + rewind(sf); > + > + if (nscores == 0) { > + nscores = 1; > + scores[0].score = iscore; > + oldbest = 0; > + } else { > + oldbest = scores[0].score; > + scores[nscores].score = iscore; > + if (nscores < TOPN) > + nscores++; > + } > + > + /* Insert this joker's current score */ > + while (rank-- > 0 && iscore > scores[rank].score) { > + memcpy(&tmp, &scores[rank], sizeof(struct highscore)); > + memcpy(&scores[rank], &scores[rank+1], sizeof(struct > highscore)); > + memcpy(&scores[rank+1], &tmp, sizeof(struct highscore)); > } > - /* Figure out what happened in the past */ > - lseek(rawscores, uid * sizeof(short), SEEK_SET); > - read(rawscores, &oldbest, sizeof(short)); > - if (!flag) > - return (score > oldbest ? 1 : 0); > - > - /* Update this jokers best */ > - if (score > oldbest) { > - lseek(rawscores, uid * sizeof(short), SEEK_SET); > - write(rawscores, &score, sizeof(short)); > + > + if (rank++ < 0) > printf("\nYou bettered your previous best of $%d\n", oldbest); > - } else > - printf("\nYour best to date is $%d\n", oldbest); > + else if (rank < nscores) > + printf("\nYour score of $%d is ranked %d of all times!\n", > + iscore, rank + 1); > + > + if (fwrite(scores, sizeof(scores[0]), nscores, sf) < nscores) > + err(1, "fwrite"); > + if (fclose(sf)) > + err(1, "fclose"); > > - fsync(rawscores); > /* See if we have a new champ */ > snscore(rawscores, TOPN); > return(1); > @@ -924,6 +953,19 @@ length(int num) > printf("You made %d moves.\n", num); > } > > +void > +snscore(int fd, int topn) > +{ > + int i; > + > + if (nscores == 0) > + return; > + > + printf("%sSnake scores to date:\n", topn > 0 ? "Top " : ""); > + for (i = 0; i < nscores; i++) > + printf("%2d.\t$%d\t%s\n", i+1, scores[i].score, > scores[i].name); > +} > + > #ifdef LOGGING > void > logit(char *msg) > @@ -938,3 +980,53 @@ logit(char *msg) > } > } > #endif > + > +int > +readscores(int create) > +{ > + const char *home; > + const char *user; > + const char *modstr; > + int modint; > + int ret; > + > + if (create == 0) { > + modint = O_RDONLY; > + modstr = "r"; > + } else { > + modint = O_RDWR | O_CREAT; > + modstr = "r+"; > + } > + > + home = getenv("HOME"); > + if (home == NULL || *home == '\0') > + err(1, "getenv"); > + > + ret = snprintf(scorepath, sizeof(scorepath), "%s/%s", home, > + ".snake.scores"); > + if (ret < 0 || ret >= PATH_MAX) > + errc(1, ENAMETOOLONG, "%s/%s", home, ".snake.scores"); > + > + rawscores = open(scorepath, modint, 0666); > + if (rawscores < 0) { > + if (create == 0) > + return 0; > + err(1, "cannot open %s", scorepath); > + } > + if ((sf = fdopen(rawscores, modstr)) == NULL) > + err(1, "cannot fdopen %s", scorepath); > + nscores = fread(scores, sizeof(scores[0]), TOPN, sf); > + if (ferror(sf)) > + err(1, "error reading %s", scorepath); > + > + user = getenv("USER"); > + if (user == NULL || *user == '\0') > + user = "???"; > + > + if (nscores > TOPN) > + nscores = TOPN; > + strlcpy(scores[nscores].name, user, sizeof(scores[nscores].name)); > + scores[nscores].score = 0; > + > + return 1; > +} > Index: snscore.c > =================================================================== > RCS file: snscore.c > diff -N snscore.c > --- snscore.c 18 Nov 2014 20:51:00 -0000 1.11 > +++ /dev/null 1 Jan 1970 00:00:00 -0000 > @@ -1,115 +0,0 @@ > -/* $OpenBSD: snscore.c,v 1.11 2014/11/18 20:51:00 krw Exp $ */ > -/* $NetBSD: snscore.c,v 1.5 1995/04/24 12:25:43 cgd Exp $ */ > - > -/* > - * Copyright (c) 1980, 1993 > - * The Regents of the University of California. All rights reserved. > - * > - * Redistribution and use in source and binary forms, with or without > - * modification, are permitted provided that the following conditions > - * are met: > - * 1. Redistributions of source code must retain the above copyright > - * notice, this list of conditions and the following disclaimer. > - * 2. Redistributions in binary form must reproduce the above copyright > - * notice, this list of conditions and the following disclaimer in the > - * documentation and/or other materials provided with the distribution. > - * 3. Neither the name of the University nor the names of its contributors > - * may be used to endorse or promote products derived from this software > - * without specific prior written permission. > - * > - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND > - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE > - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE > - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE > - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL > - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS > - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) > - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT > - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY > - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF > - * SUCH DAMAGE. > - */ > - > -#include <sys/types.h> > -#include <err.h> > -#include <fcntl.h> > -#include <pwd.h> > -#include <stdio.h> > -#include <stdlib.h> > -#include <string.h> > -#include <unistd.h> > -#include "pathnames.h" > - > -#define MAXPLAYERS 256 > - > -struct player { > - uid_t uids; > - short scores; > - char *name; > -} players[MAXPLAYERS], temp; > - > -void > -snscore(int fd, int topn) > -{ > - uid_t uid; > - short score; > - int noplayers; > - int i, j, notsorted; > - char *q; > - struct passwd *p; > - > - if (fd < 0) { > - fd = open(_PATH_RAWSCORES, O_RDONLY, 0); > - if (fd < 0) > - errx(1, "Couldn't open raw scorefile"); > - } > - > - lseek(fd, 0, SEEK_SET); > - printf("%sSnake scores to date:\n", topn > 0 ? "Top " : ""); > - /* read(fd, &whoallbest, sizeof(uid_t)); > - * read(fd, &allbest, sizeof(short)); SCOREFILE FORMAT CHANGE > - */ > - noplayers = 0; > - for (uid = 0; ; uid++) { > - if (read(fd, &score, sizeof(short)) == 0) > - break; > - if (score > 0) { > - if (noplayers >= MAXPLAYERS) > - errx(2, "Too many entries in scorefile!"); > - players[noplayers].uids = uid; > - players[noplayers].scores = score; > - p = getpwuid(uid); > - if (p == NULL) > - continue; > - q = p -> pw_name; > - if ((players[noplayers].name = strdup(q)) == NULL) > - err(1, "strdup"); > - > - noplayers++; > - } > - } > - > - /* bubble sort scores */ > - for (notsorted = 1; notsorted; ) { > - notsorted = 0; > - for (i = 0; i < noplayers - 1; i++) > - if (players[i].scores < players[i + 1].scores) { > - temp = players[i]; > - players[i] = players[i + 1]; > - players[i + 1] = temp; > - notsorted++; > - } > - } > - > - if ((topn > 0) && (topn < noplayers)) > - noplayers = topn; > - j = 1; > - for (i = 0; i < noplayers; i++) { > - printf("%d:\t$%d\t%s\n", j, players[i].scores, > players[i].name); > - if (i < noplayers - 1 && > - players[i].scores > players[i + 1].scores) > - j = i + 2; > - } > - if (noplayers == 0) > - printf("None.\n"); > -} >