Changing a binary state

Ok, so I know how to do what I’m thinking of in a bull-in-a-china-shop kind of way. That is, I can plow through it with a bunch of code, but I’m almost certain there has to be a more elegant way.

If there is, I’m not sure how to phrase it.

This scenario isn’t exactly what I’m hoping to create, but it’s a simplified version that gets the idea across.

Imagine a room with two lamps, devices which can be switched on or off to change their status of “lit” to “unlit.” That’s pretty simple.

But now let’s add one button in the room that when pressed, changes the status of each lamp to the opposite of what it already is (switched on to switched off and vice versa). For what I’m planning to do, I need this to be a button that can be activated with one action (being pressed), not a device like a light switch that can be activated with two distinct actions (switching on or switching off).

The button is the part I’m having trouble with. I can do it, but it looks like this:

Basement is a dark room. 

The red lamp is a device.
The red lamp is in basement.
The red lamp is switched on.
The red lamp is lit.

The blue lamp is a device.
The blue lamp is in basement.
The blue lamp is switched off.
The blue lamp is unlit.

Carry out switching on the red lamp:
	now the red lamp is lit.
	
Carry out switching off the red lamp:
	now the red lamp is unlit.
	
Carry out switching on the blue lamp:
	now the blue lamp is lit.
	
Carry out switching off the blue lamp:
	now the blue lamp is unlit.
	
The green button is in Basement.

Before pushing the green button:
	if the red lamp is switched on:
		if the blue lamp is switched on:
			try silently switching off the red lamp;
			try silently switching off the blue lamp;
			say "click!";
			stop the action;
	if the red lamp is switched off:
		if the blue lamp is switched off:
			try silently switching on the red lamp;
			try silently switching on the blue lamp;
			say "click!";
			stop the action;
	if the red lamp is switched on:
		if the blue lamp is switched off:
			try silently switching off the red lamp;
			try silently switching on the blue lamp;
			say "click!";
			stop the action;
	if the red lamp is switched off:
		if the blue lamp is switched on:
			try silently switching on the red lamp;
			try silently switching off the blue lamp;
			say "click!";
			stop the action;

The only way I can figure out how to do this is to code in every possibility for the two lamps: on/on, off/off, on/off and off/on. That’s not a big deal with two lamps, but what if I’d like for the button to control three or four lamps? Or ten? The number of possibilities gets out of control very fast. It’s not impossible to code, but I can’t help but think there’s a way to say the equivalent of “If the button is pressed: reverse the on/off status of the red lamp and reverse the on/off status of the blue lamp.”

Is there a way to say that?

Thanks in advance!

1 Like

One way to do it would be by making the lamps a kind:

A lamp is a kind of device.

Basement is a dark room. 

The red lamp is a lamp in the basement.
The red lamp is switched on.
The red lamp is lit.

The blue lamp is a lamp in the basement.
The blue lamp is unlit.

The yellow lamp is a lamp in the basement.
The yellow lamp is unlit.

Carry out switching on a lamp (called L):
	now L is lit.

Carry out switching off a lamp (called L):
	now L is unlit.
	
The green button is in Basement.

Before pushing the green button:
	repeat with L running through lamps in the basement:
		if L is switched on:
			try silently switching off L;
		otherwise if L is switched off:
			try silently switching on L;
	say "Click!";
	stop the action;
2 Likes

So that’s clean and nice and in all probability the best solution. But now I’m wondering whether we can do the switching with one statement? If being switched on/off were a kind of value that we could refer to in code, we could do something like this:

now the brightness of L is the brightness after the brightness of L

which is also nice, because we don’t need to distinguish two cases. (And kinds of value loop around, so this would work.) But can we refer to boolean properties in this way?

You could write a phrase:

To reverse-switch (L - lamp):
	if L is switched on:
		try silently switching off L;
	otherwise if L is switched off:
		try silently switching on L;

Then you can do it in one line. One line elsewhere in your code, I mean.

1 Like

You can whip up a “logical not” for boolean values.

It’s a little tricky in this case because – at least in the examples so far – there are two boolean values involved! switched on/off and lit/unlit. As written above, you need to invoke the “try switching on/off” actions in order to keep them in sync.

There’s another approach where you don’t make the lamps devices at all. Just make them lit or unlit directly. This may be easier. It depends (a) whether you want the player to be able to type SWITCH ON LAMP, and (b) whether you have “Check/before/after switching” rules that may affect the process.

3 Likes

The drawback of StJohnLimbo’s approach is that it executes switching actions for every lamp – in effect, per the world model, pressing the green button causes the player character to run around switching lamps on or off. This could be an issue if you have other rules governing those actions.

I would separate out the lit/unlit functionality as its own logic, responsive to the switched on/switched off state of each lamp, and have the green button change the switch state directly:

Toggle
"Toggle"

[safe 6M62 version]
[To toggle (P - property) of (O - object):
	(- SetEitherOrProperty({O}, {P}, GetEitherOrProperty({O}, {P})); -).]

[unsafe 10.1.2 version]
To toggle (P - property) of (O - object):
	(- if ( {O} has {P}) give {O} ~{P}; else give {O} {P}; -).

A lamp is a kind of device.

Every turn (this is the lamp activation rule):
	repeat with L running through lamps:
		if L is switched on, now L is lit;
		otherwise now L is unlit.

Basement is a dark room.

A red lamp and a blue lamp are lamps in Basement. The red lamp is switched on and lit.

A green button is fixed in place in Basement.

The can't push what's fixed in place rule does nothing when the noun is the green button.

Carry out pushing the green button:
	repeat with L running through lamps in the Basement:
		toggle the switched on of L.

After pushing the green button:
	say "There is a simultaneous click from every lamp in the room."

As noted in the comment, the to toggle... phrase is slightly unsafe in 10.1.2, but it should be fine in this case. I’d be curious to know what the proper way to do this in 10.1.2 would be.

The I6 inclusion is not really necessary… I just hate it that there’s not a built-in phrase to flip a binary state.

EDIT: For a little more ease-of-use in your setup code, you can declare:

A switched on lamp is usually lit.

Then a case like the red lamp need only say:

The red lamp is switched on.

and it will be lit when the story begins.

2 Likes

@StJohnLimbo and @zarf and @VictorGijsbers and @otistdog

I thank all of you so much for your help! I spent most of yesterday working out my puzzle. It’s much more complicated than the mini-lamp puzzle I described, but your input was essential to me getting started on it. I couldn’t have done it otherwise without multiplying the workload times ten at least. You saved me a ton of time.

Thanks again!

3 Likes

tldr: in Version 10 you need to do your own investigation of whether your either-or property has been compiled as an I6 attribute or a property, because these thing are done in the final compiled I6 by compiler-generated functions that are not available at compile-time for I6 inclusions.

The key thing to know is that an I7 property is compiled to an I6 word array with the following structure:

-->0 flag for compiled I6 property type; 2 => Attribute; 1 => Property
-->1 I6 property id
-->2 flag for either/or type; 1 => either/or property; 0 => not either/or property
-->3 address of I6 string containing textual name of property
-->4 onwards- a NULL-terminated list of kinds/objects which provide this property

so for example, the I7 property openable compiles to

Array A_openable --> [ 2; subterfuge_15; 1; "openable"; K4_door; K5_container; NULL; ];

small print for complex technical reasons, the property id of openable is actually aliased to a constant of equivalent value, subterfuge_15

After which preamble, here’s how to do it:

Version 10 Toggling Attributes And Properties
"Version 10 Toggling Attributes And Properties" by PB

Lab is a room.

A person can be happy.
A person can be lucky.
A person can be tall.
A person can be short.
A person can be rich.
A person can be poor.
A person can be rude.
A person can be polite.
A person can be famous.
A person can be obscure.
A person can be black.  [up to this point, these either-or properties are implemented as I6 Attributes, hereafter as I6 properties]
A person can be white.
A person can be crazy.
A person can be sad.
A person can be unlucky.


When play begins:
	say "Toggling description of the Entire Game...";
	toggle description of the Entire Game;
	say "Toggling description of the Lab...";
	toggle description of the Lab;
	say "Toggling crazy of the Lab...";
	toggle crazy of the Lab;
	say "The player is [unless the player is happy]not [end if] happy.";
	say "Toggling happy of the player...";
	toggle happy of the player;
	say "The player is [unless the player is happy]not [end if] happy.";
	say "Toggling happy of the player...";
	toggle happy of the player;
	say "The player is [unless the player is happy]not [end if] happy.";
	say "The player is [unless the player is crazy]not [end if] crazy.";
	say "Toggling crazy of the player...";
	toggle crazy of the player;
	say "The player is [unless the player is crazy]not [end if] crazy.";
	say "Toggling crazy of the player...";
	toggle crazy of the player;
	say "The player is [unless the player is crazy]not [end if] crazy.";
	
		
To toggle (P - property) of (S - scene): (- ToggleProperty({S},{P}); -).

To toggle (P - property) of (O - object): (- ToggleProperty({O},{P}); -).

Include
(- 
[ToggleProperty o p;
	if (~~(o ofclass Object)){
		print "*** Programming error: tried to toggle a property of something that is not an object (", (name) o, "). ***^^";
		rfalse;
	}
	if (p-->2 ~=1) {
		print "*** Programming error: tried to toggle a property (", (string) p-->3, ") that is not an either-or property type. ***^^";
		rfalse;
	}
	if (p-->0 ==2){                                ! compiled as I6 attribute
		if ( o has p) give o ~p;
		else give o p;
	}
	else {                                                   ! compiled as I6 property
		if (~~(o provides p)){
			print "*** Programming error: tried to toggle a property (", (string) p-->3, ") of an object which doesn't provide it (", (name) o, "). ***^^";
			rfalse;
		}
		if (o.p==0) o.p=1;
		else o.p=0;
	}
];
-).

EDIT: I reallised after posting this that although the posted code works fine, as it turns out it isn’t necessary to differentiate between Attributes and Properties in code written for I6 inclusions because the I6->Inter->I6 compilation process in fact translates the I6 property-handling code as written to instead use the generic compiler-generated property-handling functions alluded to above, which take Attributes and Properties equally in their stride. So the code posted above actually ends up in auto.inf as:

Summary
[ ToggleProperty o p;
    if ((~~((o ofclass Object)))) {
        print "*** Programming error: tried to toggle a property of something that is not an object (";
        PrintShortName(o);
        print "). ***^^";
        rfalse;
    }
    if (((p-->(2)) ~= 1)) {
        print "*** Programming error: tried to toggle a property (";
        print (string) (p-->(3));
        print ") that is not an either-or property type. ***^^";
        rfalse;
    }
    if (((p-->(0)) == 2)) {
        if (_final_propertyvalue(OBJECT_TY, o, p)) {
            (_final_store_property(OBJECT_TY,o,p, 0));
        } else {
            (_final_store_property(OBJECT_TY,o,p, 1));
        }
    } else {
        if ((~~((_final_propertyexists(OBJECT_TY, o, p))))) {
            print "*** Programming error: tried to toggle a property (";
            print (string) (p-->(3));
            print ") of an object which doesn't provide it (";
            PrintShortName(o);
            print "). ***^^";
            rfalse;
        }
        if ((_final_propertyvalue(OBJECT_TY, o, p) == 0)) {
            (_final_store_property(OBJECT_TY,o,p, 1));
        } else {
            (_final_store_property(OBJECT_TY,o,p, 0));
        }
    }
];

As can be seen by inspecting this, the following would (and does) actually work fine:

Summary
Include
(- 
[ToggleProperty o p;
	if (~~(o ofclass Object)){
		print "*** Programming error: tried to toggle a property of something that is not an object (", (name) o, "). ***^^";
		rfalse;
	}
	if (p-->2 ~=1) {
		print "*** Programming error: tried to toggle a property (", (string) p-->3, ") that is not an either-or property type. ***^^";
		rfalse;
	}
	if (~~(o provides p)){
		print "*** Programming error: tried to toggle a property (", (string) p-->3, ") of an object which doesn't provide it (", (name) o, "). ***^^";
		rfalse;
	}
	if (o.p==0) o.p=1;
	else o.p=0;
];
-).
4 Likes

I might do something like:

Basement is a dark room. 

a lamp is a kind of thing.
The red lamp is a lit lamp in the basement.
The blue lamp is a lamp in  the basement.

Instead of switching on a lit lamp, say "[The noun] is already lit.".
Instead of switching off an unlit lamp, say "[The noun] is already unlit.".
Instead of switching on or switching off a lamp, say "There's no obvious switch.".

The green button is in Basement.
carry out pushing the green button:
  repeat with l running through lamps in the basement begin;
    if l is lit, now l is unlit;
    else now l is lit;
  end repeat;

after pushing the green button: say "click!"
2 Likes

Which means that if you’re willing to forgo the I6 error-checking (by ensuring that your object provides said either-or property) you can for 10.1.2 just write:

To toggle (p - a property) of (o - an object): (- {o}.{p} = ~~({o}.{p}); -).

because it will be translated into I6 code which works for both Attributes and Properties.

2 Likes