For my own edification

i’m still unclear on when i need to use (exhaust) and when i can just use a multiquery. I’ve just sort of shambled on, trying a multiquery first and then adding the (exhaust) in the cases where it doesn’t work.

these are from the manual and seem functionally identical to me.

(program entry point)
     *(fruit $Obj) 
     $Obj is a fruit.
     (colour $Obj)
     We found a fruit that is also a colour!

(program entry point)
     (exhaust) {
          *($X is one of [#door #foot #apple #pencil])
          Checking (the $X).
          (fruit $X)
          Yes, it's a fruit!

the manual seems to imply that the (exhaust) version is simply more thorough? but isn’t the point of a multiquery to try each branch of the search tree anyway?

1 Like

Multiquery alone will backtrack on fail only, so the first example will find the first fruit that also happens to be a color and then move on. Add (color #banana) to test it, it won’t find the #banana.
Multiquery in an (exhaust) block will backtrack for everything; it behaves as if an invisible (fail) is appended to the end of the block. In fact the manual says,

(exhaust) statement


{ statement (fail) (or) }

are equivalent.
Test it by putting all four lines in your example in an (exhaust) block and also add (color #banana). Now it will find both the orange and the banana.


In other words, use a multi-query if you only care about finding a solution, and use (exhaust) if you want to iterate over every solution.

If you’re used to Inform, sometimes you want “let the item be a random thing held by the player”, and sometimes you want “repeat with the item running through things held by the player”.


got it, thanks. that’s much easier for me to wrap my brain around.

1 Like

My favorite rule from the library is (split $ anywhere into $ and $). See if you can figure out what it does and how it does that. One needs to grok choice points, backtracking, and multi query concepts(and unification of partial lists added for good measure) to understand that rule completely.

(split [$First $Second | $Tail] anywhere into [$First] and [$Second | $Tail])
(split [$First | $More] anywhere into [$First | $Left] and $Right)
	*(split $More anywhere into $Left and $Right)

If following that rule causes a headache, here is one that uses the same concepts(sans partial lists), but might be easier to understand what and how:

(repeat ($N > 0))
(repeat $N)
	($N minus 1 into $Remaining)
	*(repeat $Remaining)
1 Like

Shouldn’t that second one have (repeat 0) as its base case? It looks like the first rule will always succeed so the second one won’t get run.

1 Like

Part of the puzzle is figuring out the way those queries are used. Should I just answer your question or would you like to figure it out yourself?

Oh, I see. You’re supposed to call this with an (exhaust), not with a normal multi-query, so that the first rule succeeding doesn’t cut it off? That makes more sense!

1 Like

Correct. Let’s recap: *(repeat $N) generates upto N choice points.

Edit: Added missing *.

1 Like

Here’s another fun bit: the only time I’ve ever used one of the (collect) variants without a multi-query!

(maximum score 12)
(current score $Score)
	(accumulate 1)
		~(player is imprisoned) (or)
		(introduction finished) (or)
		(#frontdoor is unlocked) (or)
		(#attic-w is visited) (or)
		(#cellardoor is handled) (or)
		(#suitedoor is unlocked) (or)
		(gardener is exculpated) (or)
		(nephew is exculpated) (or)
		(friend is exculpated) (or)
		(companion is exculpated) (or)
		(REDACTED) (or)
	(into $Score)

That’s cool, modern scoring.

Here’s one as old school as it gets from Advent430 (spoilers):

Advent 2.5 Scoring (no save penalty)
(score plus 25)		(deep visited)
(score plus $S)		%% treasures
	*(treasure $Obj)
	($Obj is found)
		($Obj is #in #wellhouse)
		($Obj is in order)
		($Obj score $S)
		(treasure found score $S)
(score plus 30)		(death count 0)
(score plus 20)		(death count 1)
(score plus 10)		(death count 2)
(score plus 4)		(endgame is reached)
(score plus 25)		(game is closing)
(score plus $S)		(game bonus $S)
(score plus 1)		(#magazine is #in #wittsend)
(score plus 2)		%% Roundup

%% score penalties
(score minus 5)		(novice mode)
(score minus 10)	(#oyster is read)
(score minus $TurnPenalty)
	(turn threshold penalty $TurnPenalty)
(score minus $S)
	*($HintObject hint is used)
	(hint penalty $HintObject $S)

(update score)
	(accumulate $N)
		*(score plus $N)
	(into $Plus)
	(accumulate $S)
		*(score minus $S)
	(into $Penalty)
	(if) ($Plus minus $Penalty into $Score) (then)
	(else) ($Score = 0)
	(now) (current score $Score)
1 Like