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

Reply via email to