Most port 'em: a Trail Stash postmortem

My BBKK postmortem took longer, so I’m not sure where to place that, if at all. This is enough of a long read, for a relatively short event!

First, a general note I’m quite pleased TS got as many reviews as it did & they helped me make tweaks I wanted to. My goal, if I submitted two entries, was to get more reviews with my 2 entries than the top entry got all told–and I indeed did so, “winning” 29-27! So even though the final stretch was last-minute and harried, it was neat to hit this goal. Heck, I’d have been happy to “lose” 27-25, even if other things came up and I got distracted from integrating everything I hoped from said reviews. There were just more reviews and reviewers this year, and long may that continue.

Of course a lot of this goal was on the reviewers putting in effort, but I also wanted to make something not too abstruse while still doing something potentially rather new, or something I meant to do.

Trail Stash was both years in the making and last-minute.

Most of the rooms, and pretty much all of the items, were written up in the last week or so. But I’d kept a list of spoonerisms for years. Some were forced, such as a last name that probably didn’t exist, but some, I really liked. Long ago, I thought it would be fun to write a parser game in the vein of Nord and Bert’s Shake a Tower sub-game some day. But I never really saw a mechanism to write an overarching game that played to the strengths of parser gaming while not being too obvious e.g. “find all the spoonerisms and win.” It didn’t feel right for IFComp. So it languished in a private repo.

The first commit of this private repo was October 7, 2017. Using git show (commit) | grep -i Andrew.Schultz I found what it was originally called (I have a new name now!) It was Spell Woken. I feel okay sharing this for reasons you may soon find.

But other things got in the way. I was originally going to have a big spoonerism game with a small palindrome area. But the small palindrome area kept growing and became Ailihphilia for 2018. Then when I tried some spoonerisms, I’m not sure how, but I got “very fine fairy vine.” Then I thought, vine, vile, and that became Very Vile Fairy File in 2019, as well as a few sequels. That, along with other projects, distracted from spoonerisms! And I didn’t really have any special sauce I could add. But I kept putting spoonerisms into my big, long file. I even had a script that would search through my weekly file and move spoonerisms into my big, long file, e.g.

trail stash 99 stale trash # this could be a really neat item! You never know!

The 99 indicated the priority or quality of spoonerism (11 being the lowest, because although Billy Crystal has done some great comedy, “Killy Bristol” gets an 11. Icky Peter might get 88, as a possible NPC. The reason for double numbers was so stuff like 1. this 2. maybe that 3. the other didn’t get caught up) and the # was commentary after–any spoonerism with a comment got bumped to a special section. So this all built up well past critical mass. And of course sometimes it gave ideas for my double-rhyme games.

The break may’ve occurred with Leon Lin’s game Insomnia. For whatever reason, I really wanted to track down all the endings, and he’d put in checkpoints, but I was greedy and demanding. What if you could block out, not just the node where you explored all the dead ends, but all nodes leading to other such nodes?

I’d done this in various parser games after I created the spoonerism repo: Ailihphilia, Very Vile Fairy File, etc. It’s not bad in Inform, using a loop and a temporary property for a room. I even had an option in there for whether or not you want to be restricted for a bonus point room!

Though I cheated in Ailihphilia, which had a spidery-map structure. Every room was out or in from the center, so it had one direction called in-direction.

Pseudocode that may or may not compile
check going when restrict-moving is true: if room gone to is puzzle-left, say "No point. You're done there!" instead;

to decide whether (rm - a room) is puzzle-left-beyond:
  if rm is puzzle-left, yes;
  repeat with Q running through directions:
    if Q is in-dir of rm, next;
    if the room Q of rm is puzzle-left-beyond, yes;

to decide whether (rm - a room) is puzzle-left;
  follow the stuff-done rule of rm;
  if the rule succeeded, no;

This is obviously not very robust, but it did the job for the map structure and I didn’t have to worry about testing for too many bugs!

But what would happen in a map with loops?

I7 code, presented without too much explanation but I hope it's readable
the gong rules are a rulebook. the gong rules have outcomes completed, llp-remaining, and uncompleted.

a room has a rule called this-gong-rule. this-gong-rule of a room is usually the everything-left gong rule.

a room-hint-state is a kind of value. The room-hint-states are points-left, bonus-left, and nothing-left.

player-room-allow-threshold is a room-hint-state that varies. player-room-allow-threshold is nothing-left.

to reset-go-check:
	now all rooms are not go-checked;
	now location of player is go-checked;

check going when player-room-allow-threshold is not nothing-left:
	if the room gone to is nothing, continue the action;
	now hunt-bonus-points is false;
	if the room gone to is overall-go-useful, continue the action;
	say "[one of]A guide gong[or]That guide gong, again,[stopping] rings to notify you that you don't need to go back through [room gone to]." instead;

a room can be go-checked. a room is usually not go-checked.

book gong core definitions

definition: a room (called rm) is overall-go-useful:
	now hunt-bonus-points is false;
	if rm is go-useful, yes;
	if player-room-allow-threshold is nothing-left, yes;
	if player-room-allow-threshold is bonus-left:
		now hunt-bonus-points is true;
		if rm is go-useful:
			vcal "The pride-prong you summoned earlier pokes you to go and see what's ahead, even if it might not be critical to your quest.";

definition: a room (called rm) is go-useful:
	if rm is location of player, no;
	now rm is go-checked;
	process the this-gong-rule of rm;
	let room-done be the outcome of the rulebook;
	if room-done is the uncompleted outcome, yes;
	if room-done is the llp-remaining outcome and hunt-bonus-points is true, yes;
	repeat with R2 running through rooms:
		unless R2 and rm are gong-adjacent, next;
		if R2 is go-checked, next;
		if R2 is go-useful, yes;

I implemented this in Inform and thought, hey, I bet I could do this in twine. I didn’t know what sort of project, until I remembered–if I just wanted a technical demo, spoonerisms would work great!

Twine was more than up to the task. I’m proud of the code I wrote, because the goal forced me to use object-oriented programming, and I think it’s very well-organized. I’d like to think you can guess what the properties are used for, whether or not you know twine.

So much of this, I got from the old forums, and I’d like to h/t @Greyelf in particular for all their help to other people, which is still useful to me today! Their answers to others’ questions helped with ones I had, as well as ones I should have had. I was able to ask for more than I thought I would of Twine, and I got it. I don’t believe Greyelf and I have corresponded, but man … without them, Trail Stash might’ve waited for next IFComp.

Sample item

<<set $itemInfo[‘pert door’] to {
initroom: ‘Ganging Hate’,
useroom: ‘Funny Mound’,
used: false,
taken: false,
map_piece: false,
description: “A pert door stands here on its own. Odd!”,
success: “You place the pert door down. Somehow, it sinks into the funny mound, causing a small sinkhole. The pert door then opens, revealing passage outward to three new areas!”,
reveals: [ “Harming Fights”, “Weedy Nerds”, “Feeling Diner” ]

Sample room

<<set $roomInfo[‘Rusty Fiddlers’] to {
exits: [ [‘Ganging Hate’, ‘back to hanging gate’, ‘(Hanging Gate traversed)’] ],
todo: “Rusty fiddlers make bad music here. They look as if they could use something to give them a boost. Maybe not a temporarily and healthy boost, but a boost.”,
done: “The fusty riddlers, tongues lubricated by the booze clusters, go on about all manner of riddles, abstract or requiring specific language. At least they’re having fun.”,
available: false,
solved: false,
looted: false,
area: 4,
solvedname: “Fusty Riddlers”

Again I hope the properties are clear, though the areas are magic numbers. 0 = the center, then 4 = by the Hanging gate, etc. “Available” switches to true once you solve the adjacent center room. “Looted” just checks to make sure you took the item. This, along with Twine allowing footers and headers, meant most of the rooms were just one macro! I could’ve collapsed things even further, but I was short of time.

:: Peer Class


My main problem then was making something that would show non-trivial cases while not beating the joke into the ground. I managed to get a prototype working with generic rooms and a “solve this room” button to solve a room. It worked! I shelved it.

Then it was September 14 or so and I figured I’d better get going if I wanted to do something. I hadn’t written in all the properties above yet, but some became obvious, and I wrote in one a day. But first I had to decide on the treasures found! I was well aware it would be a dry-goods game, but … I liked what I had, and it seemed worth sharing.

I settled on a relatively symmetric map. It has the 4 middle rooms with 3 rooms each branching from it. This wouldn’t do a REALLY good job of showing things, but it’d block off side areas. A room would be considered solved once you dropped the right item there. I figured the inner rooms could have a passageway (door, etc.) while the outer had treasure. The symmetry worked out well from a developing standpoint: one item each goes from cluster X to cluster Y, with none going from X to X.

And this is where my big long spoonerism file came into play. Its size is well into 6 digits in bytes. I had a lot of ideas for the sort of thing I wanted, and it was a matter of GREPping. It felt like such overkill, having 10000+ lines of which I would take, maybe, 24 really solid ideas. But they were waiting for me. The best candidates rose to the surface, and there were a lot where I cringed and said “Oh, I’d love to use that some time.”

Nevertheless I still had to make up a few on the fly. I also wanted to make things a bit of a chase, but I wanted to clue the player there were no quick solutions. A few fell pretty easily, and a few times just finding a location would open up ideas for the items, and vice versa.

I was surprised and amused to see it all work out, at least, on a basic level. I suspected one item would have to be replaced. “Snide tracks” felt like a placeholder, eventually replaced by Tridents.

I felt it was a nice technical shell but had ideas that it was missing something to make it fly a bit more. @RockmanX pointed out that if this is a treasure game, maybe it should have a treasure map. That will be in the post-comp release. I also put in some bumpers to make it a bit clearer what goes with what, but the basic idea was: the spoonerism version of the item matches the spoonerism of the room where you need to use it. I think I always had the idea floating around from Juhana Leinonen’s Sparkle, from 2014 ShuffleComp.

So I got some ease-of-play fixes in during IFComp. And there was one “oh geez it can wait” bug: the tridents were plural, so I had to adjust the pronouns from the tridents being referred to as “it.” I7 does this automatically, but it’s trickier with Twine! (Other than that, Twine programming was very smooth.) Perhaps people noticed this and felt, why nitpick? But in any case, I enjoyed the challenge of adding to the item object. I just got distracted from bug-testing.

Oh–and one other thing. I had an ulterior goal, sort of. I had had an idea for an EctoComp parser game for a few years–if it had a spoonerism game beforehand to go with it. Once I had the idea for TS, I changed the EctoComp WIP’s name, from “Welp, Haunted” to “Roads of Liches.” And that started things going.

But it felt right to get through TS first, and doing so allowed me to have fun with a bunch of silly jokes/spoonerisms for EctoComp. It wasn’t a serious effort, but I did want to share a fun odd world that potentially gave good laughs.

I still have a big file of spoonerisms. A lot feel original. Maybe I’ll be able to use them some day. Parser or Choice, I don’t know.