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");
-}

Reply via email to