Scheme-Test-Unit

Moving my library from Chicken to Chez (when I moved it from Chez to Chicken originally, it was much smaller and I only used a few asserts to test), I discovered that:

  1. Chicken's test egg is pretty nice but non-standard.
  2. SRFI-64 (from Thunderchez ) is OK as far as it goes, but has an inadequate test runner (the default just lists PASS/FAIL with no explanation for each test, and has one total). Ridiculous when you have dozens or hundreds of tests.
  3. There's no good alternative. There's a SchemeUnit which is for PLT Scheme née Racket, and a couple others which aren't SRFI-64 and aren't for Chez.

So I ended up writing my own:

Here's how it works:

#!/usr/bin/env scheme-script
;; example-test.ss

(import (chezscheme)
    (srfi s64 testing) ;; thunderchez
    (scheme-test-unit)
)

(define (all-tests)
(test-group "All Tests"

(test-group "Math"
    (test-equal "add" 4 (+ 2 3))
)

(test-group "Strings"
    (test-equal "append" "foobar" (string-append "foo" "bar"))
)

) ;; test group "All Tests"
) ;; all-tests

(define (main argv)
    (scheme-test-configure argv)
    (all-tests)
)

(main (command-line-arguments))

----
% chmod 755 example-test.ss
% ./example-test.ss --help
Usage: scheme-test-configure [-v|--verbose|-q|--quiet|-o FILENAME|--output FILENAME]
% ./example-test.ss
*** All Tests START
*** Math START
- add [(+ 2 3)] expected <<<5>>> but got <<<4>>>
*** Math END, PASS: 0 / FAIL: 1
*** Strings START
+ append [(string-append foo bar)]
*** Strings END, PASS: 1 / FAIL: 0
*** All Tests END, PASS: 0 / FAIL: 0
FINAL PASS: 1 / FAIL: 1

My own test cases come out:

% ./marklib-test.ss -q
*** Control END, PASS: 4 / FAIL: 0
*** Logic END, PASS: 12 / FAIL: 0
*** Math END, PASS: 18 / FAIL: 0
*** Strings END, PASS: 29 / FAIL: 0
*** Any END, PASS: 31 / FAIL: 0
*** Hashtable END, PASS: 9 / FAIL: 0
*** List END, PASS: 6 / FAIL: 0
*** Maybe END, PASS: 6 / FAIL: 0
*** Stack END, PASS: 10 / FAIL: 0
*** Vector END, PASS: 8 / FAIL: 0
*** Data Structures END, PASS: 0 / FAIL: 0
*** Dice END, PASS: 7 / FAIL: 0
*** All Tests END, PASS: 0 / FAIL: 0
FINAL PASS: 140 / FAIL: 0

Programming on Your Phone

Pythonista lets you use your pocket UNIX workstation as a workstation. I use Pythonista, if not every day, very heavily on the days I use it. As always it's crippling of Apple that there's no upgrade pricing, so I can't give him more money every year that I keep using it. The new keyboard module is an interesting script launcher, but I already wrap a bunch of utilities in a main menu program.

There should really be more of these mobile programming environments. In the early days, Apple severely restricted you from shipping one; you could kind of cheat with JavaScript, and a few games snuck in some bytecode interpreters, but scripting was right out. They loosened up eventually, but are still dicks about you saving code anywhere it could be shared, so for example I have to keep my Pythonista stuff in iCloud, not DropBox where it'd make more sense.

  • Panic's Coda and Coda for iOS (née "Code Editor" WTF) is the only other one that's really functional; I've built real web sites out of it, but I mostly use it for ssh. Sweet baby Cthulhu, I hate Panic's crooked-text "designer" sites, I hit Reader view on those instantly. Designers shouldn't be allowed access to CSS or JS.
  • Hotpaw BASIC still works (as does his Chipmunk BASIC on the Mac), but hasn't been updated in 2 years. Not that I want to program in BASIC, but it's better than no programming at all.
  • The iPad used to have a very nice "BASIC!" (with a structured BASIC and a bunch of system functionality), and a very limited "iSkeme" (scheme interpreter, R5RS-ish? with nothing but text I/O), but they were killed in the 64-bit-pocalypse. Update 2020-09: miSoft Basic! has been updated. Searching for this is utterly impossible!
  • Workflow (née Apple Shortcuts) is great for putting a few tasks in a row but you'd go insane trying to write anything complex from drag-and-drop clicky boxes.
  • Apple's Swift Playgrounds on iPad is a tutorial, not really usable for applications AIUI.
  • There's a bunch of "kids learn to code!" apps that are mostly ripoffs charging $60/year to play robot tanks. Do not buy anything like this.

I dunno if the 'droids have anything comparable, I'm sure they can root their phone and try to use vi in a busybox shell, but that's not a reasonable work environment for a thumb-sized on-screen keyboard.

7DRL

I'm planning on doing the 7DRL challenge, which runs from Saturday Feb 29 to Saturday Mar 7.

I did some design work this morning, picking out tilesets and making some index card notes for my 7DRL. Think I have kind of a neat world model, and different combat/levelling system.

I expect to have my Chez graphics library fully ported by then (I can draw sprites now! Events are much harder.), but getting it to compile on Mac, Windows, & Linux? Way out of scope, as I found out when I did Eldritch, catastrophic waste of effort for a freebie game. So I'm probably doing it in JS, just so much easier to upload a web page.

What I'm Watching: BoJack Horseman

Finally finished BoJack Horseman's final sixth season, which could've been just 3 eps without any loss. Just endless character vamping; not even development, because they can't develop further and there's no arc, just everyone gets a pony and a birthday cake courtesy of the writers, except BoJack who continues to fuck up.

Season 3 was really the peak of the show, and would've been better if they'd wrapped up seasons 4-6 in season 4.

Disliked the supposedly emotional songs, which in previous seasons had been a little trite but fine, this one it's heavy-handed and mediocre lounge-pop.

The death dinner show, "The View From Halfway Down", was great, perfect, the kind of closure and horror of death we all need from this, except that it dragged on forever and I loathe BJ's family.

None of the people who get their lives together in S6 are capable of doing that, every one of them would be a terror to live with. OK, Mr Peanutbutter, sure, he's an oblivious narcissistic asshole so he's capable of happiness because of it. But Diane's not capable of not wrecking her life; control-freak Princess Carolyn's not capable of not overmanaging control-freak Judah, that'd last 10 days if they're lucky; Todd's attention span is slightly shorter than the lifespan of the little people he's supposed to be taking care of.

I honestly expected more of a "BJ is driven into the desert and digs his own grave" or just end the death dinner show in death. Maybe a funeral closer? But you can't put a dozen people pissing on his grave in an animated show, even one for adults. And killing him would prevent the inevitable revival series in 3 years.

★★★½☆ for the show as a whole, ★★★★½ for bits of it, please don't bring it back.

Green Window Means Thunderchez is Working

This weekend, I wanted to have dynamic access to SDL for some interactive graphing, and again ran into the problem from More Fun and Swearing with Scheme. With more experienced eyes, I read the Chez Scheme User's Guide and discovered everything I'd done wrong a year+ ago.

First, install Thunderchez somewhere, I just put it in ~/Code/CodeChezScheme

In particular, read ffi-utils.sls which provides somewhat easier wrapping of binary flags; thunderchez is a very low-level port.

Second, install SDL2, I used port install libsdl2 and that puts it in /opt/local/lib, but in production I'll use a local dir. Read the SDL2 API for reference.

Took me a few hours to translate the basic functions I needed, and now I have:

sdltest.ss:

#!/usr/bin/env scheme-script
;; sdltest.ss
;; Copyright © 2020 by Mark Damon Hughes. All Rights Reserved.

(import (chezscheme)
    (sdl2)  ;; thunderchez
)

;; Set this to your location. I used `port install libsdl2`, YMMV.
;; In production, I'll use a local dir with libs.
(load-shared-object "/opt/local/lib/libSDL2.dylib")

(define kWindowWidth 320)
(define kWindowHeight 240)

(define gWindow #f)
(define gRender #f)
(define gState 'quit)

(define (gr-init title x y w h)
    (sdl-set-main-ready)
    (sdl-init sdl-initialization-everything)
    (set! gWindow (sdl-create-window title x y w h (sdl-window-flags 'shown 'allow-highdpi)) )
    (set! gRender (sdl-create-renderer gWindow -1 (sdl-renderer-flags 'accelerated)) )
    (sdl-show-window gWindow)
    (sdl-raise-window gWindow)
)

(define (gr-event)
    (make-ftype-pointer sdl-event-t (foreign-alloc (ftype-sizeof sdl-event-t)))
)

(define (gr-rect x y w h)
    (let [ (rect (make-ftype-pointer sdl-rect-t (foreign-alloc (ftype-sizeof sdl-rect-t)))) ]
        (ftype-set! sdl-rect-t (x) rect x)
        (ftype-set! sdl-rect-t (y) rect y)
        (ftype-set! sdl-rect-t (w) rect w)
        (ftype-set! sdl-rect-t (h) rect h)
        rect
))

(define (gr-mainloop)
    (let [ (event (gr-event)) ]
        (let mainloop []
            ;; event
            (sdl-poll-event event)
            (let [ (evt-type (ftype-ref sdl-event-t (type) event)) ]
                (cond
                    [(eqv? evt-type (sdl-event-type 'quit))  (set! gState 'quit)]
                    [(eq? gState 'main)  (main-event evt-type event) ]
                ))
            ;; TODO: loop polling until no event left
            ;; update
            (case gState
                [(main)  (main-update) ]
            )
            ;; render
            (sdl-render-clear gRender)
            (case gState
                [(main)  (main-render) ]
            )
            (sdl-render-present gRender)
            ;; loop
            ;; TODO: wait timer, currently burns CPU
            (unless (eq? gState 'quit) (mainloop))
    ))
    (gr-shutdown)
)

(define (gr-shutdown)
    (sdl-quit)
)

(define (main-event evt-type event)
    #f ;; no event handling yet
)

(define (main-update)
    #f ;; no updates yet
)

(define (main-render)
    (sdl-set-render-draw-color gRender 0 255 0 255) ;; green
    (sdl-render-fill-rect gRender (gr-rect 0 0 kWindowWidth kWindowHeight))
)

(define (main argv)
    (gr-init "Hello, Thunderchez!" 100 100 kWindowWidth kWindowHeight)
    (set! gState 'main)
    (gr-mainloop)
)

(main (command-line-arguments))

To start it I just need:

% CHEZSCHEMELIBDIRS=thunderchez-trunk: ./sdltest.ss

Green window means it's working! And it can be closed with the window close button or Cmd-Q, but menu or other events don't work yet.

So… to port my graphics library from Chicken to Chez is probably a week's work, maybe less—I only fill rects, blit images, play sounds, and read a few events. My sound support on Chicken is minimal because the maintainer didn't bother to port the audio API, I had to do that myself.

Rehosting my game on Chez is an unknowably difficult debugging problem, but I code in a way that's usually portable. Large chunks of my large marklib library exist only to work around the non-portable parts and missing features of R5RS.

But the advantages are:

  1. Chez's R6RS is a better language than Chicken's R5RS++/R7RS-not-really.
  2. Chez is much faster, sometimes just 10-25%, sometimes hundreds of times, even compiled.
  3. Chez's JIT compiler is fast and invisible. Working on Chez is instant gratification. Chicken has a 30-second compile before I see anything, which has been impossibly frustrating.

At the very least, if I get my graphics library ported I can do the graphing I wanted last weekend.

What I'm Watching: BLAME!

BLAME! was a manga by Tsutomu Nihei from the late '90s/early '00s, like the result of listening to the Terminator soundtrack and Front Line Assembly and drawing that. A weird loner named Killy with an overpowered gravity gun, wanders an infinitely large ruined city, infested with Exterminators that want to kill all the unauthorized Humans, as he searches for someone with the Net Terminal Gene which would allow Humans to connect to the city again. It's bitter and mostly silent, harsh industrial lines and weird kabuki-masked spidery bots and fake people.

It's not quite "cyberpunk", because it's not the street finding new uses for (military-corporate) technology, but techno-savages trying to survive the street killing them. Cyber-apocalyptic, like Terminator's future, Screamers, or Hardware.

What I didn't know is there was a movie made, available on Netflix!

And, uh, it's the manga. The point of view characters are Human survivors in a village, and why they were mostly safe (but dying out) until Killy shows up is the main plot. The city is as brutal and unliveable as the manga, and the technical scenes are fantastic. Killy is quiet and blank, because he knows they're screwed and they don't have what he wants, but he'll help as long as it's practical and he might get some advantage over Safeguard. Very slight nitpicks: There's only a couple of bot designs instead of the rampant cyberization of Human and near-Human and the whole environment of the manga. There's a plot twist I didn't see any clues for, but I might've spaced out at some run-and-scream bit. There's a lack of discussion of the nature and motives of Cibo, that I think was also needed. Maybe the Man in Black Rides Off Into the Sunset ending and denouement is a little trite for the manga which is so harsh and unforgiving. But for anime adaptation of an impossibly harsh and inhuman source material, I've never seen better.

★★★★★

What I'm Reading: Instantiation, by Greg Egan

Egan's always been best at short story length, writing an idea that cuts away your Human delusions of self-importance and self-awareness, and then terminates. His characters have maybe more depth now than they did 30 years ago, but it's focused on the task at hand. A number of these skip forward in time rapidly, sketching out a scene and then a # section break and it's months or years later; generally obvious from context, but I'd prefer timestamps.

I'll try to be vague but it's impossible to say even how a story worked for me, without some hint of what it is; you might want to read these cold. If so, you can probably skip Uncanny Valley or Break My Fall.

 

  • The Discrete Charm of the Turing Machine: The soft AI apocalypse, as they take our jobs but nobody can figure out how to stop it. Borderline positive view of Human adaptability, which we also got in Perihelion Summer, which is always a little surprising from Egan.
  • Zero For Conduct: Afghan girl in Iranian school cultural piece, see also Egan's novel Zendegi, with a bit of SF on the side; I don't believe the SF widget is plausibly that easy, or it would be found by someone other than the protagonist, but it's a cute enough story.
  • Uncanny Valley: Legal wrangling around a kind of immortality. I've previously read this online, and was bored out of my skull by it then; the character development/backstory would be interesting if the setup wasn't all for accountants and lawyers.
  • Seventh Sight: Not quite a Reasons to be Cheerful but improved sight doesn't make everything better. OK, sure. I would expect this to be commonly available as glasses or a shitty phone app long before it became available as implants, bicycles before rockets and all that. Feels like a Vernor Vinge short story, in the good idea/half-assed delivery way.
  • The Nearest: Excellent story about alienation and how fallible Human meat brains are. Almost exactly what I read Egan for (although there's no technical cause or solution, which he'd normally provide).
  • Shadow Flock: How do you defend against insect-sized drones? Almost every kind of security is just nonexistent. Egan barely touches on this; it's kind of a straightforward heist story with the inevitable twist (see also Rick & Morty S4E03 "One Crew"). I think I have some technical arguments against the sight & sound suite these things have, real insects don't have great senses because physics makes it difficult, but maybe it's solveable with enough software post-processing?
  • Bit Players: A woman calling herself Sagreda awakes in a world that makes no sense, immediately tests the physics and deduce the nature of the world, and exploits that, as life always does. So, this is 100% in my wheelhouse. But I question the peaceful nature of this world. Maybe it's just lucky that there's not psychotic adventurers running thru here, and the next world over is blood-soaked? James P. Hogan's Realtime Interrupt and Terry Bisson's In the Upper Room deal with this at length, and I'd like to see Egan address it beyond contempt for barrel-bottom shovelware and misuse of AI. Also the setting reminds me strongly of one of my favorite joke D&D adventures, There's No Place Like Up by Paul Jaquays, in WG7 Castle Greyhawk: "If the PCs wish, they can fall forever".
  • Break My Fall: I assume this is a fragment from a book in progress, or perhaps a fragment that didn't gel into a book; it seems of a similar setting to The Four Thousand, the Eight Hundred. An interesting if unlikely low-powered spaceflight solution, a disaster, and people doing their best to fix it… but it goes nowhere and the finale has no closure at all.
  • 3-adica: Sequel to Bit Players, Sagreda and Mathis move on to new worlds and try to find the way out. The world most of it is in, is the kind of shithole I would expect adventurers to like. The titular world is a bizarre mathematical premise, like Rudy Rucker's White Light, and I don't know how to visualize it; I get the trick of movement, but not how you'd even exist particle-by-particle within it. Very much the middle third of a novella.
  • The Slipway: A cosmic disaster story, the like of which Egan hasn't done since Distress or Diaspora. The Pane's an interesting Big Dumb Object, and reminds me of the Artifacts in Charles Sheffield's Summertide series, but I think either people would panic to the point of global disaster, or not care in the least, and the middle ground here is unstable. I'm not clear on how you get the long-distance view until old light has passed and new light reaches the Earth, a few years out. On the one hand, this is the safest possible place for Earth, on the other hand it's not great for long-term exploration, and on the gripping hand I would be surprised if there were any more Panes in their new location.
  • Instantiation: The finale of Bit Players and 3-adica, Sagreda spends much of this one following someone playing Kurt Gödel in a Vienna intellectuals killing Nazis game. I understand Gödel's Incompleteness Theorem (mostly?), but every explanation of it in plainer English/German sound more insane than the last. A moderately clever heist/con game, and finally a conclusion to a story. Yes, for once Egan mostly wrapped up a story without an apocalypse or "well what do I do now?"

★★★★★ despite a couple clunkers