1 week bump.

--
Scott Cheloha

> On Aug 5, 2017, at 8:25 PM, Scott Cheloha <scottchel...@gmail.com> wrote:
> 
> Hi,
> 
> In tetris(6) we use gettimeofday(2) to determine (roughly) how
> long we polled for user input.  This then gets subtracted from
> the time remaining until we drop the block another row.
> 
> This should be computed with a monotonic clock instead, lest
> bullshit like clock drift rob you of that crucial tenth of a
> second and cost you your new high score.
> 
> Moving to a monotonic clock implies using nanoseconds instead of
> microseconds, but this actually turns out to be kind of nice
> because ppoll(2) accepts a timespec structure, so then we can
> delete a few things we were using to jam a timeval into poll(2).
> 
> I've playtested a bit and it doesn't ~feel~ any different.  If
> anything the game *should* feel less choppy under certain
> conditions, though I can't really prove that.
> 
> Feedback?
> 
> --
> Scott Cheloha
> 
> Index: games/tetris/input.c
> ===================================================================
> RCS file: /cvs/src/games/tetris/input.c,v
> retrieving revision 1.18
> diff -u -p -r1.18 input.c
> --- games/tetris/input.c      27 Aug 2016 02:02:44 -0000      1.18
> +++ games/tetris/input.c      6 Aug 2017 01:21:41 -0000
> @@ -40,89 +40,75 @@
>  */
> 
> #include <sys/time.h>
> +
> #include <errno.h>
> #include <poll.h>
> +#include <time.h>
> #include <unistd.h>
> 
> #include "input.h"
> #include "tetris.h"
> 
> -/* return true iff the given timeval is positive */
> -#define      TV_POS(tv) \
> -     ((tv)->tv_sec > 0 || ((tv)->tv_sec == 0 && (tv)->tv_usec > 0))
> -
> -/* subtract timeval `sub' from `res' */
> -#define      TV_SUB(res, sub) \
> -     (res)->tv_sec -= (sub)->tv_sec; \
> -     (res)->tv_usec -= (sub)->tv_usec; \
> -     if ((res)->tv_usec < 0) { \
> -             (res)->tv_usec += 1000000; \
> -             (res)->tv_sec--; \
> -     }
> +/* return true iff the given timespec is positive */
> +#define      TS_POS(ts) \
> +     ((ts)->tv_sec > 0 || ((ts)->tv_sec == 0 && (ts)->tv_nsec > 0))
> 
> /*
> - * Do a `read wait': poll for reading from stdin, with timeout *tvp.
> - * On return, modify *tvp to reflect the amount of time spent waiting.
> + * Do a `read wait': poll for reading from stdin, with timeout *limit.
> + * On return, subtract the time spent waiting from *limit.
>  * It will be positive only if input appeared before the time ran out;
>  * otherwise it will be zero or perhaps negative.
>  *
> - * If tvp is nil, wait forever, but return if poll is interrupted.
> + * If limit is NULL, wait forever, but return if poll is interrupted.
>  *
> - * Return 0 => no input, 1 => can read() from stdin
> + * Return 0 => no input, 1 => can read() from stdin, -1 => interrupted
>  */
> int
> -rwait(struct timeval *tvp)
> +rwait(struct timespec *limit)
> {
> -     int     timo = INFTIM;
> -     struct timeval starttv, endtv;
> +     struct timespec start, end, elapsed;
>       struct pollfd pfd[1];
> 
> -#define      NILTZ ((struct timezone *)0)
> -
> -     if (tvp) {
> -             (void) gettimeofday(&starttv, NILTZ);
> -             endtv = *tvp;
> -             timo = endtv.tv_sec * 1000 + endtv.tv_usec / 1000;
> -     }
> -again:
>       pfd[0].fd = STDIN_FILENO;
>       pfd[0].events = POLLIN;
> -     switch (poll(pfd, 1, timo)) {
> +
> +     if (limit != NULL)
> +             clock_gettime(CLOCK_MONOTONIC, &start);
> +again:
> +     switch (ppoll(pfd, 1, limit, NULL)) {
>       case -1:
> -             if (tvp == 0)
> +             if (limit == NULL)
>                       return (-1);
>               if (errno == EINTR)
>                       goto again;
>               stop("poll failed, help");
> -
>       case 0: /* timed out */
> -             tvp->tv_sec = 0;
> -             tvp->tv_usec = 0;
> +             timespecclear(limit);
>               return (0);
>       }
> -     if (tvp) {
> -             /* since there is input, we may not have timed out */
> -             (void) gettimeofday(&endtv, NILTZ);
> -             TV_SUB(&endtv, &starttv);
> -             TV_SUB(tvp, &endtv);    /* adjust *tvp by elapsed time */
> +     if (limit != NULL) {
> +             /* we have input, so subtract the elapsed time from *limit */
> +             clock_gettime(CLOCK_MONOTONIC, &end);
> +             timespecsub(&end, &start, &elapsed);
> +             timespecsub(limit, &elapsed, limit);
>       }
>       return (1);
> }
> 
> /*
> - * `sleep' for the current turn time (using poll).
> - * Eat any input that might be available.
> + * `sleep' for the current turn time and eat any
> + * input that becomes available.
>  */
> void
> tsleep(void)
> {
> -     struct timeval tv;
> +     struct timespec ts;
>       char c;
> 
> -     tv.tv_sec = 0;
> -     tv.tv_usec = fallrate;
> -     while (TV_POS(&tv))
> -             if (rwait(&tv) && read(STDIN_FILENO, &c, 1) != 1)
> +     ts.tv_sec = 0;
> +     ts.tv_nsec = fallrate;
> +     while (TS_POS(&ts))
> +             if (rwait(&ts) && read(STDIN_FILENO, &c, 1) != 1)
>                       break;
> }
> 
> @@ -132,7 +118,7 @@ tsleep(void)
> int
> tgetchar(void)
> {
> -     static struct timeval timeleft;
> +     static struct timespec timeleft;
>       char c;
> 
>       /*
> @@ -144,10 +130,10 @@ tgetchar(void)
>        *
>        * Most of the hard work is done by rwait().
>        */
> -     if (!TV_POS(&timeleft)) {
> +     if (!TS_POS(&timeleft)) {
>               faster();       /* go faster */
>               timeleft.tv_sec = 0;
> -             timeleft.tv_usec = fallrate;
> +             timeleft.tv_nsec = fallrate;
>       }
>       if (!rwait(&timeleft))
>               return (-1);
> Index: games/tetris/input.h
> ===================================================================
> RCS file: /cvs/src/games/tetris/input.h,v
> retrieving revision 1.5
> diff -u -p -r1.5 input.h
> --- games/tetris/input.h      3 Jun 2003 03:01:41 -0000       1.5
> +++ games/tetris/input.h      6 Aug 2017 01:21:41 -0000
> @@ -35,6 +35,6 @@
>  *    @(#)input.h     8.1 (Berkeley) 5/31/93
>  */
> 
> -int  rwait(struct timeval *);
> +int  rwait(struct timespec *);
> int   tgetchar(void);
> void  tsleep(void);
> Index: games/tetris/tetris.c
> ===================================================================
> RCS file: /cvs/src/games/tetris/tetris.c,v
> retrieving revision 1.31
> diff -u -p -r1.31 tetris.c
> --- games/tetris/tetris.c     10 Jun 2016 13:07:07 -0000      1.31
> +++ games/tetris/tetris.c     6 Aug 2017 01:21:41 -0000
> @@ -197,7 +197,7 @@ main(int argc, char *argv[])
>       if (argc)
>               usage();
> 
> -     fallrate = 1000000 / level;
> +     fallrate = 1000000000L / level;
> 
>       for (i = 0; i <= 5; i++) {
>               for (j = i+1; j <= 5; j++) {
> @@ -280,7 +280,7 @@ main(int argc, char *argv[])
>                               scr_msg(key_msg, 0);
>                               scr_msg(msg, 1);
>                               (void) fflush(stdout);
> -                     } while (rwait((struct timeval *)NULL) == -1);
> +                     } while (rwait(NULL) == -1);
>                       scr_msg(msg, 0);
>                       scr_msg(key_msg, 1);
>                       place(curshape, pos, 0);
> Index: games/tetris/tetris.h
> ===================================================================
> RCS file: /cvs/src/games/tetris/tetris.h,v
> retrieving revision 1.11
> diff -u -p -r1.11 tetris.h
> --- games/tetris/tetris.h     20 Nov 2015 07:40:23 -0000      1.11
> +++ games/tetris/tetris.h     6 Aug 2017 01:21:41 -0000
> @@ -135,15 +135,15 @@ extern const struct shape *nextshape;
> /*
>  * Shapes fall at a rate faster than once per second.
>  *
> - * The initial rate is determined by dividing 1 million microseconds
> - * by the game `level'.  (This is at most 1 million, or one second.)
> - * Each time the fall-rate is used, it is decreased a little bit,
> + * The initial rate is determined by dividing 1 billion nanoseconds
> + * by the game `level'.  (This is at most 1 billion, or one second.)
> + * Each time the fallrate is used, it is decreased a little bit,
>  * depending on its current value, via the `faster' macro below.
>  * The value eventually reaches a limit, and things stop going faster,
>  * but by then the game is utterly impossible.
>  */
> -extern long  fallrate;       /* less than 1 million; smaller => faster */
> -#define      faster() (fallrate -= fallrate / 3000)
> +extern long  fallrate;       /* less than 1 billion; smaller => faster */
> +#define      faster() (fallrate -= fallrate / 3000000)
> 
> /*
>  * Game level must be between 1 and 9.  This controls the initial fall rate

Reply via email to