Friday, June 3, 2016

More on Zarf's testing scripts: automating yes-no, and debugging

One problem with Zarf's scripts is that they hit a loop if there's a pause without a > for a cursor. This isn't unique to them. The IDE can trip up, too. Or maybe you just don't want to remember when to say yes or no.

This is the sort of Inform 6 dabbling that I think helps you understand what's going on without getting overwhelmed. Below is code cut from the Trivial Niceties extension I use for testing.

volume yes-no substitutes

[this lets the programmer skip over yes/no decides]

debug-state is a truth state that varies.

to decide whether the player yes-consents:
    (- YesOrNoExt(1) -).

to decide whether the player no-consents:
    (- YesOrNoExt(0) -).
[ YesOrNoExt yn;
    if ( (+ debug-state +) == 1)
        return yn;
    return YesOrNo();

Let's go over what this does. First, YesOrNo() is the standard Inform function for getting a yes or no from the player and moving on. If you want to see its details, it's in Parser.i6t in the reserved folder. It's worth a look to see that it's not terribly intimidating.

YesOrNo is pretty straightforward, and you can change the 'Please answer yes or no' message if you want. In fact, I did so in Threediopolis. You can search for EdBlab here in its source, though I may not detail it here. You can also see how to insert Inform 6 source into Inform 7 code. Not that I did a brilliant job of it, but yeah. It's not THAT esoteric if you want to change the default message or cut the player off after X tries.

So what do yes-consent and no-consent mean? Well, yes-consents goes to YesOrNoExt with a parameter of 1. If debug-state is on, it returns 1. If it isn't, it returns YesOrNo--which is 1 for yes and 0 for no.

I also defined a function called switch-consents which means the user can set a flag to decide whether or not to make yes or no the default. And I suppose the yes-no automation variable could be separated from debug-state.

Because I've actually defined debug-state earlier in the extension, and it's useful for other things, such as

to d (x - text):
    if debug-state is true:
        say "DEBUG: [x][line break]";

This is handy for, well, printing debug values when something doesn't quite work, or just making sure you have the right row in a table when the text is constantly changeing. It also is a great way to be sure that the end-player won't see these commands. After a while, typing d has become second nature for me when I've wanted to include diagnostic stuff. As long as we have this sort of section:

chapter debug setting - not for release

when play begins:
    now debug-state is true;

dbting is an action out of world.

understand the command "dbt" as something new.

understand "dbt" as dbting.

carry out dbting:
    now debug-state is whether or not debug-state is false;
    say "Now debug-state is [debug-state].";
    the rule succeeds;
We're okay. The point is that, well, the programmer can toggle debug-state, but the user never can.

This is only the surface of debugging, but I think it's a good start, and it helped me worry less about "what if it slips into the game text" or other issues. I've had it so long I've almost forgotten about it, but...a little I6 can go a long way, so it's good not to be scared, and to explore (if you haven't already) the Standard Rules.i7x file or even the Parser.i6t file.

I felt pretty clever making my first I6 edit to make Inform do what I wanted, and once you have a bit more you want to do, you will, too. Just remember to use the "include" syntax instead of editing the core files.

This can similarly be applied to skipping over narrative breaks, which I did so that the "test" commands wouldn't freeze. Emily Short's Basic Screen Effects needs to be included, but once you do, you can replace wait for any key with wait-key-d, or something even shorter, if you want.

include Basic Screen Effects by Emily Short

volume regression test - not for release

when play begins:
    now debug-state is true;

volume regression test stubs

debug-state is a truth state that varies.

to say wait-key:
    if debug-state is false:
        wait for any key;

to wait-key-d:
    if debug-state is false:
        wait for any key;


Wednesday, June 1, 2016

Applying Zarf's automated testing scripts for Inform

Zarf's script is something I wish I'd tried much earlier. It had the added bonus of helping me learn python. As wonderful as the Inform IDE's "test" command is, it has limitations:

* no undo, which is sort of a problem if you get killed
* only one test run, with restarts being awkward
* can't check the content

I've been tinkering with this in my own games, but I think I've found a sub-script that's flexible enough to share with others. Eventually I'd like to do a whole post on using Zarf's framework from Windows, but for now, here's an idea of how to get things rolling if you have the framework up.

It's one of my favorite axes to grind, fixing the "You can't go that way" generic command. It's always meant restriction more than exploration to me, and changing it helps me develop my world, and I look for it in games I test. So here is my Python script:

This needs a text file in the same directory, labeled reg-noway.txt, to start. I ran this test on The Problems Compound, which had a test file at converted from

You can see the general syntax--just list the name of the rooms, and adjust the binary names accordingly. Obviously, this can be more flexible as I build the script, but the main thing is that it will take a text file full of rooms and spit out a bunch of tests. I like this because insta-death movements are neutralized, and the "undo" means you can try a different direction right away.

I also disabled diagonal directions in some games, which means a lot fewer tests need to be run.

But this only does so much. You want a working walkthrough, with the right clues. So I spruced up and just made sure everything worked okay...and it turned out I had a few typos to look through.

Thus my fears of just doing-over work I already did, and not really getting anything, were misplaced. I even tuned up some double-text I should've seen before. I think just the process of sitting down and creating the test cases switches the focus from "I should've tested this, I think" to "I'm going to make sure I tested this." And if I cheated with some GOTO commands, well, I was able to cover the major walkthrough and check off on more than just "you won."

It's already helped me fix a few trivial annoying bugs, because I could just slip in a test and say "OK, does this work? I'd hate for this to break." And so I've gotten a lot of mileage. And I suspect it'll help you, too, but I'll write more later.

Still, the short take-away is: do it on a game you want to re-release. Or a WIP. It provides a lot of security that stuff you fixed will stay fixed. Or you'll know when it broke.

Three chances to sac my queen in chess

I ran across an old chess game from the past. If the ghost of Mitch Hedberg were around, he would say <a href = "">every old chess game I played was from the past. (I used to like that joke. I still like it, but I used to like it, too.)</a>

Anyway. It wasn't a very good game, but it was much more instructive than if I'd done what I was supposed to and won it quickly. Let's look at the start.

1. e4 e5 2. Nf3 d6 3. Bc4 a6 4. d3 (d4 is a good idea, but...I still just sort of got all my pieces out and didn't try anything crazy) h6 5. Nc3 b5 6. Bb3 Bg4

Black hasn't bothered to get his knights out. Now, I remembered Legal's Mate, but I also remembered that if you did it wrong, you just went down a piece and were kind of in big trouble and your coach and teammates probably thought,  gee, what the heck were you doing? (Note: this sort of thing can get in your way with general creativity. There are things to check off on. But it's a legitimate fear, and one that needs to be dealt with.)

The right move, here, is 7. Nxe5! because of Bxd1 Bxf7+ Ke7 Nxd5#. And it's a weird thing, here. Every chess player dreams of sacrificing a queen for a quick win. But I remember talking myself out of it: well, it's not an ORIGINAL sacrifice. Well, I'll feel dumb if I get it wrong. And I don't know when it happened, but at some point, I stopped trying for cool stuff and started grinding out wins. I was still a pretty aggressive player at this point, but...well, not this game.

7. h3 7... Bxf3 8. Qxf3 Qf6 9. Qxf6? Now I still had a big advantage, and Qe2 or Qg3 keeps queens on the board and also still threatens the annoying Nd5. So I am letting him off the hook and letting him get his knight in the game. Even an immediate Nd5 is rather good, but I don't know--I think I was learning about endgames and so tried to steer the game that way.

9... Nxf6 10. Nd5 Nxd5 11. Bxd5 c6 12. Bb3 a5 13. c3 Na6 14. f4 (this is rather good, giving the rook an open file) b4 15. fxe5 dxe5 16. Be3 bxc3 17. bxc3 Rd8 18. O-O-O Ba3+ 19. Kc2 Bc5 20. Bxc5 (this is where d4 really should have been obvious) Nxc5 21. Rhf1 Nxb3 22. axb3 and I felt I could gang up on the a-pawn and win and I did but it took way longer than I should have. The main moves aren't important here--it's that I'm trading down and not giving a weaker opponent a chance to mess up. As if I was running from the position with lots of pieces where I didn't want to face my chicken-ness.

Now I learned a lot from this game, with the endgame small advantages etc. And overall, it was favorable, though being exhausted may've cost me in the next two rounds. I got a bad position in round 2 then lost a tricky (but again instructional) game in round 3. So, yeah, I remember missing my chance. I remember fearing my (kind of sarcastic) chess coach saying "Mi-i-i-ster Schu-u-u-ltz! What took so long?" and also fearing him asking about why I didn't play Nxe5. The thing's something I wished I'd nailed down. But I sort of let my fears trap me. And I didn't really do much after that.

And so I remember this when I think about missing chances or not going with what I know or intimidating myself into going for the safe route without checking off. But the story doesn't end sadly.

I eventually did get my queen sacrifice later. It was actually a sham sacrifice--in other words, I got the material back quickly. But then a month later I had a chance for the real thing, and I took it, and it was glorious. It was a positional sacrifice! The guy next to me said he was glad I had the guts to play it! The end of the game was ridiculous--I pitched a bishop in a winning position, then my opponent pitched a rook. But--I got my queen sacrifice, and it was my own. Let's not worry about the opening play too much.

1. c4 b6 2. Nf3 Bb7 3. g3 Bxf3 4. exf3 c5 5. d4 Nc6 6. d5 Nd4 7. Bg2 Rc8 8. O-O g6 9. Nd2 Nh6 10. Nb3 Nhf5 11. Nxd4 cxd4 12. Bg5 Bg7 g4 Nd6 13. Qxd4 Rg8 14. Bf4 Bg7 15. Be5 Rxc4 16. Bxg7!? Rxd4 17. Bxd4 f5 18. Rfe1 Qa8 (he really should've played Kf7 etc. It looks ugly but it starts to untangle. King safety first, and ...b5 can develop the queen) 19. Bf6 Nc8 20. d6 e6 21. Be5 21... Qd5?! (...b5 and Nb6) 22. f4 and I'm having all the fun.

The sham sacrifice is below. In this game I went up a pawn then my opponent won an exchange for a pawn, but I had two passed queenside pawns, so I won material back.

I remember this when I talk myself out of something good, or if I think I might be about to. It's a good way to check off. You never know when good fortune is going to happen--but it occurs to me I never gave myself a chance to make another queen sac. I just didn't play aggressively enough. Well, there was 1. e4 c5 2. c3 d6 3. d4 cxd4 4. cxd4 Nf6 5. Nc3 Nc6 6. Nf3 Bg4?! 7. e5 Ne5 8. Nxe5! but that doesn't really count (Bb5+ gets it back.) Still, you take what you can. The only reason I'd return to tournament chess is to get one more queen sac. That'd mean playing aggressively, with confidence, and so forth, and not second guessing, but having fun calculating to make sure of things.