Bubbling Beaker Awards (Award #23, Apr 19 2024)

Oh yeah, and an extension version is coming as soon as I’ve stress-tested it a bit more!

1 Like

This week’s prize, Bubbling Beaker Award® #4, is presented to @evc003 (Eric Conrad), who hasn’t visited the forum in some time. His award-winning post has a practical bent: It demonstrates a method for setting up asymmetrical map connections at compile-time using the relatively-undocumented mapping <direction> relations. It may not sound like much, but it drew a comment of “Ooh, nice.” from none other than zarf. (Note that there may be tweaks needed in 10.1.2 or future releases. See discussion at https://intfiction.org/t/mapped-above-mapped-below)

7 Likes

It occurs to me that, although the compiler source code was a black box at the time this week’s award-winner was posted, now the source code is open for inspection.

I still don’t know my way around it very well, but I know that @Zed and @Draconis have spent some time reviewing it. Can either of you two shed some light on the dividing line between “simple” and “complex” declarations on which evc003’s technique depends? That would be a good supplement to the discussion on the original post itself.

4 Likes

Some time ago I did some experimenting and observed the following:

Making one-way connections

As noted above, phrases including ‘nowhere’ can be used to override the compiler’s innate tendency to assume two-way connections.

Also, an explicitly stated alternate destination for an assumed reverse mapping direction will take precedence over the assumption:
For example after: The Lawn is east of the Potting shed and west of the Herbaceous Border. East is the Lake. the Lawn remains west of the Herbaceous Border, but there is no corresponding eastward connection- east of the Lawn is the Lake.

Furthermore, the compiler only creates two-way connections for what the documentation calls ‘simple sentences’ Unfortunately, this cryptic comment leaves it a little vague where the boundary between ‘complicated’ and ‘simple’ lies. The example given in the documentation is:
The Attic is a dark room above the Parlour. rather than the simple The Attic is above the Parlour, but actually even The Attic is a room above the Parlour appears ‘complicated’ enough to create a one-way connection. A dark room above the Parlour called the Attic contains a brass lamp. also creates a one-way connection.

(Note that the Parlour must have been already declared for the compiler to accept this- otherwise the following complaint is made:

The sentence ‘The Attic is a room above the Parlour’ appears to say two things are the same - I am reading ‘Attic’ and ‘room above the Parlour’ as two different things

Also, The Attic is a room above or The Attic is a room above it both provoke a similar complaint even with prior declaration of the Parlour)

Conversely, the apparently equally complicated The Parlour is a room. Above is a room called the Attic or Above the Parlour is a room called the Attic or A dark room called the Attic is above the Parlour or even Above is a dark room called the Attic all lead to a two-way connection.

The simplest approach to creating explicit one way connections is therefore probably to use the exact forms:

The <name-of-a-new room> is a <optional-list-of-adjectives> room <direction> of/from/-- <name-of-a-previously-declared-room> OR

A <optional-list-of-adjectives> room <direction> of/from/-- <name-of-a-previously-declared-room> called <name-of-a-new-room> contains <list-of-objects>

remembered through the archetypes:

  'The Attic is a dark room above the Parlour.' OR
  'A dark room above the Parlour called the Attic contains a brass lamp.'
4 Likes

This week’s prize, Bubbling Beaker Award® #5 is presented to @Zed, the mad scientist par excellence. His award-winning post demonstrates use of an anonymous relation set up as a property of an enumerated value as a method of handling color mixing rules. (NOTE: Due to a bug in Inform 6M62, his code causes an interpreter crash on startup if compiled for Glulx using 6M62. It will work if compiled to Z-Machine using 6M62, and works for either VM using 10.1 because the bug has been fixed.)

What makes this invention special is that it is a working implementation that approximates some of the functionality of ternary relations, a feature that many Inform users have wished for in the past. For this reason, by vote of his fellow mad scientists, this is the first award to be granted the coveted distinction of being Peer Revued™.

Congratulations, Zed! You are formally invited for show-and-tell.

12 Likes

Thank you. I’m tickled pink to have been Peer Revued™. But I must confess that I have misgivings that this one was mad enough! Like I said there, something like this technique may be what’s “meant to” be idiomatic Inform!

WI 4.16:

Relations are really the central organising idea of Inform [emphasis added]

WI 13.14-13.15 and WI 22.1 make clear that the relation of K to L syntax is a kind-of-value constructor, and one can have values of that kind, and variables that hold values of that kind. And, so, why not properties?

Only problem is: it didn’t actually work. Or so I thought. I only compile to Z-code on rare occasion to test something in particular. Until you told me it worked on the Z-machine with 9.3/6M62, Otis, I had no idea.

It’s easy to point to when I found out that relations as properties worked in 10.1 (on glulx) 'cause I posted about it (in one of the several threads over the years in which someone was yearning for a ternary relation).

(A different example of using a constructed KoV as a property can be seen in an answer to Dynamic relations: what’s the idom? where I made an enumerated kind of value with a thing-based rulebook as a property.)

6 Likes

I guess that link answers the question I posed in the original award-winning thread ‘Can forms of relation other than various to various be usefully used as properties?’ :wink:

1 Like

Well, at least partially. I can’t (so far) find a working syntax that explicitly creates for example a various-to-one color to color relation as a property.

Two things:

  1. The fact that it works on Z-Machine for 6M62 is pure luck – an oddity of some low-level logic being right enough for the wrong reasons due to an integer overflow that results in a fortuitous value. (Details available by PM, if you care, but the root cause is already fixed in 10.1.)

  2. As far as whether it was “mad enough,” I can reveal only that a comment by one of the revue-ing peers deemed it “insane genius,” so I think you’re on firm ground.

I had missed that you did basically the same thing 16 months ago. Better late than never!

3 Likes

As CrocMiam pointed out on the thread from 16 months ago, it’s also possible to approximate ternary relations via a table. But as Dannii pointed out in response, the main drawback of a table is that it has to be sized at compile time and cannot be expanded at run time, so the author must attend to this detail. By contrast, with Zed’s technique, the various relations of colors to colors will all be sized correctly by the compiler.

With a table approach, the author can set up initial relations which will be compiled into the starting state of the table. With Zed’s approach, the author must write a rule to initialize the relations at startup, because all of the anonymous relations begin empty. (It’s about the same amount of typing either way.)

With either approach, the state of pseudo-ternary relations can be modified at run time.

With a table approach, the binding between elements of the tuple is via sharing a row in the table. With Zed’s approach, binding is in two stages: A given second color is bound to the mix result color via a relation assertion, and the first color is bound to a set of relation assertions via the link between that color value and its relation-holding property.

With either approach, phrases must be used to both set and get information from the pseudo-relation.

Overall, the two approaches seem comparable:

Comparison of methods
Color is a kind of value.

The colors are brown, red, orange, yellow, blue, green, indigo, violet.

[Zed's technique]

A color has a relation of a color to a color called the mixmaster.

To (c1 - color) mixed with (c2 - color) is (c3 - color):
  now the mixmaster of c1 relates c2 to c3;
  now the mixmaster of c2 relates c1 to c3;

[if no relation, decides on default value of color]
To decide what color is (c1 - color) mixed with (c2 - color):
  if c2 relates to a color by the mixmaster of c1, decide on the color that c2 relates to by the mixmaster of c1;

When play begins (this is the initialize color mixing relations rule):
	[these lines cause crash for Glulx in 6M62]
	red mixed with blue is violet;
	red mixed with orange is red;

[Table-based implementation]

Table of Color Mixing
color1 (color)	color2 (color)	result (color)
red	blue	violet
red	orange	red
with 50 blank rows.

[if no row, decides on default value of color]
To decide which color is the result of mixing (c1 - color) with (c2 - color):
	repeat through the Table of Color Mixing:
		if color1 entry is c1 and color2 entry is c2, decide on result entry;
		if color1 entry is c2 and color2 entry is c1, decide on result entry;


When play begins (this is the compare techniques rule):
	say "red + blue = [red mixed with blue].";
	say "red & blue = [result of mixing red with blue].";
	say "blue + red = [blue mixed with red].";
	say "blue & red = [result of mixing blue with red].";
	say "red + orange = [red mixed with orange].";
	say "red & orange = [result of mixing red with orange].";
	say "orange + green = [orange mixed with green].";
	say "orange & green = [result of mixing orange with green].";

… but Zed’s is definitely the “mad science” version.

4 Likes

In celebration of International LISP Day (well, not really- that’s May 4th) - today everything’s a list!

Here’s a method to do the same thing with a list of lists of colours…

Which enables the 3-tuples, e.g. {red,yellow,orange}
(i) to be asserted in initial setup without an initialisation routine (unlike a relation property)
(ii) to be modified at run-time
(iii) to be added to indefinitely at run-time subject to the number of colour-combinations available (unlike a table)

This heavily commented example sets up an intial list of tuples, including some inappropriate duplicates, then manipulates the list through user input.

1 Like

Musing a little more about the different applications of these methods:
(i) Relations allow some nice built-in phrases such as ‘list of colours to which yellow relates by the mixmaster of blue’
(ii) Tables allow the widest range of different kinds to make up the tuple from (three columns of different kinds), while if using a list of lists all the elements of the tuple must be of exactly the same kind. It would be possible, if a little awkward, to maintain three corresponding lists of different kinds where a tuple is made up of entry x of list 1, entry x of list 2 and entry x of list 3.

1 Like

This week’s prize, Bubbling Beaker Award® #6 is presented to @BadParser, who is not a mad scientist but seems to take a scientific approach to Inform. BadParser’s award-winning post demonstrates how to skirt the I7 compiler’s refusal to accept an action that applies to two kinds of value (and zero objects) by deploying a general parsing routine modeled after the “third noun” example from the DM4.

BadParser, congratulations! You are invited to share details about inspiration, conception and execution here, if so inclined.

6 Likes

I’ll note that you’re a mad scientist if you say you’re a mad scientist, so maybe BP is a :test_tube:mad scientist :atom_symbol: and they just haven’t told us yet!

4 Likes

I am truly humbled and not sure how worthy this post is for such an honor, but would like to respond by saying: my inspiration was @raragi’s question (in post #6 of that original thread); my conception was to search this forum; my execution was to slightly modify the code I found.

However, to make my contribution a little more “Beaker-worthy” and, in the spirit of fellowship with past award winners, I have decided to extend the code and combine it with last week’s winner, Zed Lopez’s BBA #5.

This is also an attempt to finally answer Daniel’s question to @zarf from December, 2013:

This implementation is not really complete as a general solution, which is left up to the reader. I may not be a mad scientist, but I do like to drive scientists mad.


"Action with Two Values - Bubbling Beaker Awards Edition" by Mike Tarbert

[NB: does not compile at all in version 6M62; crashes as written here in 10.1.0 (in Borogove app); works as written in 10.1.2. In order to work properly in general using 10.1.2 (or at all in Borogove's 10.1.0) requires voodoo and/or other black magic unbeknownst to this author (or just trace experimentation) to determine the second argument for ParseToken called within SecondColor.]

Lab is a room. 

Section 1 - Zed Lopez's Bubbling Beaker Award Winner #5

color is a kind of value.

the colors are brown, red, orange, yellow, blue, green, indigo, violet.

a color has a relation of a color to a color called the mixmaster.

to (c1 - color) mixed with (c2 - color) is (c3 - color):
  now the mixmaster of c1 relates c2 to c3;
  now the mixmaster of c2 relates c1 to c3;

to decide what color is (c1 - color) mixed with (c2 - color):
  if c2 relates to a color by the mixmaster of c1, decide on the color that c2 relates to by the mixmaster of c1;

when play begins (this is the set up colors still on the TODO list rule):
	red mixed with blue is violet;
	red mixed with yellow is orange;
	yellow mixed with blue is green;
	

Section 2 - BP's BBAW #6 Modified for Use with a New KOV

Include (-
Global second_color;
-).

The second color understood is a color that varies.
The second color understood variable translates into I6 as "second_color". [represented as a number in i6]

Include(-
[ SecondColor x firstnum;
	firstnum = parsed_number; ! saves the first parsed number locally
	x = ParseToken(GPR_TT, 212542); ! <- I don't know how to calculate this GPR number
	if (x == GPR_FAIL or GPR_REPARSE) return x;
	second_color = parsed_number; ! saves the second parsed number into a global
	parsed_number = firstnum; ! returns the first number to its usual global spot
	return GPR_PREPOSITION;
];
-).

The Understand token second-color translates into I6 as "SecondColor".

Section 3 - Mixing It Up

Mixing it with is an action applying to one color.

Understand "mix [color] with [second-color]" as mixing it with.

Report mixing it with:
	say "Mixing [color understood] with [second color understood] (in this implementation at least) yields: [color understood mixed with second color understood].".
	
test me with "mix red with blue / mix blue with red / mix blue with yellow / mix violet with yellow / mix blue with blue / mix blue with vermilion".

I would also like to thank the academy and to tell all you youngsters out there that you never know what a little pouting in the Lounge can get you.

3 Likes

I don’t know how to find this number programmatically (it is the address of the General Parsing Routine (GPR) represented by the [color] token, and as far as I know, inaccessible to I7) but by creating a replica GPR function, we can use the name of that as the argument instead, which is a litlle less ‘voodoo’ than finding and inserting a highly-unstable magic number:

1 Like

Here is a method to find the address of the GPR routine represented by a token such as [color], by pulling it from the grammar of a verb word that in the first grammar line in its grammar table includes [color] as the first token after the verb word.

In this example, after Understand "mix [color] with [second-color]" as mixing it with., the grammar token [color] is indeed the first token after ‘mix’ in a single grammar line, so…

First when play begins: set color token.

To set color token: (- default_color_token=GetGPRRoutine('mix'); -).

Include (- 
Global default_color_token;
[ GetGPRRoutine verb_wd i line_address first_ttype first_tdata pcount;
! call this with a verb (dictionary word) and if the 1st token of the 1st grammar line for that verb is a GPR routine, the function returns its address

	i = DictionaryWordToVerbNum(verb_wd);

	line_address = ((#grammar_table)-->(i+1))+4;     ! look up start of grammar table for this verb, skip 1 byte for <no of grammar lines>; 3 for <##action name + flags byte> of first line, to reach first token
	first_ttype=line_address->0 & $0F;                         ! token type (we want GPR_TT)
	first_tdata=(line_address+1)-->0;                           ! token data
	
	if (first_ttype==GPR_TT) return first_tdata;    ! first_tdata is address of the GPR routine
	print "###Error - GetGPRRoutine() tried to find a GPR routine address from the first grammar token of the verb'", (address) verb_wd, "' but that token wasn't a GPR routine.^^";
!	PrintGrammar(verb_wd);
	rfalse;
];
-).

which means when play begins, the I6 global default_color_token now contains the address of the GPR routine represented by [color], and we can use that in our second colour token routine, thus:

[ second_color_token x firstnum;
	firstnum = parsed_number; ! saves the first parsed number locally
	x = ParseToken(GPR_TT, default_color_token); ! <- global assigned when play begins with the address of the [color] token GPR routine
	if (x == GPR_FAIL or GPR_REPARSE) return x;
	second_color = parsed_number; ! saves the second parsed number into a global
	parsed_number = firstnum; ! returns the first number to its usual global spot
	return GPR_PREPOSITION;
];
5 Likes

Addendum:
Although it’s possible to modify this code to extract the address of a GPR token routine that is not the first in a grammar line, or is found in a grammar line that is not the first line in a verb’s grammar table (in cases where the verb has more than one grammar line), it’s simpler to stick to the code as written and if desired or needed, invent a dummy verb/action declared with just one line of suitable grammar to extract the [color] token address from:

Color-extracting is an action applying to one color.
Understand "c-ext [color]" as color-extracting.

To set color token: (- default_color_token=GetGPRRoutine('c-ext'); -).

If you want to see the grammar table for a verb, the little-known debugging command showverb c-ext, for example, will do so:

>showverb take
Verb ‘carry’ ‘hold’ ‘take’
* ‘inventory’ → Inv
* multi → Take
* ‘off’ noun → Disrobe
* noun ‘off’ → Disrobe
* multiinside ‘from’ noun → Remove
* multiinside ‘off’ noun → Remove

>showverb c-ext
Verb ‘c-ext’
* Routine(212794) → A_color_extracting

Here, * stands in for <(one of) the command word(s)>, so for example
* inventory matches ‘take inventory’, invoking the ##Inv action;
* multiinside 'from' noun matches ‘take <one or more things inside the something mentioned later> from <something>’, invoking the ##Remove action;

A_color_extracting is the internal I6 name that the compiler has invented for our new dummy action, which could be elsewhere referred to in I6 code as ##A_color_extracting.

212794 is the address of the [color] GPR routine for this particular compilation (which the previously-posted code extracts using GetGPRRoutine(‘c-ext’) ), and corresponds to the ‘magic number’ found here x = ParseToken(GPR_TT, 212542) in the SecondColor token routine of @BadParser 's original post. As demonstrated here, that ‘magic number’ is likely to change with changes to the source code and/or compiler, so can’t be relied on over time. Hence the usefulness of a more general solution as presented here and previously.

NB study of the grammar for take illustrates some interesting points, such that one can take inventory by typing ‘carry inventory’ or remove clothing (the disrobe action) by typing, for example, ‘hold coat off’.

3 Likes

This week’s prize, Bubbling Beaker Award® #7 is presented to @drpeterbatesuk, who is the kind of quiet, diligent mad scientist who not only builds fabulous doomsday devices but also releases high-quality spec sheets and do-it-yourself guides for the general public. His award-winning post demonstrates how to smuggle I6 code deemed illegal in Inform 10.1.2 past the compiler’s inspection for contraband instructions. (Think of it as a “jailbreak” for 10.1.2.)

By virtue of this invention’s particular combination of deviousness and utility, the award is also granted the distinction of being Peer Revued™.

Congratulations, drpeterbatesuk! This marks your second award. I look forward to the discussion!

9 Likes

I am most humbled by this unexpected Peer Revued™ accolade.

I will submit further context for and useful examples of this ‘discovery’ over the weekend, assuming the agents of sensible and non-deprecated coding don’t reach me first…

4 Likes