Hello, I have a problem where I’d like to run tests on various rooms, but the problem is, they’re named identically.
Putting aside whether this is a good idea in game design, how would we print out r1/r2/r3 below instead of “Dead End?”
r1 is a room. printed name of r1 is "Dead End".
r2 is a room. printed name of r2 is "Dead End".
r3 is a room. printed name of r3 is "Dead End".
section testing - not for release
when play begins:
repeat with Q running through rooms:
say "Testing diagnostics for [Q]: (stuff)[line break]";
Good question. The source code name of a room (or other object) is not compiled into the game as text; only the printed name is. So you’d have to do something like this:
R1 is a room.
Rule for printing the name of R1 while not debug-listing:
say "Dead End".
Debug-listing is an action out of world applying to nothing.
Carry out debug-listing:
repeat with R running through rooms:
say "[R]...";
(The debug-listing action can’t be invoked by the player, since it has no grammar.)
There’s a simpler hacky solution, though. It turns out that the source-code name is compiled in as part of the object’s name synonyms – unless the object is privately-named. A room’s name synonyms aren’t normally checked (because rooms are out of scope by default) but you can peek at them:
To say internal name of (O - object): (- print (address) ({O}).&name-->0; -)
This only prints the first word of the name, and, again, only for publicly-named rooms and objects. So with that definition, this works:
When play begins:
repeat with R running through rooms:
say "[internal name of R]...";
I forgot to mention the rooms were privately-named, but it’s good to see some of the I6 nuts and bolts here, because it’s something I’m always interested in, but I put it off.
The explanation pointed me towards other ways to identify the rooms specific to my code, which is a good enough workaround.
I suppose if nothing else I could arbitrarily number the rooms and say
a room has a number called room-index.
say "[X] ([room-index of X]) needs (diagnostics).";
, which again might not be great code, but it gives the information that I need.
When play begins:
let K be zero;
repeat with R running through rooms:
now the room-index of R is K;
increment K.
Or you could use the rooms’ internal object numbers, which will be more useful on Z-machine than on Glulx but are guaranteed to be unique:
To decide which number is the internal index of (O - an object): (- {O} -).
Or you could come up with an extremely basic hash function to make the internal index more readable, and hope for no collisions:
To decide which text is the hash of (N - a number):
let the alphabet be "0123456789ABCDEFGHJKLMNPQRSTUVWXYZ";
let R be the remainder after dividing N by the length of the alphabet;
decide on character number R in the alphabet.
Then:
Rule for printing the name of a room (called the place) when using debug mode:
say "[printed name of the place] ([hash of the internal index of the place])".
This should disambiguate most rooms; mathematically, the odds of getting two rooms that have the same printed name and the same hash are low (unless you’ve got a whole maze filled with identical rooms).
A better hash function might be something like “take each four-bit unit from the number, add them all together, and discard any overflow”, but that gets into bit manipulation in I6 which is tedious and annoying. So I don’t want to implement that unless there’s actually a demand for it. If that would be useful to people, though, I’ll do it.
…I say things like this, and then the problem sticks in my mind and I can’t get rid of it until I solve it. So here’s a better hash function in I6.
[ ExtractNybble n amt mask shift ;
mask = $0f; ! 00001111 in binary
shift = 4 * amt;
mask = LeftShift(mask, shift);
n = BitwiseAnd(n, mask);
n = RightShift(n, shift);
return n;
];
[ Hash n res i ;
res = 0; ! Just in case
for( i=0 : i<WORDSIZE*2 : i++ ){
res = res + ExtractNybble(n, i);
}
return ExtractNybble(res, 0);
];
Plus some syntactic sugar for things that I6 doesn’t have operators for:
[ LeftShift n amt ;
#Ifdef TARGET_GLULX;
@shiftl n amt n;
#Ifnot;
@log_shift n amt -> n;
#Endif;
return n;
];
[ RightShift n amt ;
#Ifdef TARGET_GLULX;
@ushiftr n amt n;
#Ifnot;
amt = -amt;
@log_shift n amt -> n;
#Endif;
return n;
];
[ BitwiseAnd x y ;
#Ifdef TARGET_GLULX;
@bitand x y x;
#Ifnot;
@and x y -> x;
#Endif;
return x;
];
This will turn any value into a number between 0 and 15 inclusive. So:
To decide which number is the disambiguator of (O - an object): (- Hash({O}) -).
Rule for printing the name of a room (called the place) when using debug mode: say "[printed name of the place] ([disambiguator of the place])".
I’m really glad my question spurred all these ideas. They’ll be useful beyond my current problem.
I also thought of deriving a room’s rough coordinates, as long as the map is relatively reciprocal e.g. going north and south leads you back to the same room. This is uncompiled pseudocode, but I hope it gives the general idea.
after printing the name of a room (called rm) while testanalyzing: say "([xcoord of rm], [ycoord of rm])";
when play begins:
a room has a number called xcoord. a room has a number called ycoord.
map-coords Home Base;
definition: a room (called rm) is uncoord:
if rm is Home Base, no;
if xcoord of rm is 0 and ycoord of rm is 0, yes;
no;
to map-coords (rm - a room):
if the room north of rm is uncoord:
now xcoord of the room north of rm is xcoord of rm;
now ycoord of the room north of rm is ycoord of rm + 1;
map-coord room north of rm;
if the room south of rm is uncoord:
now xcoord of the room south of rm is xcoord of rm;
now ycoord of the room south of rm is ycoord of rm - 1;
map-coord room south of rm;
if the room west of rm is uncoord:
now xcoord of the room west of rm is xcoord of rm - 1;
now ycoord of the room west of rm is ycoord of rm;
map-coord room west of rm;
if the room south of rm is uncoord:
now xcoord of the room east of rm is xcoord of rm + 1;
now ycoord of the room east of rm is ycoord of rm;
map-coord room east of rm;
I suppose another way to do this if directions weren't reciprocal would be to generate a hash that describes the shortest path from home base to the room, maybe where the number was in base 5, e.g. 4321 in base 5 would mean go north east south west.This has its own problems, especially with overflow in a big map, but it seems to work well enough. Maybe you could use a string.