trip logs / gnuvola


Trip Log 2022-01-22 h13 -- Hooray! Part 3

In Part 3 of the Hooray! series, we look at two patches, one that adds theme loading, and one that adds a sanity check to make sure we're on the right track.  Yes, we're picking up the pace — two patches in one episode!  This is not what the teaser said, but that's okay.  Feel free to let me know if that's too fast for you. 

patch 3

     1  commit 9196a8d8bee0c4b5831e086e3e30efe26643d370
     2  Author: Thien-Thi Nguyen <ttn@gnuvola.org>
     3  Date:   2022-01-22 05:40:36 -0500
     4
     5      Add theme loading
     6
     7      * hooray.scm: Import ‘(sdl misc-utils) copy-surface’.
     8      (load-theme): New proc.
     9      (HT): New data structure.
    10  ---
    11   hooray.scm | 38 ++++++++++++++++++++++++++++++++++++--
    12   1 file changed, 36 insertions(+), 2 deletions(-)
    13
    14  diff --git a/hooray.scm b/hooray.scm
    15  index f8572ed..7212f47 100644
    16  --- a/hooray.scm
    17  +++ b/hooray.scm
    18  @@ -15,15 +15,17 @@
    19
    20   ;;; Commentary:
    21
    22  -;; Usage: guile -s hooray.scm
    23  +;; Usage: guile -s hooray.scm THEME
    24   ;;
    25  -;; This opens a window, waits a moment, then exits.
    26  +;; This opens a window, loads THEME (a stylized PNG file),
    27  +;; waits a moment, then exits.
    28   ;; The window has title "hooray!!!", maybe.
    29
    30   ;;; Code:
    31
    32   (use-modules
    33    ((srfi srfi-11) #:select (let-values))
    34  + ((sdl misc-utils) #:select (copy-surface))
    35    ((sdl sdl) #:prefix SDL:))
    36
    37   (SDL:init 'video)
    38  @@ -34,6 +36,38 @@ (let-values (((cap mem fmt) (SDL:video-cmf)))
    39
    40   (define TILE-EDGE-LENGTH 64)
    41
    42  +(define (load-theme filename)           ; => hash table
    43  +
    44  +  (define (proper surface)
    45  +    ;; Q: This is very important!  Why?
    46  +    ;; A: Disabling Per-Surface Alpha allows Per-Pixel Alpha to work.
    47  +    (SDL:surface-alpha! surface #f)
    48  +    surface)
    49  +
    50  +  (let* ((theme (proper (SDL:load-image filename)))
    51  +         (ht (make-hash-table))
    52  +         (rect (SDL:make-rect 0 0
    53  +                              TILE-EDGE-LENGTH
    54  +                              TILE-EDGE-LENGTH)))
    55  +
    56  +    (define (read-row! row tile-names)
    57  +      (SDL:rect:set-y! rect (* TILE-EDGE-LENGTH row))
    58  +      (do ((x 0 (+ x TILE-EDGE-LENGTH))
    59  +           (names tile-names (cdr names)))
    60  +          ((null? names))
    61  +        (SDL:rect:set-x! rect x)
    62  +        (hashq-set! ht (car names) (copy-surface theme rect))))
    63  +
    64  +    (read-row! 0 '(window cell sender rec-0 rec-1 lock win unused))
    65  +    (read-row! 1 '(up-0 up-1 opp-0 opp-1 corn-0 corn-1 tee-0 tee-1))
    66  +    (read-row! 2 '(left right check-0 x mix tools blank check-1))
    67  +
    68  +    (hashq-remove! ht 'unused)
    69  +
    70  +    ht))
    71  +
    72  +(define HT (load-theme (cadr (command-line))))
    73  +
    74   (SDL:set-video-mode (* 5 TILE-EDGE-LENGTH)
    75                       (* 5 TILE-EDGE-LENGTH)
    76                       32)

Let's run through the obvious points of the patch first.  The first thing it does (line 34) is to make available ‘copy-surface’.  This is a useful abstraction that saves a lot of time.  Next, on lines 42-70, there is the new procedure ‘load-theme’, and finally on line 72, a call to ‘load-theme’ that saves the return value in ‘HT’. 

When we run this (note the new Usage on line 23), it appears no different than before.  That's no surprise, because we are loading the theme only, not really doing anything with it (that's for patch 4).  To explain ‘load-theme’, let's now review the top part of scheme.png:

The overall strategy is straightforward: load the entire image (line 50) and then select parts of it to save in the hash table (lines 56-62), naming them according the scheme.png (lines 64-66).  First, a bit about the naming.  We use 0 and 1 to mean off and on, respectively.  The “rec” stands for “receiver”, the “opp” stands for “opposite”, the “corn” stands for “corner”. 

Next: internal proc ‘read-row!’.  Aside from the iteration, most of it is to set the ‘x’ (line 61) and ‘y’ (line 57) components of ‘rect’ (defined on lines 52-54).  Procedure ‘copy-surface’ selects the ‘rect’ part of ‘theme’ and returns a new surface, to be stashed in the hash table (line 62, with definition on line 51). 

Lastly, here's an exercise for the reader: Can you rework things (a bit) to get rid of line 68 altogether?  (Hint: line 60.)  Well, that was a quick first five minutes, right?  Now onto the last five minutes.  :-D 

patch 4

     1  commit bbf71969fc00fc119c7e31b3d6d753c6591a360b
     2  Author: Thien-Thi Nguyen <ttn@gnuvola.org>
     3  Date:   2022-01-22 05:46:52 -0500
     4
     5      Add sanity check
     6
     7      * hooray.scm (smokin): New proc.
     8      <top-level>: Set random state.
     9      <top-level>: Call ‘smokin’ prior to delay.
    10  ---
    11   hooray.scm | 34 ++++++++++++++++++++++++++++++++++
    12   1 file changed, 34 insertions(+)
    13
    14  diff --git a/hooray.scm b/hooray.scm
    15  index 7212f47..0f8f0bd 100644
    16  --- a/hooray.scm
    17  +++ b/hooray.scm
    18  @@ -18,6 +18,7 @@
    19   ;; Usage: guile -s hooray.scm THEME
    20   ;;
    21   ;; This opens a window, loads THEME (a stylized PNG file),
    22  +;; displays some random tiles from it placed in random locations,
    23   ;; waits a moment, then exits.
    24   ;; The window has title "hooray!!!", maybe.
    25
    26  @@ -72,6 +73,39 @@ (SDL:set-video-mode (* 5 TILE-EDGE-LENGTH)
    27                       (* 5 TILE-EDGE-LENGTH)
    28                       32)
    29
    30  +(define smokin
    31  +  (let ((keys (list->vector (hash-fold (lambda (k v acc)
    32  +                                         (cons k acc))
    33  +                                       '() HT)))
    34  +        (rect (SDL:make-rect 0 0
    35  +                             TILE-EDGE-LENGTH
    36  +                             TILE-EDGE-LENGTH)))
    37  +
    38  +    (define (random-coord)
    39  +      (* TILE-EDGE-LENGTH (random 5)))
    40  +
    41  +    (define (random-tile)
    42  +      (let ((name (vector-ref keys (random (vector-length keys)))))
    43  +        (display " ")
    44  +        (display name)
    45  +        (hashq-ref HT name)))
    46  +
    47  +    ;; smokin
    48  +    (lambda (millisecs)
    49  +      (do ((i 0 (1+ i)))
    50  +          ((= 420 i))
    51  +        (let ((tile (random-tile)))
    52  +          (SDL:rect:set-x! rect (random-coord))
    53  +          (SDL:rect:set-y! rect (random-coord))
    54  +          (SDL:blit-surface tile #f #f rect))
    55  +        (SDL:flip)
    56  +        (SDL:delay millisecs))
    57  +      (newline))))
    58  +
    59  +(set! *random-state* (seed->random-state (let ((now (gettimeofday)))
    60  +                                           (* (car now) (cdr now)))))
    61  +(smokin 9)
    62  +
    63   (SDL:delay 420)
    64   (exit (SDL:quit))

In this patch, we add procedure ‘smokin’, which repeatedly (lines 49-50) selects a random tile from ‘HT’ (lines 41-45), places it at a random location (lines 52-53), and arranges to let it be seen (line 55), briefly (line 56).  This again uses a ‘rect’ (defined on lines 34-36, configured on lines 52-53) in a familiar pattern.  Then, of course, we mix up the random state a bit (lines 59-60) and call ‘smokin’ (line 61).  Pretty simple. 

Here's what I see when I run “guile -s hooray.scm Plumbing.png”: 

Some experiments you might want to try at home:  (a) What happens when you change the definition of ‘random-coord’ to ‘(random (* TILE-EDGE-LENGTH 5))’?  (b) What happens when you comment out line 47 (from patch 3)? 


Copyright (C) 2022 Thien-Thi Nguyen