diff options
| author | Paul Eggert | 2022-08-01 00:38:33 -0700 |
|---|---|---|
| committer | Paul Eggert | 2022-08-01 01:17:15 -0700 |
| commit | afa67ed6f20780ee8e99a5cac1bcc4899d83adea (patch) | |
| tree | a7c090e32030b05e74e9abf1c57bcf570fe0294c | |
| parent | 2fd2008e6717189627019e30591bc788f7957917 (diff) | |
| download | emacs-afa67ed6f20780ee8e99a5cac1bcc4899d83adea.tar.gz emacs-afa67ed6f20780ee8e99a5cac1bcc4899d83adea.zip | |
Fix year-285428751 bug in hanoi-unix-64
* lisp/play/hanoi.el (hanoi-move-period, hanoi, hanoi-unix)
(hanoi-unix-64): Use integers, not floating point, to avoid
rounding errors for timestamps greater than 2**53.
| -rw-r--r-- | lisp/play/hanoi.el | 44 |
1 files changed, 21 insertions, 23 deletions
diff --git a/lisp/play/hanoi.el b/lisp/play/hanoi.el index 227dd790af5..58fb82b6ed0 100644 --- a/lisp/play/hanoi.el +++ b/lisp/play/hanoi.el | |||
| @@ -73,7 +73,7 @@ | |||
| 73 | "Non-nil means that hanoi poles are oriented horizontally." | 73 | "Non-nil means that hanoi poles are oriented horizontally." |
| 74 | :type 'boolean) | 74 | :type 'boolean) |
| 75 | 75 | ||
| 76 | (defcustom hanoi-move-period 1.0 | 76 | (defcustom hanoi-move-period 1 |
| 77 | "Time, in seconds, for each pole-to-pole move of a ring. | 77 | "Time, in seconds, for each pole-to-pole move of a ring. |
| 78 | If nil, move rings as fast as possible while displaying all | 78 | If nil, move rings as fast as possible while displaying all |
| 79 | intermediate positions." | 79 | intermediate positions." |
| @@ -112,35 +112,32 @@ intermediate positions." | |||
| 112 | (prefix-numeric-value current-prefix-arg)))) | 112 | (prefix-numeric-value current-prefix-arg)))) |
| 113 | (if (< nrings 0) | 113 | (if (< nrings 0) |
| 114 | (error "Negative number of rings")) | 114 | (error "Negative number of rings")) |
| 115 | (hanoi-internal nrings (make-list nrings 0) (float-time))) | 115 | (hanoi-internal nrings (make-list nrings 0) (time-convert nil 'integer))) |
| 116 | 116 | ||
| 117 | ;;;###autoload | 117 | ;;;###autoload |
| 118 | (defun hanoi-unix () | 118 | (defun hanoi-unix () |
| 119 | "Towers of Hanoi, UNIX doomsday version. | 119 | "Towers of Hanoi, 32-bit UNIX doomsday version. |
| 120 | Displays 32-ring towers that have been progressing at one move per | 120 | Display 32-ring towers that have been progressing at one move per |
| 121 | second since 1970-01-01 00:00:00 GMT. | 121 | second since 1970-01-01 00:00:00 UTC. |
| 122 | 122 | ||
| 123 | Repent before ring 31 moves." | 123 | Repent before ring 31 moves." |
| 124 | (interactive) | 124 | (interactive) |
| 125 | (let* ((start (ftruncate (float-time))) | 125 | (let* ((start (time-convert nil 'integer)) |
| 126 | (bits (cl-loop repeat 32 | 126 | (bits (nreverse (cl-loop repeat 32 |
| 127 | for x = (/ start (expt 2.0 31)) then (* x 2.0) | 127 | for x = start then (ash x -1) |
| 128 | collect (truncate (mod x 2.0)))) | 128 | collect (logand x 1)))) |
| 129 | (hanoi-move-period 1.0)) | 129 | (hanoi-move-period 1)) |
| 130 | (hanoi-internal 32 bits start))) | 130 | (hanoi-internal 32 bits start))) |
| 131 | 131 | ||
| 132 | ;;;###autoload | 132 | ;;;###autoload |
| 133 | (defun hanoi-unix-64 () | 133 | (defun hanoi-unix-64 () |
| 134 | "Like `hanoi-unix', but pretend to have a 64-bit clock. | 134 | "Like `hanoi-unix', but with a 64-bit clock." |
| 135 | This is, necessarily (as of Emacs 20.3), a crock. When the | ||
| 136 | `current-time' interface is made s2G-compliant, hanoi.el will need | ||
| 137 | to be updated." | ||
| 138 | (interactive) | 135 | (interactive) |
| 139 | (let* ((start (ftruncate (float-time))) | 136 | (let* ((start (time-convert nil 'integer)) |
| 140 | (bits (cl-loop repeat 64 | 137 | (bits (nreverse (cl-loop repeat 64 |
| 141 | for x = (/ start (expt 2.0 63)) then (* x 2.0) | 138 | for x = start then (ash x -1) |
| 142 | collect (truncate (mod x 2.0)))) | 139 | collect (logand x 1)))) |
| 143 | (hanoi-move-period 1.0)) | 140 | (hanoi-move-period 1)) |
| 144 | (hanoi-internal 64 bits start))) | 141 | (hanoi-internal 64 bits start))) |
| 145 | 142 | ||
| 146 | (defun hanoi-internal (nrings bits start-time) | 143 | (defun hanoi-internal (nrings bits start-time) |
| @@ -378,9 +375,10 @@ BITS must be of length nrings. Start at START-TIME." | |||
| 378 | (/ (- tick flyward-ticks fly-ticks) | 375 | (/ (- tick flyward-ticks fly-ticks) |
| 379 | ticks-per-pole-step)))))))) | 376 | ticks-per-pole-step)))))))) |
| 380 | (if hanoi-move-period | 377 | (if hanoi-move-period |
| 381 | (cl-loop for elapsed = (- (float-time) start-time) | 378 | (cl-loop for elapsed = (float-time (time-subtract nil start-time)) |
| 382 | while (< elapsed hanoi-move-period) | 379 | while (time-less-p elapsed hanoi-move-period) |
| 383 | with tick-period = (/ (float hanoi-move-period) total-ticks) | 380 | with tick-period = (/ (float-time hanoi-move-period) |
| 381 | total-ticks) | ||
| 384 | for tick = (ceiling elapsed tick-period) do | 382 | for tick = (ceiling elapsed tick-period) do |
| 385 | (hanoi-ring-to-pos ring (funcall tick-to-pos tick)) | 383 | (hanoi-ring-to-pos ring (funcall tick-to-pos tick)) |
| 386 | (hanoi-sit-for (- (* tick tick-period) elapsed))) | 384 | (hanoi-sit-for (- (* tick tick-period) elapsed))) |
| @@ -389,7 +387,7 @@ BITS must be of length nrings. Start at START-TIME." | |||
| 389 | (hanoi-sit-for 0))) | 387 | (hanoi-sit-for 0))) |
| 390 | ;; Always make last move to keep pole and ring data consistent | 388 | ;; Always make last move to keep pole and ring data consistent |
| 391 | (hanoi-ring-to-pos ring (car to)) | 389 | (hanoi-ring-to-pos ring (car to)) |
| 392 | (if hanoi-move-period (+ start-time hanoi-move-period)))) | 390 | (if hanoi-move-period (time-add start-time hanoi-move-period)))) |
| 393 | 391 | ||
| 394 | ;; update display and pause, quitting with a pithy comment if the user | 392 | ;; update display and pause, quitting with a pithy comment if the user |
| 395 | ;; hits a key. | 393 | ;; hits a key. |