You Won't Get Her Back postmortem

Well, this blew up a bit, but I hope it’s worth it.

First, I’m really grateful to the organizers allowing us to fix bugs–there were a bunch that were easy to fix but hard to test. I don’t go into a comp deliberately saying “hahaha I’ll just fix any stray bugs” or “hahaha I’m not perfect, deal with it” but in this case it was a relief. Given the sort of fixes I was making during programmer-testing, I knew there was stuff I was going to miss, even as I tried to keep things simple. I didn’t want to use that as an excuse, and while I had a dead period where maybe I could and should have had things to look at, I’m happy with the bug fixes I made during the comp, and having that buffer to fix bugs helps me to experiment and have some degree of ambition with this project. I also think that it’s good not to allow actual added content, as fixing bugs is harrowing enough, and post-comp releases are neat, anyway. Garry Francis also alerted me (and the community in general) to how it would be a good idea to track updates in whatever way possible–this might be something for the comp organizers to address, as I realized it wasn’t too bad for me to provide separate binaries with various bug fixes.

One thing I mentioned elsewhere was that two people were kind enough to file bug reports at my github repo at GitHub - andrewschultz/you-wont-get-her-back: ParserComp 2022 candidate. If you don’t have github, or you don’t understand source control, I think it’s worthwhile to establish a repo just to have a place where people can report issues formally and you can fix them–or you can even do so for yourself! Some issues you know are going to be a problem, but you can’t or don’t want to tackle them right now. And it’s good to find a foothold, or to be able to alert judges and players about what may be planned for post-comp. While for in-comp bugs, allows people to report things in a comments thread, I like being able to have things separated by issue and the ability to explain bugs without clogging up general game discussion. I’m a bit embarrassed to admit a lot of the fixes were one-liners I could and should’ve seen with a bit more testing, or if I could’ve slipped in the features before sending it to my testers, they’d have found the silly bugs immediately.

As usual, I had a good combination of testers I’d worked with before and testers new to the process, and they combined to help me see things I couldn’t have seen on their own. (I just wish I’d asked earlier. As it turns out, I got a good jump on the game, thought “nah, it’s too early to ask for testers for such a small game,” then put it aside, then realized it was close to too late.) One thing I sort of saw but failed to take action on was to label the board coluns/rows. When I did so I realized I probably took more energy making excuses not to than writing code. So it goes. One tester asked to be able to adjust the chessboard square width, which was a pleasing feature to implement and then hash out the bugs for. But if I’d even just coded the column and row labels, I’d have left myself a lot more time to write the narrative text I kept putting off. (That’ll be post-comp.) As it was, they found some good bugs (they always do! It saves a lot of stress!) and also made some really good suggestions for features that couldn’t break. Right?

Narrator’s voice: they could. And they did, and I cringed when I saw what I did wrong. One was incorrectly using “the noun” and the other was defining a for loop from 1 to 8 instead of 8 to 1. These and others were some one-line fixes where it felt great I didn’t have to do much, but it would’ve been nice to find those tweaks before. People reported this stuff within the first day to me via email, people who’d tested for me before, and I appreciated them following up greatly (and their diplomacy in not saying “boy, this looks like a big bug,”) because I know I tend to let this sort of thing slip when I try to add just one more thing. And I try to check for it. But I missed it. So I’m grateful for when testers make me feel I’ll have the time for one more feature. Most of the ones added late were for readability and minor options. I’m glad I put them in, on balance, even if I’d have had to live with the bugs. But @DorianPasser’s intfiction topic on using time intervals reminds me of how it is very useful to take the time to add a small feature one day and maybe test it the next.

As for placing, sneaking in the top half again this year was a goal, and it seems just about right given the oddness of YWGHB. There were really strong and creative and involved and worthwhile games ahead of me, obviously. And I managed to do something I wanted to do. So the results are more than satisfactory, given everything I realized I missed along the way. But what about the process? (Procrastination aside.)

I’d done other games about chess, but they felt more didactic than anything else. I had a story around them, but it was more about “oh hey there’s a war on.” And I said to myself, if I wrote another game, it would try to have more of a personal story. Not necessarily a big one, but one where you had actual opponents and they would react to your moves. Magnus Olsson’s Zugzwang started to show the way to that. It was a relatively simple king-and-pawn endgame with three possible endings. I found the win without much trouble, but I was intrigued by how you could mess up, and I was pleased the author programmed the loss as well as the draw. Could I do the same?

I couldn’t have too many pieces strewn around, because it would be hard to program, especially in Inform. But I did want to allow the player to be able to use different types of notation and commands, because this was ParserComp, after all. It would be fun to program, whatever the puzzle would be. It’s hard for chess puzzles to give most people any emotions beyond “dang it, this is too frustrating, too many rules to remember” and “haha, I got it, easy-peasy, let’s move on.” And you don’t want to get bogged down in rules or terminology.

And I used the same method or self-talk I did to come up with other entries. “Well, I don’t see a way to write something, but if I did, it would have to be like so,” or, “Well, this can’t quite work, but it’d get close, but barrier X is too high.” Then I decide, what the heck, I’ll make a prototype because I’m just curious. It’s a sort of negotiation with myself, a building confidence, or more accurately meta-confidence. I have confidence I will have the confidence to plow through creative and technical barriers.

Then “the obvious one” hit me, and it almost seemed too obvious. And it had other problems, too. It was one I was quite surprised and pleased to have solved in high school. I liked the combination of trail and error with a moment of intuition, but would the last step be too much of a jump? Perhaps having it expressed in VERBS or wherever might lose the shock value. I did want to pass along that feeling when I got the problem first, and while it’s fun to go over the puzzle, you only get that rush of solving it once. And while a look at the solution may make you say hey, yeah, clever–it’s not the same as solving it. I wanted an interesting struggle without it getting too “what’s in my pocket?” I worried about the instructions spoiling everything too much, but I should not have, because I’ve enjoyed a lot of other parser games that said “all the commands you need to win are here.”

major spoiler about the solution/plot point(s)

I thought about Kurt Vonnegut’s story All the King’s Horses, too, when I was putting ideas together. In it, there’s a chess game for a prisoner’s freedom. An American Colonel is captured by communist guerillas and plays a chess game where for every piece he loses, one of his men is executed. The plot twist is–he sacrifices his son to win, and his captor is about to kill his son, and his captor’s wife kills him. But this always felt a bit overwrought to me, even if I couldn’t explain why when I read it for Senior English. I felt there must be a better example, or (dare I say) I’d write things a different way, even if I didn’t know how yet, and my teacher (who was a good teacher and a good person) was perplexed I didn’t enjoy the story more, even though it was seemingly dang-near targeted for me. But he encouraged the class to follow through on this sort of question, and years later, I managed to answer such a question, at least sort of, by writing YWGHB.

And it occurs to me I’d never really written about loss or sacrifice, even if I had plenty to say about dealing with annoying people who cost you in other ways and maybe about “losing” them. So this was a new angle for me, even though I wanted to load it up with snark from the black king.

The basic stuff pulled together at the start of May. The technical hull of the game comes first for me, and I think I was surprised how much came together, how fast, just by doing the stuff that seemed dumb. I figured there’d be potholes, but creating the first “oops” endings with the tables and core code to handle whatever else I wanted to drop in–well, it was a big leap early, and it allowed me to find and implement the trickiest accomplishment, which was tracking repeating moves as much as possible. Tied up in this was a move log which checked off a draw by repetition, and which had a bug I’ll get to in the shop talk.

I even wrote test scripts, which was the first time I’d really gotten ahead of the curve (before, I just squeezed things in last-minute,) and perhaps I was so shocked and unprepared to be ahead of schedule that I put things aside for a month and a week until I was good and behind schedule as usual. Things felt normal again, in a way. Though one speedbump may’ve been the post-comp releases of Shuffling Around and A Roiling Original, which–well, they’re still ongoing, but they were worth making progress on. That said, YWGHB was easy to test, and I was able to code some simple test commands e.g. “FORCE RG6” means the enemy rook would move to g6 once it had a choice. So I was able to test everything, and pretty quickly, too, since it was a z-code game without a lot of objects. Perhaps I got overconfident once my tests passed, and I slacked on the narrative tweaks that can’t be tested, but I did have tests in place, and I was able to add test cases when testers reported stuff. And I was also able to send in updates quickly after making bug fixes. So the preparation was good, there.

But I think I also caught myself in something small. I had defined a lot of booleans about the game state, when it was easier to wrap things up into value types. I have a terrible time knowing when to define values, but here it was pretty obvious, especially with Inform’s natural language bent and all. The below simply tells what stage of the puzzle you’re at and groups the rook-fleeing squares into different groups. For instance, the rook moving to g4 is the same as moving to h4. I wanted to weight where the enemy rook eventually fled in order to let people concentrate on achievements. This was more exhausting than I thought, and it was probably one factor that contributed to the lost time. The final code was simple, but it was hard to break it down from the whole mess of truth states, and if I’d planned better, I’d have saved a lot of energy.

game-state is a kind of value. game-states are the-beginning, need-kb3, rook-forks-kq and rook-doomed.

fleestate is a kind of value. the fleestates are unreachable, a-guarding, a-allowing, skewer-allow, sucker-sacrificing, useless-sacrificing, spite-checking.

I was, however, able to hit a lot of trivial-seeming test cases such as option setting that–couldn’t break, right?

One thing I repeatedly forget to do is to look into those small test cases that seem trivial–I did a lot better this time, but they still leaked, and I think they can often be a catalyst to get back to work. If they pass, hooray, I did something right, and if not, well, hooray, I caught something bad. I was glad I did once the testing transcripts came in. I just wish I’d spent more time poking at the various “wrong” endings. I was a bit worried that they would bleed together and seem the same, and of course they were at first, because it was a first draft. But finding narratives for the first draft and refining them was surprisingly fun. I wanted them to explain things without being too technical, and they were, in fact, fun to figure out. The explanations or silly stories seemed to follow naturally. I was kicking myself when I thought of them during the comp, when you couldn’t add content, but it was a huge motivation to patch these things quicker next time.

Shop talk for GitHub early bug that took a week to fix

I soon saw that tracking all the ways the rook could flee would be a problem. There were 14 squares to flee to, and if they were at random, it’d maybe take forever to hit one to get a specific accomplishment. So I had to group flee-squares into types, then have a list of types to cycle through. No problem. There were six.

So I had a good chunk of code done for the initial release, and I figured I’d missed some weird stuff. What I hadn’t considered was a sucker punch that gets me every time. I am pretty sure I tested all the simple features, but one slipped by in a rush to just code a bit more. In this case, testers suggested it’d be helpful to label the rows and columns, which seemed easy compared to some other last-minute fixes, but I wound up counting from 1 to 8 instead of 8 to 1. I’m grateful to the people who pointed this out right away!

But that wasn’t all. I got a bunch of GitHub bug reports from someone I’d never heard of, and I was glad I’d gotten such publicity! Yes, even when I was left with a tricky one. It was, quite simply, that you could not get all the endings. And they’d fingered the line that needed to be added as well.

The kicker was, it was in code that made it so you didn’t have to wait too long to see a different sort of square for the enemy rook to flee to. I’d already grouped the flee-squares by type to help get an achievement, but I’d had the bright idea to 1) make the first pass through a generic flee with no tricks and 2) then add the squares that gave unique bad endings to find and 3) delete them once you found the endings.

The only problem was that I forgot to add a square in 2). And my tests didn’t catch that! They passed the case if the rook ever fled to a certain square, but the rook never got there, and I couldn’t test that. So during the comp I fixed the obvious bugs before narrowing down my perspective to that. It took time to write test commands to say, okay, let’s have the rook flee in exactly this order. I took a week. I’m pleased with this, though I’m less pleased with how I waited to roll up a few other low-risk, high-reward bug fixes before then.

Shop talk for a late GitHub bug

The TLDR here is that it’s never too late to give really good feedback, or even any at all. When @bkirwin stepped up to the plate and found a semi-embarrassing bug running YWGHB through Folly, I was glad to have it.

As a judge I know how it is to report stuff late and wish I could’ve told the author earlier, but but in the case of his bug, I was glad to have it reported to make the final version of the game much nicer. The bug itself seemed like a one-line fix. But I needed to test it. When I did, a new weird seemingly unrelated one popped up. As it turns out, that bug was due to a problem with the move tracker. Since the z-machine has 16-bit numbers, I figured you could add 10000 for a position if Black was to move. Or your king is on b3, you add 100 * 23. If the enemy rook is on c5, add 35 (c=3, etc.) But the problem was, in the special late-game cases, I forgot to flip who was to move, so I got a false “white to move” flagging a repetition. This caused a bug that triggered the repetition of moves achievement–but only if the black rook fled to d4, which had a 1-in-5 chance of happening at any test run! So I wouldn’t have found this if Ben hadn’t reported the bug and I hadn’t written the buggy code, but I also wouldn’t have found it without the test cases I took the time to write out. While I’d have found it testing post-comp fixes eventually, I’m pleased my process paid off. It definitely motivates me to slip in those seemingly trivial test cases earlier.

Finally, a non-text adventure flex

Of course you can never be sure about cause and effect. But after doing the major part of coding for YWGHB a couple weeks before ParserComp, I went on and boosted my maximum rating on chess to 2050 or so. Then once ParserComp was entered, I sat down and tried again several times. On July 22nd, I hit 2100. This was my goal for 2022. On the one hand, it’s only a number. On the other hand, I think I did a lot of things right – and I think writing something like YWGHB reminded me of the fun of exploration I had when I was making big improvements, and how I wanted to find that instead of mindlessly playing games.

Just writing YWGHB made me realize how I’d grown as a programmer, too, even if I still have a lot of ways to grow. And there are parallels. The most obvious one is that you can do a lot you want to as a programmer, even if you make a lot of mistakes, and you need to remember that. And you can get to 2100 while making big mistakes. Of course, you have to do a lot right. To play your best chess, there’s an optimal amount of attention you need to give to your mistakes. Too much, or too little, and you miss different sorts of good moves. You can wind up just moving pieces. And being able to pull back from that and reevaluate and refocus is important. Similarly for programming. It’s a delicate dance not to burn out. Whether the creative ideas are any good is another matter.

Some more chess problems in the spirit of the game, for fun, I hope

Synochronicity is a funny thing. The last few days of ParserComp, during my daily free Puzzle Rush tries on, I got not one but TWO puzzles related to the winning theme in YWGHB. I think they’ll be interesting for you–again, without flexing too much, these puzzles will be hard without seeing YWGHB, but if you know its secret, you may be able to guess what to do here. I’m going to skip over the details especially for the second one.

White is going up the board here.

  1. Nd4+ Kb6

  2. e7+ Kc7

  3. e8=N+! forks the king and knight and wins. e8=Q+? loses to Ra6 checkmate.

White is again going up the board.

  1. Rxd7+! Ke6 (taking the rook allows a knight fork. Moving the rook to the c-file allows Rc7+.

  2. f7 Qa2+

Black is down too much material and has to keep checking.

3-5. Ke1 Qb1+ Kf2 Qc2+ Kg3 Qd1

Black has a mate threat! But …

  1. f8=N+, checkmate

Finally, if you enjoyed YWGHB, there are a bunch of puzzles here that you may or may not get. But I think you will enjoy them either way. I think these can be enjoyed regardless of chess strength or experience.

9 Amazing Underpromotions -
The Rarest Chess Move: Underpromoting To A Bishop! -
Practical underpromotion


Have you looked into something like Semantic Versioning? This is just off the top of my head, but what about something like this?

Given a version number MAJOR.MINOR.PATCH, increment the:

  1. MAJOR version when you add or remove features
  2. MINOR version when you change features
  3. PATCH version when you fix features

GitHub is great for this! Their issue templates are excellent for shepherding user feedback. Have you looked into this yet?

I definitely do this, too!

This is just a humble question, but do you think that an in-game menu-based way to promote a pawn would have been too strong of a hint? My thinking here is that if I had to cycle through the pieces to get to a queen, that this forced exposure could have triggered an insight.

Anyways, thanks for sharing this excellent postmortem! As I’ve said before, I always love reading about an authoring process.

And keep dancing that good dance!

1 Like

Honestly, it had never occurred to me that it might be too late to report the bug, but I can totally understand the psychology of it! Glad this one was useful.

(Normally I’ll only go out of my way to report game-breaking bugs, and while this one was a bit marginal, it seemed a little extra important since it was an accessibility feature. Plus the repo link was right there on itch!)

1 Like

I think it was great you went ahead and did things. I guess I fumbled my point a bit: people worrying they might be too late, aren’t, whether it’s with a transcript or bug report or (potentially slightly rushed) review.

The absolute worst case would be getting a bug an hour before the contest wraps up, and that’s still quite useful to me. Because it could still helps motivate me to do a quick post-comp release, as I know something matters enough to a player to, well, report it on GitHub. I do tend to get stuck in interesting but low-impact bugs!

So maybe I can mention that explicitly on the game page.

And in your case I even had a couple days to fix the bug your report uncovered! So I’m glad I provided the link.

I think there’s also trepidation over reporting a bug as we worry “what if it gets ignored” or “is it more nuisance than it’s worth” or whatever, but the more bugs I report, the more I’m comfortable with the author not emotionally attached enough to any bugs I report to be upset if an author marks it not planned.

So keep reporting those bugs–it’s handy!

1 Like

Apologies in advance for the little side track…

Most of the software industry uses this (or something very similar) and a 4th number, which is the build number for internal builds prior to any public release.

The trouble with this is that authoring tools like Inform 6 & 7, Dialog, ZIL, TADS and Adrift only have a single release number, because this is imposed by the compiled file format. You can certainly use semantic versioning in your source code, but every time you do a new public release, you have to increment the release number, which is effectively the major version and the others are ignored.

Has anyone ever given any thought to what’s going to happen when we get to 2080 (or thereabouts) and the two-digit year in the serial number becomes ambiguous? Does it mean 1980 or 2080? For that reason, I always include a copyright statement in my games. A properly formatted copyright statement always has the year of release.

1 Like

Quite the optimist there, just assuming your games, the IF scene, or people in general will still be around.

Maybe our AI overlords will be stumped by this ambiguity. Maybe its inability to differentiate between ZORK and the 2084 IFComp winner will be Skynet’s downfall!


Since all of those formats have save files that aren’t portable between releases, you could argue that any change at all necessitates a major version bump in the strict SemVer sense, since every change is non-backwards-compatible with previous save files. In that sense, all the formats you mention are accidentally technically correct.

1 Like

Good point.

1 Like

Well, Y2K wasn’t much of a disaster at all, so that’s a good sign. We’ve still got 60 years to think of things, amirite?

But seriously, yeah, this is something I noticed way-back-when in 2010 and said “nah, there’s no real reason to worry, we’re not doing heavy software development, I can’t imagine differentiating big updates from small ones.”

Now though there’s so much information we can track, and it’s easier to track and worth tracking, so stuff like point releases that didn’t seem to matter in 1995 – well, it’s not a fatal flaw, but now the subject’s been brought back up again, I can’t shove the genie back in the bottle in my own mind…

@Warrigal I didn’t find it a sidetrack at all–apparently I had a message drafted that I forgot about that discussed this very thing! I don’t want to tack too much on to my previous message, so I’ll add this. Sometimes discussion of one thing opens up an important general discussion, like here.

the release number is 1000. [ thousands digit = major release stuff, lots of changes. hundreds = new features, tens = minor features/bugs, ones = trivial stuff.] This would keep z-machine (8-bit) values for the release number under the signed int ceiling of 32767. The version deltas can be fungible and I don’t need hard and fast rules, but yeah, I really need to just get on with it. It’s highly unlikely I’ll ever have the equivalent of, say, 1.11.3, so integers work well enough.

The main things stopping me doing it is an idee fixe of “No! The first release can’t be 1000!” However, it really seems this should be the way in the future.

First, apologies for not responding earlier–I got this thread tangled up with the “maintaining momentum” thread, and somehow my drafted response didn’t appear until after my most recent post. But I thought I posted it!

This nudge finally pushed me into writing an issue template! Now that I’ve been through the process, it’ll be much easier in the future. So that is a big boost.

That is a good question and one I considered, and I recognized I hadn’t hit a happy medium in-game.

I had a vague idea I’d like to give the player the option to page through things first. One of the things I wanted to do was to give progressive hints each time you got the “almost” ending. So the player would have the chance to deliberately solve things. Then when you failed, you’d get clues like

You did as much as you could and brought all the firepower possible. What more can you do, really? That’s the right question to ask, right?

She had to be the right person for the job. Right?

Okay, there was that case long ago where a knight could hit squares she couldn’t, but that doesn’t apply here, does it?

You remember her worrying she was scared of her power. Perhaps there was a chance for overreach, Odd!

And so forth, where it’s clued at random. I the player to get the solution with as few hints as possible with, well, teasing and leading them too much.

Thanks. It’s tough to keep my steps fresh, so to speak, and your suggestions helped me do so.

We need to keep asking these questions and trying new things. And sometimes it feels like I’m saying the same old things in my postmortem, but it’s great when someone chips in with things I hadn’t thought of, whether on others’ postmortems or my own.

1 Like

You’re very welcome. And the new template looks great! :smile:

Yeah, that’s a good approach. I think that would be a fun way to stay on track. But also, this might be getting into differentiated a design based upon a target audience. That’s when design tends to become overwrought with trade-offs.

Yes, please! In my day job, I often have to discuss and consider “people, process, and technology”. These postmortems seem like the perfect place to talk about all three, so I think they’re great.

1 Like