I Think We're Property

Dangers of near approach -- nevertheless our own ships that dare not venture close onto a rocky shore can send rowboats ashore --
Why not diplomatic relations established between the United States and Cyclorea -- which, in our advanced astronomy, is the name of a remarkable wheel-shaped world or super-construction? Why not missionaries sent here openly to convert us from our barbarous prohibitions and other taboos, and to prepare the way for a good trade in ultra-bibles and super-whiskeys; fortunes made in [155/156] selling us cast-off super-fineries, which we'd take to like an African chief to some one's old silk hat from New York or London?
The answer that occurs to me is so simple that it seems immediately acceptable, if we accept that the obvious is the solution of all problems, or if most of our perplexities consist in laboriously and painfully conceiving of the unanswerable, and then looking for answers -- using such words as "obvious" and "solution" conventionally --
Or:
Would we, if we could, educate and sophisticate pigs, geese, cattle?
Would it be wise to establish diplomatic relation with the hen that now functions, satisfied with mere sense of achievement by way of compensation?

I think we're property.

I should say we belong to something:
That once upon a time, this earth was No-man's Land, that other worlds explored and colonized here, and fought among themselves for possession, but that now it's owned by something:
That something owns this earth -- all others warned off.
Nothing in our own times -- perhaps -- because I am thinking of certain notes I have -- has ever appeared upon this earth, from somewhere else, so openly as Columbus landed upon San Salvador, or as Hudson sailed up his river. But as to surreptitious visits to this earth, in recent times, or as to emissaries, perhaps, from other worlds, or voyagers who have shown every indication of intent to evade or avoid, we shall have data as convincing as our data of oil or coal-burning aerial super-constructions.
Book of the Damned (1919), ch. 12, by Charles Fort

Eerily repeated in a lot of science fiction. Ironically, H.G. Wells' "War of the Worlds" is nearly a "we're property" story, but Wells irrationally hated Charles Fort. Likes repel, I suppose.

A few obvious ones, but there are dozens more (put them in comments if you like; archive.org links preferred!):

Haunted Halloween

I've added a new seasonal game to my Mystic Dungeon: Haunted Halloween.

A text-mode twin-stick shooter (well, except it's an emulated Atari 800 "text mode", and the sticks are WASD and IJKL, I haven't written joystick support yet).

Five different levels:

  • Pumpkin Patch: Collect pumpkins for ammo.
  • Dark Forest: Find the path through.
  • Graveyard: Easy, just dodge gravestones and monsters.
  • Corn Maze: Unless you're Ted Forth you won't have a problem with this maze.
  • Haunted House: Just run thru the spooky house full of ghosts, and other monsters crawling in from the woods, get candy, get out!

Three difficulty levels:

  • Treat is turn-based, but there's so few monsters & candies you won't get a high score.
  • Trick is real-time, but you can generally outrun monsters.
  • Nightmare has twice as many monsters & candies, so it's the best way to get a high score… or die quickly, completely overwhelmed.

You can also play it like a stealth game, H hides you from non-adjacent monsters, so you can just run in, hide, wait for them to move off.

You collect candies for score (and banishing monsters earns candy), but every time you move to the next level you eat 10 candy to recover a hit and get some free pumpkins. So it's usually better to stay and clear all monsters, pick up all candies, then move on. But if you have a bunch of witches and ghosts, might be worth running away early.

The interesting thing from development is how little code is required for this kind of game. Halloween is under 1000 LOC, and that includes some long text blobs! Portal Worlds was 3000 LOC, Dungeon's over 3200 LOC and not even close to "done".

I'm still working on Public Caves, moving from BASIC to web-tech requires a lot more infrastructure!

Computer Archaeology: Public Caves Discovered!

Exploring the archives of the People's Computer Company (a public timesharing computer center in the early '70s, yes before home computers), and many of the programs we're familiar with from David H. Ahl's Creative Computing come from here. 15 different variations on guess the number and guess a coordinate, sure, but also some really important things, many of them long forgotten.

Then I find this artifact:

pcaves

What the. This is basically a MUD†, from 1973!

Source code (uses a very long TREES library on previous pages).

Everyone knows WUMPUS, which is based on CAVES, but this is the rock star of these! How does everyone not have a copy of PCAVES? This is like finding a working Airwolf helicopter in a cave with ochre handprints on the walls. HOW THE FUCK did cavemen do that? Why don't we all have an Airwolf, if it existed 47 years ago?!

So anyway some barely-modernized version of this will be added to the MysticDungeon soon, you'll all be able to graffiti up a cave!

† more like a MUSH ("Multi-User Shared Hallucination") with one user at a time, specifically.

  • Note: You can play a version from the Narrascope conference 2019: PublicCavesNarrascope
  • Renga in Blue typed it in for the above event, and briefly reviews it as an adventure game. Which it's not, this is a social environment, literally a MUSH.

What I'm Playing: Clubhouse Games: 51 Worldwide Classics

What I'm Playing: Clubhouse Games: 51 Worldwide Classics

I loved the version on the DS, so I got this the moment it was released on Switch.

You start by picking from a set of little human pieces, which you can recolor their skin & hair, but not their appalling clothes. I almost went with Dad there, but in the end Cool Bro looks better. Looking at the random other players later, I see a lot of them chose that or Suit Guy. As I've noted before, Nintendo has Mii avatars, and then doesn't use them in games even where it'd make sense. You see a little face photo of your Mii in some games, but it should use your Mii in the world! Nintendo is so frustrating and anti-social.

Then you go to a globe UI, with figures representing "guides" that give you a menu of a few games. Or you can just pick any game from a preposterously long line menu, or you can hit X (up button) to switch to a grid which is more reasonable. UX is very confused, always a couple extra button presses or spinning the cursor around a too-large area to get to anywhere you want. You unlock more guides by playing games, earning trophies.

Your piece has up to 5 "recommendations", but you can't set them from in the game, you have to go all the way back out to the globe, find your piece, and add them from a list. And these don't help you jump back to a game fast, you have to find it in the grid every time.

Like almost all Nintendo software now, there's no settings for audio, and the "music" is driving me insane, but I need the sound to play some of these, so I'm constantly muting and unmuting. At least in the old days, Nintendo's music had complete scores, but they've apparently fired all their musicians, this is just beep-doo-beep-de-beep, over and over until I stab someone.

Each game starts with couple figures playing the game with often amusing commentary—the kids narrating Connect Four as a samurai duel is fantastic—often enough tutorial for anyone, but it immediately comes up to a menu with "How to Play" and Play, and hitting + in game usually gets a help menu. They're trying to teach you games you may be unfamiliar with. However, showing the tutorial EVERY time you start a game until you hit X (up) is insipid.

There are medals for winning against the AI and playing at least 2-4 times depending on the game, so there's a little grind possible if you're into that.

Nintendo History guide gives you: Hanafuda, Gomoku, President, Shogi, and Riichi Mahjong, which Nintendo made for a century before going into the videogames business.

Many of the games have local and Internet multiplayer, which I haven't yet tried. I expect the usual Nintendo® Quality™ networking, which is to say everything will drop out constantly. I'd rather play against AIs.

Current playlist of 11 good, 16 bad, 25 unplayed doesn't seem all that positive, but the good games are usually very good, and you can just ignore the stupid ones. The constant terrible music is the only strong negative.

I'll keep updating this post as I play more of them.

★★★★☆

bold is good, italic is bad, plain is I haven't bothered to play it yet.

  1. Mancala: aka Awari. Anyone who's typed in games from Basic Computer Games is intimately familiar with Awari. It's a weird little game, but fast and fun, and there's just enough strategy against a smart player (not the AI) to make it hard to win.
  2. Dots and Boxes: "Boxing" is also very familiar from school. The first player (default to you) is at a severe disadvantage, but it's possible to only give up a few boxes to the second player, and then clean up the rest.
  3. Yacht Dice: aka Yahtzee, Poker Dice. Nice enough, but I found the controls a little finnicky, it should not use the "do stuff" button for both pick and reroll. Slaughtered the AI, as one would expect.
  4. Four in a Row: aka Connect Four. Pretty dull, aside from the tutorial.
  5. Hit and Blow: aka Mastermind, Bagels, etc. with an unfortunate translation name. But I dislike the color-matching version, I'm a numbers person.
  6. Nine Men's Morris: I don't understand this game. You start playing while setting up, and it just screws anyone who loses one piece. Also obviously should have been #9.
  7. Hex: Again, should've been #6. It's a road-building game, dumb low-challenge game.
  8. Checkers
  9. Hare and Hounds
  10. Gomoku
  11. Dominoes
  12. Chinese Checkers: aka Pegboard. Not Chinese, sort of checkers except nothing is captured.
  13. Ludo: aka Parcheesi, Sorry!, Trouble, etc. I switch to the 3 dice to come out rule, rather than automatic/on 6, otherwise it's Parcheesi (not quite Indian Pachesi), a good being-dicks-to-each-other race game.
  14. Backgammon: An ancient dice game, a good fun game. I dislike the joycon controls, cursor-moving by spike around the track instead of selecting individual pieces left/right.
  15. Renegade: aka Othello, Reversi, etc., pretty standard. I lost really badly the first round, and then eked out a win, I've always been bad at this game, or anything that requires me to do deep analysis of simple positions (go, checkers, etc.), I'm a broad strategy for complex positions (wargames) thinker.
  16. Chess: There's a sort of lesson program, but the starter AI is incredibly suicidal, so it's not even interesting. Probably it gets harder, but I'm not that interested yet. I dislike the set design, it's very hard to tell the pawns apart from bishops, queen from king, and there's no alternate set option. Still, it's Chess.
  17. Shogi
  18. Mini Shogi
  19. Hanafuda: Very pretty cards, but I've never learned the sets, and visual association like this is harder for me. AI let me win 3/3 on this, which is crazy since I was just clearing chaff, I never saw more than 2 cards of a good set. However, after winning the guide "gave me a gift" of Mario-themed Hanafuda cards, so that might be easier for me. I think this might be worth practicing at.
  20. Riichi Mahjong
  21. Last Card: aka Uno, Crazy Eights. The card branding is almost but not quite infringing on Uno, so it hits that uncanny valley effect, and I kinda hate looking at it. AI didn't stand a chance, I don't know what they were even doing, picking cards at random? There isn't much strategy to Uno, but no strategy means you lose.
  22. Blackjack: Gives a limited number of rounds, and chips but you can go into debt. Does not have Split, which is kind of amateurish, and it doesn't have the dealer check their down card on A or 10 up, so you might play a round and find out they have Blackjack. It's bizarre beyond belief that they didn't make Blackjack be game #21, but #22. But I can always play a few hands of Blackjack.
  23. Texas Hold 'em: aka Poker.
  24. President: aka Asshole, Daifugo, etc. Kind of an annoying party game, giant hand of cards to manage at start. I hate the rich-get-richer mechanic, which is why it's sometimes called Capitalism, but it's more like Monarchy.
  25. Sevens
  26. Speed
  27. Matching
  28. War:

    War, huh!
    What is it good for? Absolutely nothin'
    Say it again, war, huh!
    What is it good for? Absolutely nothin', come on!
    —Bruce Springsteen, "War"

    I timed this, and it takes 5 seconds and one button-press for each card, and it always resets the cursor to the rightmost card, so it takes a minimum of 2.5 minutes. Was this included as a prank?

  29. Takoyaki: Ten octopus. Almost as random as War, but you get a choice when Joker is drawn, and it's much faster. Winning this nonsense unlocked a Mario-themed card deck!

  30. Pig's Tail: aka Buta no shippo. Instead of a little action game of throwing drawn cards into a pile but avoiding matches, it's a completely random War-like, with a slow "penalty cards" deck.
  31. Golf: Cute little putting game, only has 3 clubs: Driver, Iron, Putter. It's not quite a wacky golf or mini-golf, but it's not any kind of realistic golf simulator.
  32. Billiards
  33. Bowling: Has touch controls or joy-cons, but I have a Switch Lite, so I just went with touch. A little rocky start, but then I can get a strike most throws. IRL, my aim is a little too erratic, but I've played hundreds of hours of Ramp Champ and other touch-stroke games on iPhone, so this isn't hard for me.
  34. Darts
  35. Carrom: Like marbles or pogs, but without the freedom of motion, and a strange "queen" you have to take another coin after or you put it back. I don't know that I like this game, it takes too long and the controls are stupid (stick to move up/down only, L/R to aim?!), but it's competent and kind of interesting.
  36. Toy Tennis
  37. Toy Soccer
  38. Toy Curling
  39. Toy Boxing: Lightly based on Rock'em Sock'em Robots, but without the pop-up heads or movement forward/back, just button-mashing. Controls are A/B to wobble your guy's arms out to hit or up to block, which is implausibly hard to switch between, they should've used L & R shoulder buttons. Normal AI is easy, Hard AI is brutal, I assume the others are unwinnable?
  40. Toy Baseball: Accurately simulates a cheap mechanical baseball game from the '60s, with maybe the worst pitching stick control I've ever seen. Once I got the hang of it, I recovered from 0 runs to 3, while the machine that doesn't fumble with sticks got 6. Not likely to play more. There's no Toy Football, as that's not "worldwide".
  41. Air Hockey
  42. Slot Cars
  43. Fishing
  44. Battle Tanks
  45. Team Tanks
  46. Shooting Gallery
  47. 6-Ball Puzzle: A weird collapsing ball Tetris variant, not as interesting as Bejewelled or Tetris.
  48. Sliding Puzzle
  49. Mahjong Solitaire: 20 layouts each for Beginner, Standard, Advanced difficulty. Has a nice color-assist, which is good if you can't easily make out the stack depth. I can see this being a big time-killer for me.
  50. Klondike Solitaire
  51. Spider Solitaire
  52. Bonus: Piano: The piano has a single octave, the help says the buttons or shaking joycons does something, but it does nothing on the Switch Lite at least. Turning the device upside down gets you a synth with 4 octaves selectable by button, but the keys don't rotate into normal position, so it's pretty unusable. I would prefer a real "toy piano" simulator, but then you may as well buy a teaching piano toy or a proper cheap synth, they're $30 or less on the 'zon.

Morning Playlist

I don't have store/play links for these, this is all local, the playlist I listen to many mornings, pick a random start point and play forward. This'll wake you up.

Song Album Artist
Where Do I Begin (edit) Dig Your Own Hole Chemical Brothers
Neurosis Drama Bitter:Sweet
Nth Degree Morningwood Morningwood
One Too Many Mornings Exit Planet Dust Chemical Brothers
Let Forever Be Surrender Chemical Brothers
Airships Futureperfect VNV Nation
City Zen Radio 1990/2000 FM Cure For Sanity Pop Will Eat Itself
X Y & Zee [Sensory Amplification Mix] Cure For Sanity Pop Will Eat Itself
Japan Air Endless Fantasy Anamanaguchi
Life Is Sweet Exit Planet Dust Chemical Brothers
Heartbeat City Heartbeat City Cars
Waking Up Drama Bitter:Sweet
(Reach Up for The) Sunrise Astronaut Duran Duran
Let the Day Begin The Best of the Call Michael Been AKA The Call
I Want It All The Platinum Collection Queen
Sharing the World (feat. Hatsune Miku) Sharing the World - Single BIGHEAD
Streamline Automatic VNV Nation
Hit the Hi-Tech Groove Box Frenzy (Remastered) Pop Will Eat Itself
Satellite Ecstatica This Is The Day...This Is The Hour...This Is This! Pop Will Eat Itself
American Science Notorious Duran Duran
Mandelbrot Set Where Tradition Meets Tomorrow Jonathan Coulton
21st Century Digital Boy Stranger Than Fiction Bad Religion
Twenty First Century Boy 21st Century Boys - The Best Of Sigue Sigue Sputnik
Neuromancer Cyberpunk Billy Idol
The Boy In the Bubble The Essential Paul Simon (Bonus Video Version) Paul Simon
I Don't Like Monday's Emerald Rock Boomtown Rats
Tomorrow People Cyberpunk Billy Idol
Only Solutions Tron (Original Motion Picture Soundtrack) Journey
Too Much Information Ghost in the Machine (Message in a Box) Police
Where Would I Be Without IBM Peace and Love, Inc. Information Society
U.04/01/2003 U.B.L.U.D. Box Frenzy (Remastered) Pop Will Eat Itself
Life In the Fast Lane Eagles Live Eagles
Number One Best Of Chaz Jankel
Demolition Man Ghost in the Machine (Message in a Box) Police
Burn You Up, Burn You Down Big Blue Ball Peter Gabriel, Billy Cobham, The Holmes Brothers, Wendy Melvoin, Arona N'diaye & Jah Wobble
Standing In the Line Midnight Mission Carla Olson and the Textones
Big Time So (Remastered) Peter Gabriel
Not Dead Yet Edge Of The Century Styx
Everything's Cool Dos Dedos Mis Amigos Pop Will Eat Itself
Jesus Built My Hotrod Psalm 69: The Way to Succeed & the Way to Suck Eggs Ministry
Under The Influence Surrender Chemical Brothers
Atom Bomb Wipeout 2097 Fluke
Music Is My Hot, Hot Sex Cansei de Ser Sexy CSS
Control 1000 Fires Traci Lords
Stuck On Repeat Arecibo - EP Little Boots
So Alive Love And Rockets Love and Rockets
Joy Praise The Fallen VNV Nation
More Vision Thing Sisters Of Mercy
Standing (Still) Standing EP VNV Nation
Dream On Surrender Chemical Brothers
Where the I Divides Peace and Love, Inc. Information Society
Erotic Ontology Orbiting Cathedrals Pro-Tech
This Is Ponderous The Best of 2NU 2NU
Solitary Praise The Fallen VNV Nation
Come Sail Away (Edit) Styx: Greatest Hits Styx
Asleep From Day Surrender Chemical Brothers
Surrender Surrender Chemical Brothers
Everybody Wants to Rule the World Songs From the Big Chair Tears for Fears
Flash's Theme The Platinum Collection Queen

LISP Machines

I really wish we had a front-end to software even half as useful as this in the 21st C, but technology has regressed massively since the '80s and '90s. Some of the old IDEs, before they became bloated "enterprise" software (because giant mega-corporations paid for them, not individual programmers, so the IDE makers serve their paymasters), started to slouch towards this kind of usefulness but fast and small. CodeWarrior, Project Builder/Interface Builder, Borland's Turbo Whatever.

emacs isn't the answer, it's an abandonment of the question; "modern" emacs turned away from the LM zmacs model, it's now just a terrible LISP interpreter with a bad text-only-editor front end; you can make tools in it, but nobody sane will want to use them. I'm perfectly comfortable with the emacs editing keys (well, obviously, Macs use emacs keys for all text areas), but the machinery in it is just broken.

I get excited about Chez Scheme having a nice REPL, with history and can edit multi-line blocks in the REPL, unlike the crappy readline almost every other Scheme uses. But it doesn't do hypertext, it doesn't do graphics, it barely has tab completion (procedure names only), it doesn't have any inline documentation & source inspector; all of those were in the LISP Machine.

When I'm working, I have my text editor (BBEdit or Atom mostly), a terminal with the REPL running and I copy-paste to it, 3-4 PDFs open (R6RS, R6RS-lib, CSUG, sometimes TSPL), and a web browser pointed at the SRFIs. If the Internet connection went down, I'd have to search the SRFI sources to figure out what's in there. I really need a better tool for this.

DrRacket can do some graphics inline, and the tab completion shows documentation hints in the top right corner, but as I note every time, it doesn't really have a REPL because it destroys the interactive environment every time you edit code; utterly useless for code exploration. And in practice, Racket is really horrifyingly slow; it does fine in focused benchmarks but real-world use it just falls over drooling.

The graphics part's sort of irrelevant, and sort of not; the way the LISP Machine worked was getting a "presentation" form for an object, which would render as text or drawings or images, and interacting with it sent messages back to that object. That's probably out of scope for anything except a complete new OS and terminal. A simplistic number-tagged hypertext would be good enough and orders of magnitude easier.

So. I'm not sure what to do here, I don't want to just complain about tools and not do anything about them. I could try to extract the docs to make a hypertext doc system; a lot of text processing on TeX source sounds painful, and a one-off job, I want a more universal solution. It may be possible to hook into Chez's completion to call a help system. Or it could be a standalone program that you feed several doc sources into, and it lets you search against them. Dash does that, but it's Mac and C/Objective-C primarily, and does poorly at other docsets.

Retrospective: PortalWorlds

What worked, what sucked, lessons learned, The More You Know, and knowing is half the battle, go Cobra, etc.

JS: Test Your Libraries -1

Especially the hard-to-test parts. I had one "obviously correct" array shuffle function I've been using for maybe 10 years:

WRONG RIGHT
function arrayShuffle(arr) {
    for (let i = arr.length-1; i >>= 1; --i) {
        const j = Math.floor(Math.random() * i); // WRONG. NO.
        const tmp = arr[i];
        arr[i] = arr[j];
        arr[j] = tmp;
    }
    return arr;
}
/** Fisher-Yates */
function arrayShuffle(arr) {
    for (let i = arr.length-1; i >= 1; --i) {
        const j = Math.floor(Math.random() * (i+1)); // RIGHT. YES.
        const tmp = arr[i];
        arr[i] = arr[j];
        arr[j] = tmp;
    }
    return arr;
}

And because of that, I got a distorted shuffle. I only noticed because no mob using my dumb-as-rocks not-even-AI would ever choose to move north… everything ended up piled up at the bottom of the map.

It's hard to test randomness, but you can check that values are in a sane range. For a decade I've had wrong code just copy-pasted into new projects because I never checked my assumptions.

JS: UI Libraries are Great +1

I mean, I knew that. But how easy it is to throw something up is a bit of a surprise every time I do it. It's a fast, dynamic, functional/OOP language that also has good graphics and sound libraries (I didn't do sound in PortalWorlds, but I will before 1.0), and tolerable event handling these days.

I'm not releasing the full source, but in a week or two I'll add a few more things and release my common library under BSD license. This is mostly stuff from my Learn2JS project, and some from TTMS-76, but those have a giant framework for running scripts, this is just a few functions for a pure JS application, and greatly condensed.

Pulling in common code makes this stuff so much easier.

JS: Application Structure -1

There's no proper "run loop" in PortalWorlds, and late in the process that bit me in the ass.

What you should do is, collect events, put them in a queue. Every X milliseconds (I usually use 30 FPS, so 1000/30=33.333ms) have setInterval do an event-update-redraw loop. If something takes a long time or requires animation updates, it must be done in chunks or in a background thread (using Web Workers ). This is true of any application, not just games.

What I did wrong is having events immediately perform actions, and the setInterval just does update-redraw. This is much easier, but it's wrong. I have no control currently over blocking user actions, and animation has to all happen at once.

The trick I used is to keep a "frame" counter, and that chooses animation frames, moves floating text and the tracers of missiles and fireballs. Next turn invoked by user event just wipes those out. An amusing side-effect is missiles vaporize corpses as they fly past.

Switching to a correct run loop isn't super hard, but does require changes to all my timing and animation hacks, so at this point it's not worth it.

JS: Fonts -1

I used Oryx's "simplex" font, which looks fine and bitmappy in web pages, but in Canvas it gets antialiased, and I wasn't able to make it stop. So I have to make all fonts slightly larger than I'd like, and they're still kind of blurry.

The smarter way would be to use a bitmap font, and a very simple bitmap text renderer. That's what I do in Perilar: Dark Weaver, with an ATASCII-inspired fantasy font. But in 7 days I didn't have time to write and test that.

That's on a "maybe later" list for 1.0. Alternately, I could figure out how to get them antialiased in Canvas?

JS: Minimizer -1

I used to use yuicompressor, which renames variables and aggressively minimizes your code into unreadable but very compact line noise. But with the long-drawn-out death of Yahoo!, that hasn't been updated in a decade, and it doesn't handle ES2020.

I could probably have run everything thru Babel and then yuicompressor, but it's time to move on? So I just used Crockford's jsmin which only removes whitespace. It might be a good time to look into compiling JS to WASM binaries.

My build.zsh script:

#!/bin/zsh
rm -rf portalworlds*.zip
rm -rf build
mkdir -p build
mkdir -p build/js

# *.js -> .min.js
for f in js/*; do
    n=`basename $f .js`
    perl -ne 's/const DEBUG = true;/const DEBUG = false;/; s/^\s*DLOG.*$//; print;' $f >build/$f
    jsmin-crockford <build/$f >build/js/$n.min.js "Copyright (c) 2020 by Mark Damon Hughes. All Rights Reserved."
    rm build/$f
done

# index.html
perl -ne "s/\.js'/.min.js'/; print;" index.html >build/index.html

cp -R favicon.* style.css i ttf build
cd build
zname=portalworlds-$(date "+%Y%m%d_%H%M%S").zip
zip -r9Xq ../$zname * -x "*.DS_Store"
cd ..
echo $zname

Game: Combat Design +1

So I knew I wanted combat to be very swingy (allowing anything from instant death to instant kill), but not have levels or many stats; I didn't have time or inclination to make another detailed RPG!

Instead, combat works by adding your current strength and enemy's current strength, rolling 2 dice in half that range, so central results are more likely, but it's easy to get results at either far end. Then apply the difference from your strength as damage to you or the monster, depending on which side of the line it's on:

const roll = Math.floor(dice(2, this.strength + mob.strength)/2);
if (roll <= this.strength) {
    const dmg = Math.floor( (this.strength - roll) * (100 + this.getDamage()) / 100 );
    mob.takeDamage(dmg, true);
} else {
    const dmg = Math.floor( (roll - this.strength) * (100 - this.getDefense()) / 100 );
    message(mob.toString()+" hits you for "+dmg+" damage");
    this.takeDamage(dmg);
}

Kind of ridiculously simple, but it makes for symmetric combat, so player-attacks-monster and monster-attacks-player have the same results. It's not practical for a tabletop game, but this is a really fun mechanic in a computer game.

Damage and Defense from gear just modify the result by percentiles, they don't affect attack roll at all. If your strength + defense % is less than the monster's strength, you can be one-hit-killed, and vice versa.

Experience adds to current strength, and increases base strength if you're near max; you need to finish fights against slightly superior foes unharmed to grind up strength.

Game: Spells +1/-1

I knew I didn't have time for a lot of magic, and I never feel like I'm finished with magic systems anyway. So here I just picked 4 types: AOE damage, AOE control, escape, and heal; or as the game knows them, Fireball, Sleep, Invisible, and Heal. Then rather than have MP and worry about regeneration, I just give you "base" spells in each based on class, and you can pick up scrolls to add new points to current spell total. When you heal with mana potions or going thru a portal, you get back your base spells.

Last three were trivial: Sleep just searches a radius and has a die(100) > strength chance to give humanoid or animal targets a sleep condition for 2d4 turns; if they're sleeping, they skip the turn. Invis just sets an invis condition for 2d4 turns; mobs ignore you if you have that condition. Couple places like melee and taking damage I manually clear the Invis and Sleep conditions. Heal just heals 50% of base strength, woo.

Even with just 4, doing Fireball turned out to be quite challenging (the Application Structure problem); how do I show it go across the board, maybe have a turn or two delay, then explode? Well, it does it by moving 4 steps every turn, and only having the fake animation for a tracer of where it's been.

Game: Permadeath +0

Permadeath happened more by lack of save state than any intention. I much prefer to have save slots, and then you can choose to save your game and reload last or earlier, or you can play hardcore and never reload. It puts the moral burden of choosing permadeath on the player, not the developer.

But JS localStorage doesn't really have room to stash a huge amount of data. In Reaper's Crypt I had a very aggressive compression for the map (which looks like a giant grid of tiles but it's really about 64 rooms per level), and it's still problematic, sometimes maps just can't be saved.

I could save the character as of last portal you entered, and regenerate maps. But that encourages "stair-scumming", which I do have a problem with. So instead this is very arcade-like, you just play until you die, then insert another quarter.

I do plan to add a scoreboard, both local and maybe server-based for a copy hosted on mysticdungeon.club.

Personal: Shipping is Awesome +1

Actually finishing something and making it public without a decade-long process is amazing. It's not perfect, and I know nothing is perfect, I should just ship things, but I can't normally do that. Having a hard deadline and meeting it was the best feeling.

Personal: Drone-Slack Balance -1

I was desperately exhausted afterwards. Bone tired all day Sunday, and I'm still feeling it on Monday. 7 days in a row, even with a couple shorter days, is about 3 or 4 days too many.

I'm nocturnal. I like to wake up ideally just before midnight (but sometimes backslide to evening if Real Life interferes), eat and coffee, then work until dawn, walk the dog at sunrise, maybe get my own walk in (tiny dog cannot keep up for a mile+), then finish up and goof off until early afternoon when I can sleep. The schedule weirds out some daywalkers, but it's quieter and more compatible with "morning people" (ugh) than waking in afternoon and sleeping at dawn, which I did for decades.

But normally that work is 3 days of code, 1-2 days of writing or art, 2-3 days of just playing videogames or going Outside, doing Real Life AFK stuff.

Drones who can work all the time frighten me, they're basically Terminators. People who Slack all the time frighten me, they're a waste of precious oxygen & water, and I may foolishly try to rely on these people and get nothing. You need to be in the middle area.

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

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.

Project Status

Made a lot of progress on Perilar Dark Weaver map generation. Hopefully this week I'll get ruins finished.

Spent a little too much time on the new Mystic Dungeon, the TTMS-76 virtual retro-console started on tilde.town but is now replacing the BBS. It now has scoreboards for the games! I still need to finish Heist, which turned out to be bigger than I first thought. In a bit I'll get yet another damned login system done, and from there a forum. I wonder if anyone else would be interested in making games for it? I've got a trivially easy framework, so if you know any Javascript at all it's fun to work with. All BSD licensed.

Updated StupidComments.css to block some more inline "affiliate" blocks and Youtube spam segments.

I started making a console Pomodoro timer, and it works, but needs persistence and a teeny bit of task management before I can release it. Very soon.

RPG-wise, I wrote a bit more of my "survival D&D" game Delvers in Darkness (aka Dungeon Hell), which is looking to come in well under 16 pages for a full Holmes-type dungeon game; maybe 32 if I write more on the setting, which since I complain about that in everyone else's games, I should. Haven't looked at my light game in a bit, and don't know when I'll get back to that.