trip logs / gnuvola


Trip Log 2022-01-30 h04 -- Hooray! Part 7

The teaser for part 7 of the Hooray! series promised “patches multiple” because the amount of abstractions we were able to ferret out from the code was more than anticipated, and it was tiring to update the teaser w/ yet another number that would quickly become obsolete.  So, get ready for a slew of patches this episode.  The abstractions patches number eight (!), but they will be quick as we race towards the last two — patches 20 and 21 — which add real motion to the animation.  Finally, we can see those throngs thronging!  :-D 

patch 12

     1  commit 383244898499097e033b67f9c337fe449077f73a
     2  Author: Thien-Thi Nguyen <ttn@gnuvola.org>
     3  Date:   2022-01-29 17:06:33 -0500
     4
     5      Add abstraction: BUTTONS
     6
     7      * hooray.scm (BUTTONS): New data structure.
     8      (load-theme): Use ‘BUTTONS’.
     9      (change-scenery-slightly! random-button): Likewise.
    10  ---
    11   hooray.scm | 6 ++++--
    12   1 file changed, 4 insertions(+), 2 deletions(-)
    13
    14  diff --git a/hooray.scm b/hooray.scm
    15  index 37011a2..ca55c99 100644
    16  --- a/hooray.scm
    17  +++ b/hooray.scm
    18  @@ -50,6 +50,8 @@ (define (make-tile-rect r c)
    19                    TILE-EDGE-LENGTH
    20                    TILE-EDGE-LENGTH))
    21
    22  +(define BUTTONS '(left right check-0 x mix tools blank check-1))
    23  +
    24   (define (load-theme filename)           ; => hash table
    25
    26     (define (proper surface)
    27  @@ -72,7 +74,7 @@ (define (load-theme filename)           ; => hash table
    28
    29       (read-row! 0 '(window cell sender rec-0 rec-1 lock win unused))
    30       (read-row! 1 '(up-0 up-1 opp-0 opp-1 corn-0 corn-1 tee-0 tee-1))
    31  -    (read-row! 2 '(left right check-0 x mix tools blank check-1))
    32  +    (read-row! 2 BUTTONS)
    33
    34       (hashq-remove! ht 'unused)
    35
    36  @@ -253,7 +255,7 @@ (define hooray!
    37   (define (change-scenery-slightly!)
    38
    39     (define random-button
    40  -    (let ((v (list->vector '(left right check-0 x mix tools blank check-1))))
    41  +    (let ((v (list->vector BUTTONS)))
    42         ;; random-button
    43         (lambda ()
    44           (vector-ref v (random (vector-length v))))))

Yes, abstractions can be data as well as control.  Next! 

patch 13

     1  commit 2fd1fe4b2ca3e638fd9c0eba1268a2340d9ec303
     2  Author: Thien-Thi Nguyen <ttn@gnuvola.org>
     3  Date:   2022-01-29 17:12:00 -0500
     4
     5      Add abstraction: rect-xy!
     6
     7      * hooray.scm (rect-xy!): New proc.
     8      (smokin): Use ‘rect-xy!’.
     9      (hooray! place-fg!): Likewise.
    10      (hooray!): Likewise.
    11  ---
    12   hooray.scm | 18 ++++++++++--------
    13   1 file changed, 10 insertions(+), 8 deletions(-)
    14
    15  diff --git a/hooray.scm b/hooray.scm
    16  index ca55c99..d33d595 100644
    17  --- a/hooray.scm
    18  +++ b/hooray.scm
    19  @@ -116,6 +116,10 @@ (define refresh-scenery!
    20         ;; mid ground
    21         (array-map! rect place-tile! MID rect))))
    22
    23  +(define (rect-xy! rect x y)
    24  +  (SDL:rect:set-x! rect x)
    25  +  (SDL:rect:set-y! rect y))
    26  +
    27   (define smokin
    28     (let ((keys (list->vector (hash-fold (lambda (k v acc)
    29                                            (cons k acc))
    30  @@ -138,8 +142,7 @@ (define smokin
    31           (and (zero? (modulo i 42))
    32                (refresh-scenery!))
    33           (let ((tile (random-tile)))
    34  -          (SDL:rect:set-x! rect (random-coord))
    35  -          (SDL:rect:set-y! rect (random-coord))
    36  +          (rect-xy! rect (random-coord) (random-coord))
    37             (SDL:blit-surface tile #f #f rect))
    38           (SDL:flip)
    39           (SDL:delay millisecs))
    40  @@ -217,8 +220,10 @@ (define hooray!
    41       (define (place-fg! fg w/2 h/2)
    42         ;; rv
    43         (lambda (dst x y)
    44  -        (SDL:rect:set-x! dst (- x w/2))
    45  -        (SDL:rect:set-y! dst (exact-truncate (- y h/2)))
    46  +        (rect-xy!
    47  +         dst
    48  +         (- x w/2))
    49  +         (exact-truncate (- y h/2))
    50           (SDL:blit-surface fg #f #f dst)))
    51
    52       (let ((fg (svec))
    53  @@ -239,10 +244,7 @@ (define hooray!
    54         (lambda ()
    55           (array-map! x (random-c +))
    56           (array-map! y (random-c -))
    57  -        (array-for-each (lambda (rect x y)
    58  -                          (SDL:rect:set-x! rect x)
    59  -                          (SDL:rect:set-y! rect y))
    60  -                        dst x y)
    61  +        (array-for-each rect-xy! dst x y)
    62           (array-for-each
    63            (lambda (fg w/2 h/2)
    64              (refresh-scenery!)

This one is so useful, I'm considering adding it to Guile-SDL proper.  Let's see how things go... 

patch 14

     1  commit f6ad67557ac63403a20d92e6bfb38c77baf50304
     2  Author: Thien-Thi Nguyen <ttn@gnuvola.org>
     3  Date:   2022-01-29 17:16:16 -0500
     4
     5      Add abstraction: array-like-FAR
     6
     7      * hooray.scm (array-like-FAR): New proc.
     8      (MID): Use ‘array-like-FAR’.
     9      (refresh-scenery!): Likewise.
    10  ---
    11   hooray.scm | 7 +++++--
    12   1 file changed, 5 insertions(+), 2 deletions(-)
    13
    14  diff --git a/hooray.scm b/hooray.scm
    15  index d33d595..ed158f0 100644
    16  --- a/hooray.scm
    17  +++ b/hooray.scm
    18  @@ -95,12 +95,15 @@ (define FAR #2((window window window window window)
    19                  (window cell   cell   cell   window)
    20                  (window window window window window)))
    21
    22  -(define MID (let ((a (apply make-array #f (array-shape FAR))))
    23  +(define (array-like-FAR)
    24  +  (apply make-array #f (array-shape FAR)))
    25  +
    26  +(define MID (let ((a (array-like-FAR)))
    27                 (array-set! a 'rec-1 2 2)
    28                 a))
    29
    30   (define refresh-scenery!
    31  -  (let ((rect (apply make-array #f (array-shape FAR))))
    32  +  (let ((rect (array-like-FAR)))
    33
    34       (define (place-tile! name rect)
    35         (and name (SDL:blit-surface (surf name) #f #f rect))

Probably you saw this coming from “a-FAR”, right?  (Yuk yuk.) 

patch 15

     1  commit da8888ea65cc96108ec5bcbf3c19a87fa7874061
     2  Author: Thien-Thi Nguyen <ttn@gnuvola.org>
     3  Date:   2022-01-29 17:19:07 -0500
     4
     5      Add abstraction: vrandom
     6
     7      * hooray.scm (vrandom): New proc.
     8      (smokin random-tile): Use ‘vrandom’.
     9      (change-scenery-slightly! random-button): Likewise.
    10  ---
    11   hooray.scm | 7 +++++--
    12   1 file changed, 5 insertions(+), 2 deletions(-)
    13
    14  diff --git a/hooray.scm b/hooray.scm
    15  index ed158f0..0da2d6b 100644
    16  --- a/hooray.scm
    17  +++ b/hooray.scm
    18  @@ -119,6 +119,9 @@ (define refresh-scenery!
    19         ;; mid ground
    20         (array-map! rect place-tile! MID rect))))
    21
    22  +(define (vrandom v)
    23  +  (vector-ref v (random (vector-length v))))
    24  +
    25   (define (rect-xy! rect x y)
    26     (SDL:rect:set-x! rect x)
    27     (SDL:rect:set-y! rect y))
    28  @@ -133,7 +136,7 @@ (define smokin
    29         (* TILE-EDGE-LENGTH (random 5)))
    30
    31       (define (random-tile)
    32  -      (let ((name (vector-ref keys (random (vector-length keys)))))
    33  +      (let ((name (vrandom keys)))
    34           (display " ")
    35           (display name)
    36           (surf name)))
    37  @@ -263,7 +266,7 @@ (define (change-scenery-slightly!)
    38       (let ((v (list->vector BUTTONS)))
    39         ;; random-button
    40         (lambda ()
    41  -        (vector-ref v (random (vector-length v))))))
    42  +        (vrandom v))))
    43
    44     (define (new-place)
    45       (let loop ((count 9))

This reminds me of 1990s modem marketing, v.fast, v.faster, v.ludicrous... 

patch 16

     1  commit 4659a89fed66baaf8eb4411d9b227bdaac4ed562
     2  Author: Thien-Thi Nguyen <ttn@gnuvola.org>
     3  Date:   2022-01-29 17:48:52 -0500
     4
     5      Add abstraction: update-rect-and-blit!
     6
     7      * hooray.scm (update-rect-and-blit!): New proc.
     8      (smokin): Use ‘update-rect-and-blit!’.
     9      (hooray! place-fg!): Likewise.
    10  ---
    11   hooray.scm | 19 +++++++++++--------
    12   1 file changed, 11 insertions(+), 8 deletions(-)
    13
    14  diff --git a/hooray.scm b/hooray.scm
    15  index 0da2d6b..52a2ada 100644
    16  --- a/hooray.scm
    17  +++ b/hooray.scm
    18  @@ -126,6 +126,10 @@ (define (rect-xy! rect x y)
    19     (SDL:rect:set-x! rect x)
    20     (SDL:rect:set-y! rect y))
    21
    22  +(define (update-rect-and-blit! surface rect x y)
    23  +  (rect-xy! rect x y)
    24  +  (SDL:blit-surface surface #f #f rect))
    25  +
    26   (define smokin
    27     (let ((keys (list->vector (hash-fold (lambda (k v acc)
    28                                            (cons k acc))
    29  @@ -147,9 +151,9 @@ (define smokin
    30             ((= 420 i))
    31           (and (zero? (modulo i 42))
    32                (refresh-scenery!))
    33  -        (let ((tile (random-tile)))
    34  -          (rect-xy! rect (random-coord) (random-coord))
    35  -          (SDL:blit-surface tile #f #f rect))
    36  +        (update-rect-and-blit! (random-tile) rect
    37  +                               (random-coord)
    38  +                               (random-coord))
    39           (SDL:flip)
    40           (SDL:delay millisecs))
    41         (newline))))
    42  @@ -226,11 +230,10 @@ (define hooray!
    43       (define (place-fg! fg w/2 h/2)
    44         ;; rv
    45         (lambda (dst x y)
    46  -        (rect-xy!
    47  -         dst
    48  -         (- x w/2))
    49  -         (exact-truncate (- y h/2))
    50  -        (SDL:blit-surface fg #f #f dst)))
    51  +        (update-rect-and-blit!
    52  +         fg dst
    53  +         (- x w/2)
    54  +         (exact-truncate (- y h/2)))))
    55
    56       (let ((fg (svec))
    57             (w/2 (svec))

Note that we can have abstractions built upon other abstractions. 

patch 17

     1  commit 662b9de748d471d8deba4e986f06092e767621ef
     2  Author: Thien-Thi Nguyen <ttn@gnuvola.org>
     3  Date:   2022-01-29 17:53:39 -0500
     4
     5      Add abstraction: layer!
     6
     7      * hooray.scm (refresh-scenery! layer!): New proc.
     8      (refresh-scenery!): Use ‘layer!’.
     9  ---
    10   hooray.scm | 9 +++++----
    11   1 file changed, 5 insertions(+), 4 deletions(-)
    12
    13  diff --git a/hooray.scm b/hooray.scm
    14  index 52a2ada..4fad590 100644
    15  --- a/hooray.scm
    16  +++ b/hooray.scm
    17  @@ -109,15 +109,16 @@ (define refresh-scenery!
    18         (and name (SDL:blit-surface (surf name) #f #f rect))
    19         rect)
    20
    21  +    (define (layer! ground)
    22  +      (array-map! rect place-tile! ground rect))
    23  +
    24       ;; initialize ‘rect’
    25       (array-index-map! rect make-tile-rect)
    26
    27       ;; refresh-scenery!
    28       (lambda ()
    29  -      ;; far ground
    30  -      (array-map! rect place-tile! FAR rect)
    31  -      ;; mid ground
    32  -      (array-map! rect place-tile! MID rect))))
    33  +      (layer! FAR)
    34  +      (layer! MID))))
    35
    36   (define (vrandom v)
    37     (vector-ref v (random (vector-length v))))

Here we take the liberty of removing some comments (lines 29, 31), since the code has become so incredibly self-descriptive. 

patch 18

     1  commit 24f1e68767edf106293fea6dea167e978b7f2d8d
     2  Author: Thien-Thi Nguyen <ttn@gnuvola.org>
     3  Date:   2022-01-29 18:00:06 -0500
     4
     5      Add abstraction: show!
     6
     7      * hooray.scm (show!): New proc.
     8      (smokin): Use ‘show!’.
     9      (hooray!): Likewise.
    10  ---
    11   hooray.scm | 10 ++++++----
    12   1 file changed, 6 insertions(+), 4 deletions(-)
    13
    14  diff --git a/hooray.scm b/hooray.scm
    15  index 4fad590..02714c5 100644
    16  --- a/hooray.scm
    17  +++ b/hooray.scm
    18  @@ -131,6 +131,10 @@ (define (update-rect-and-blit! surface rect x y)
    19     (rect-xy! rect x y)
    20     (SDL:blit-surface surface #f #f rect))
    21
    22  +(define (show! ms)
    23  +  (SDL:flip)
    24  +  (SDL:delay ms))
    25  +
    26   (define smokin
    27     (let ((keys (list->vector (hash-fold (lambda (k v acc)
    28                                            (cons k acc))
    29  @@ -155,8 +159,7 @@ (define smokin
    30           (update-rect-and-blit! (random-tile) rect
    31                                  (random-coord)
    32                                  (random-coord))
    33  -        (SDL:flip)
    34  -        (SDL:delay millisecs))
    35  +        (show! millisecs))
    36         (newline))))
    37
    38   (define FG-EDGE-LENGTH 32)
    39  @@ -260,8 +263,7 @@ (define hooray!
    40              (refresh-scenery!)
    41              (array-for-each (place-fg! fg w/2 h/2)
    42                              dst x y)
    43  -           (SDL:flip)
    44  -           (SDL:delay 42))
    45  +           (show! 42))
    46            fg w/2 h/2)))))
    47
    48   (define (change-scenery-slightly!)

Small but sweet.  We'll take it. 

patch 19

     1  commit 0368f020fde9320cf073099c31a208ceec244bb6
     2  Author: Thien-Thi Nguyen <ttn@gnuvola.org>
     3  Date:   2022-01-29 18:03:02 -0500
     4
     5      Add abstraction: blit-to-screen!
     6
     7      * hooray.scm (blit-to-screen!): New proc.
     8      (refresh-scenery! place-tile!): Use ‘blit-to-screen!’.
     9      (update-rect-and-blit!): Likewise.
    10  ---
    11   hooray.scm | 7 +++++--
    12   1 file changed, 5 insertions(+), 2 deletions(-)
    13
    14  diff --git a/hooray.scm b/hooray.scm
    15  index 02714c5..2fdc55e 100644
    16  --- a/hooray.scm
    17  +++ b/hooray.scm
    18  @@ -102,11 +102,14 @@ (define MID (let ((a (array-like-FAR)))
    19                 (array-set! a 'rec-1 2 2)
    20                 a))
    21
    22  +(define (blit-to-screen! surface rect)
    23  +  (SDL:blit-surface surface #f #f rect))
    24  +
    25   (define refresh-scenery!
    26     (let ((rect (array-like-FAR)))
    27
    28       (define (place-tile! name rect)
    29  -      (and name (SDL:blit-surface (surf name) #f #f rect))
    30  +      (and name (blit-to-screen! (surf name) rect))
    31         rect)
    32
    33       (define (layer! ground)
    34  @@ -129,7 +132,7 @@ (define (rect-xy! rect x y)
    35
    36   (define (update-rect-and-blit! surface rect x y)
    37     (rect-xy! rect x y)
    38  -  (SDL:blit-surface surface #f #f rect))
    39  +  (blit-to-screen! surface rect))
    40
    41   (define (show! ms)
    42     (SDL:flip)

This is a happy patch — it reduces negativity (‘#f’)! 

That was quick!  Here's an interesting set of data points on the overall effect of these eight patches: 

$ wc hooray.scm.{WAS,NOW}
  293   985  8956 hooray.scm.WAS
  312   989  9014 hooray.scm.NOW
  605  1974 17970 totale

The ‘.WAS’ is a snapshot prior to the eight, the ‘.NOW’ is a snapshot after.  The figure of merit here is not line or character count, but word count.  It went from 985 to 989, barely one word per two patches!  From this we can see that our abstractions have increased the density of the code, which is a good thing.  And with that out of the way, we now turn our attention to the real interesting patches in this episode, the ones that add velocity and acceleration to our sprites. 

patch 20

     1  commit 3f6436268063cab86890e9563df0529b052c764f
     2  Author: Thien-Thi Nguyen <ttn@gnuvola.org>
     3  Date:   2022-01-29 18:30:07 -0500
     4
     5      Add velocity
     6
     7      * hooray.scm (hooray! random-v): New proc.
     8      (hooray! bump!): New proc.
     9      (hooray!): Initialize and integrate velocity vectors.
    10  ---
    11   hooray.scm | 14 ++++++++++++++
    12   1 file changed, 14 insertions(+)
    13
    14  diff --git a/hooray.scm b/hooray.scm
    15  index 2fdc55e..0c53f2b 100644
    16  --- a/hooray.scm
    17  +++ b/hooray.scm
    18  @@ -234,6 +234,14 @@ (define hooray!
    19           (lambda ()
    20             (+ origin (random 29) -14))))
    21
    22  +    (define (random-v bias)
    23  +      ;; rv
    24  +      (lambda ()
    25  +        (+ (random 9) -4 bias)))
    26  +
    27  +    (define (bump! orig delta)
    28  +      (array-map! orig + orig delta))
    29  +
    30       (define (place-fg! fg w/2 h/2)
    31         ;; rv
    32         (lambda (dst x y)
    33  @@ -247,6 +255,8 @@ (define hooray!
    34             (h/2 (svec))
    35             (x (tvec))                    ; location
    36             (y (tvec))
    37  +          (vx (tvec))                   ; velocity
    38  +          (vy (tvec))
    39             (dst (tvec)))
    40
    41         (array-index-map! fg (lambda (i)
    42  @@ -261,9 +271,13 @@ (define hooray!
    43           (array-map! x (random-c +))
    44           (array-map! y (random-c -))
    45           (array-for-each rect-xy! dst x y)
    46  +        (array-map! vx (random-v 0))
    47  +        (array-map! vy (random-v (random -4.20)))
    48           (array-for-each
    49            (lambda (fg w/2 h/2)
    50              (refresh-scenery!)
    51  +           (bump! x vx)
    52  +           (bump! y vy)
    53              (array-for-each (place-fg! fg w/2 h/2)
    54                              dst x y)
    55              (show! 42))

The “velocity vectors” (line 9) sound racy and exciting, don't they?  Actually, that's a literal description (lines 37-38) of the situation.  Init is on lines 46-47 and the real Change happens on lines 51-52.  Here's what I see when I run command “guile -s hooray.scm Plumbing.png 99”: 

patch 21

     1  commit 8a7c45d387f0966dd683fc4f0ee328081e8fdafc
     2  Author: Thien-Thi Nguyen <ttn@gnuvola.org>
     3  Date:   2022-01-29 19:58:34 -0500
     4
     5      Add acceleration
     6
     7      * hooray.scm (hooray! random-gravity): New proc.
     8      (hooray!): Initialize and integrate gravity vector.
     9  ---
    10   hooray.scm | 6 ++++++
    11   1 file changed, 6 insertions(+)
    12
    13  diff --git a/hooray.scm b/hooray.scm
    14  index 0c53f2b..9b57804 100644
    15  --- a/hooray.scm
    16  +++ b/hooray.scm
    17  @@ -239,6 +239,9 @@ (define hooray!
    18         (lambda ()
    19           (+ (random 9) -4 bias)))
    20
    21  +    (define (random-gravity)
    22  +      (random 0.420))
    23  +
    24       (define (bump! orig delta)
    25         (array-map! orig + orig delta))
    26
    27  @@ -257,6 +260,7 @@ (define hooray!
    28             (y (tvec))
    29             (vx (tvec))                   ; velocity
    30             (vy (tvec))
    31  +          (gravity (tvec))
    32             (dst (tvec)))
    33
    34         (array-index-map! fg (lambda (i)
    35  @@ -273,11 +277,13 @@ (define hooray!
    36           (array-for-each rect-xy! dst x y)
    37           (array-map! vx (random-v 0))
    38           (array-map! vy (random-v (random -4.20)))
    39  +        (array-map! gravity random-gravity)
    40           (array-for-each
    41            (lambda (fg w/2 h/2)
    42              (refresh-scenery!)
    43              (bump! x vx)
    44              (bump! y vy)
    45  +           (bump! vy gravity)
    46              (array-for-each (place-fg! fg w/2 h/2)
    47                              dst x y)
    48              (show! 42))

Why call it “acceleration” in the commit log message title and “gravity” everywhere else?  Well, “gravity” is familiar, and indicates direction (generally, “down”), not to mention shorter and faster to type.  It also has a certain... weightiness to it, no?  :-D  Anyway, note that we only apply gravity to vertical component (line 45).  It doesn't make sense (for this animation) to use it on the horizontal.  Here's what “guile -s hooray.scm Plumbing.png 99” shows now, w/ this patch: 

So, let's update our checklist now. 

* PipeWalker fg behavior (to reproduce if possible)
  - [X] starts near top of center tile
  - [x] mostly upward velocity
  - [ ] rotates clockwise slightly
  - [ ] starts smaller than 32x32
  - [ ] grows quickly
  - [ ] non synchronized (all aspects)
  - [x] fades to transparent

Like “fades to transparent”, we cannot give full marks to “mostly upward velocity” at this time.  The reasoning behind our strictness differs, however, from before.  For velocity and acceleration, our design and implementation is good, but the actual results do not truly live up to “mostly”.  In other words, there's room for tweaking the constants.  We'll get to that later (maybe). 


Copyright (C) 2022 Thien-Thi Nguyen