Adams: Randomly picking one-and-only-one of a group

Any Adams code enthusiasts have ideas about optimizing this situation?

Let’s say there’s a room with some number people (they can be objects, or not) and randomly, in each game, I want one-and-only-one of those people to be assigned as left-handed, and then that left-handed person would have some special responses when the player took a couple of actions later.

All of my ideas for doing this so far seem pretty brittle and cumbersome.

The real number of people under consideration will probably be six, plus/minus one. Examples below use three for simplicity.

I could allocate n+1 flags to the problem:

# flag 1: have we assigned a left-hander yet?
# flag 2: ABC is left-handed
# flag 3: XYZ is left-handed
# flag 4: MNO is left-handed

occur 33% when !flag 1
   set_flag 1
   set_flag 2 # ABC is the left-hander

occur 33% when !flag 1
   set_flag 1
   set_flag 3 # XYZ is the left-hander

occur 33% when !flag 1
   set_flag 1
   set_flag 4 # MNO is the left-hander

action shake abc when here abc and flag 2
   print "ABC reaches with his LEFT hand!"

action shake abc when here abc
  [do other stuff]

action shake xyz when here xyz and flag 3
   print "XYZ reaches with her LEFT hand!"

action shake xyz when here xyz
   [do other stuff]

action shake mno when here mno and flag 4
   print "MNO reaches with her LEFT hand!"

action shake mno when here mno
   [do other stuff]


…blugh.

I could use one flag and a set of duplicate items for each of these characters…

# flag 1: have we assigned a left-hander yet?

item xyz "Mr. XYZ"

item xyzlh "Mr. XYZ"
  nowhere

item abc "Ms. ABC"

item abclh "Ms. ABC"
  nowhere

item mno "Ms. MNO"

item mnolh "Ms. MNO"
  nowhere

occur 33% when !flag 1
   set_flag 1
   destroy abc
   drop abclh
.
.
.

action SHAKE ABC when here abc
  [do stuff]

action SHAKE ABC when here abclh
  print "You each lead with a DIFFERENT HAND and its very EMBARRASSING!"
.
.
.

Is there a more elegant way of going about this in that system?

First, a note—Scott Adams games (iirc) process all “occur” actions one after another, with the metaphorical dice rerolled every time. So if you give each one a 33% chance, there’s a 30% chance that none of the three is selected! (67% × 67% × 67% = 30%)

Instead, you’ll want the first one to have a 1/3 chance, the second one to have a 1/2 chance, and the third one to have a 1/1 chance. That’ll ensure one of the three is picked at random.

Second, I would use a counter for this. Use set_counter 1, set_counter 2, and set_counter 3 to mean “ABC is left-handed”, “XYZ is left-handed”, and “MNO is left-handed”, then you can test this at any point with the condition counter_eq 1 (or 2 or 3).

occur 33% when counter_eq 0
    set_counter 1

occur 50% when counter_eq 0
    set_counter 2

occur 100% when counter_eq 0
    set_counter 3

action shake abc when here abc and counter_eq 1
    print "ABC reaches with his LEFT hand!"

There are technically 16 counters available, but it’s much easier to code if you only use one of them—if not, you’ll need to use select_counter and that gets messy fast. So I’d stick to one if at all possible.

EDIT: There’s also no !counter_eq check, which is extremely annoying. So for the negated check, you’ll have to use counter_gt 1 for “not XYZ”, counter_le 2 for “not MNO”, and duplicate your code with both counter_gt 2 and counter_le 1 for “not ABC”. This is just something that the SA format is very bad at.

If it’s any consolation, it’s a bit better for random rooms: use swap_specific_room.

3 Likes

Thanks! I knew about the reroll but hadn’t processed the implications through to that level–you’re right, especially if I only take one crack at it, I should use that approach to the randomization, thanks.

I should have said this in my musings: I did think of the counter, and I’ve seen various warnings about using multiple counters, and I might very well like to have a countdown timer running during this scene. But I think I can structure things in such a way that I can dispose of the need to know who the Special Character was by time I care about running a timer, so this might end up the winner.

Borrowing your idea, something similar could be possible with flags, for up to 5 4 persons:

# Initialize one left handed person
occur 25% when !flag 2 and !flag 3 and !flag 4 and !flag 5
    set_flag 2

occur 33% when !flag 2 and !flag 3 and !flag 4 and !flag 5
    set_flag 3

occur 50% when !flag 2 and !flag 3 and !flag 4 and !flag 5
    set_flag 4

occur 100% when !flag 2 and !flag 3 and !flag 4 and !flag 5
    set_flag 5

action shake def when here def and flag 2
     print "DEF reaches with his LEFT hand"
action shake ghi when here ghi and flag 3
     print "GHI reaches with his LEFT hand"
action shake jkl when here jkl and flag 4
     print "JKL reaches with his LEFT hand"
action shake mno when here mno and flag 5
     print "MNO reaches with his LEFT hand"

action shake def when here def and !flag 2
     print "DEF reaches with his RIGHT hand"
action shake ghi when here ghi and !flag 3
     print "GHI reaches with his RIGHT hand"
action shake jkl when here jkl and !flag 4
     print "JKL reaches with his RIGHT hand"
action shake mno when here mno and !flag 5
     print "MNO reaches with his RIGHT hand"

room greeting_room "greeting room"

item def "DEF"

item ghi "GHI"

item jkl "JKL"

item mno "MNO"

Strangely though, this fails to compile in ScottKit with more than 4 conditions, maybe a bug report is in order. The engine is supposed to be able to handle 5 conditions :confused:

One “condition” is needed for defining the flag to be set in the initialization step, so that leaves 4 conditions used for defining the flag.

This approach could be extended for more “people” with the use of the “continue” command, something like this:

occur 20% when !flag 1 and !flag 2 and !flag 3 and !flag 4 and !flag 5
    continue

occur 0%
    set_flag 1

…or:

occur 20% when !flag 1 and !flag 2 and !flag 3 and !flag 4
    continue

occur 0% when !flag 5
    set_flag 1
1 Like

(just want to say that I appreciate the fact that you played along and embraced the trope of unnecessary capitalization in your own examples. I hope that we as a community can agree that all SA code examples should have at least one instance of unnecessary capitalization or completely avoidable typographical/grammatical error.)

6 Likes

Alternately, you could use one flag as a “have we picked someone?” flag, which gives you an arbitrary number (well, as many flags as you’re willing to devote).

occur 17% when !flag 1
    set_flag 2
    set_flag 1
occur 20% when !flag 1
    set_flag 3
    set_flag 1
occur 25% when !flag 1
    set_flag 4
    set_flag 1
[etc]

Which is back to what was suggested in the first post, just with fixed probabilities.

Honestly, this is making me wish ScottKit let you define your own names for flags. Something like define lefty-picked = 1 (and thus when !flag lefty-picked) would be a great quality-of-life improvement here.

2 Likes

Agreed. That, and counter numbers are the only numbers that aren’t abstracted away. Not sure how realistic it is to hope for an addition to the language at this point.

I would prefer the behavior to be something like the implicit declarations that already exist in ScottKit instead. That is, flags being declared when you actually used them, and are assigned a random id# behind the scenes, just like it’s done with items, words, etc.

2 Likes

I can see a case for either.

If it’s abstracted away behind the scenes, one of the main benefits on the upside would be to keep us from shooting ourselves in the foot using the reserved darkness flag! Yay!

But then it really would be nice if it was alert enough to keep us from shooting ourselves in the foot with too many flags. Boo.

While we’re talking, is there any other accepted documentation out there of limits in the SA system on things like “numbers of items and rooms” and “number of characters in an item or room name”?

As far as I know, there was never a hard limit on this (except what was imposed by the BASIC the interpreter was running on). Same with flags and such.

The system is obscure enough for there not to be much in the way of consensus, I think. I like to think of the SA system as fairly unlimited in spirit, but bogged down by some de-facto limits imposed by the available interpreters for the .dat/.sao format.

The documentation for Bruce Hansen’s ADVEDIT has the following to say:

The following limitations are imposed on data entered via the ADVEDIT program:

    Maximum number of action entries is 300.
    Maximum number of vocabulary entries is 150.
    Maximum number of rooms is 100.
    Maximum number of messages is 100.
    Maximum number of objects is 150.
    Maximum characters in a text description of an object, room, message or action title is 255.

These "limitations" are far in excess in most cases of any current Scott Adams' Adventure.

The maximum values encountered by any current Scott Adams' adventure are:

    270 Action entries
    80 Vocabulary words
    30 Rooms
    99 Messages
    100 Objects

My next web-based interpreter is (optionally) going to be using a JSON format with less limited storage capacity, and possibly some more counters and flags. Maybe I’ll need to make my own ScottKit compiler for it, so that it can load ScottKit code directly. It’s going to be pretty fast and efficient either way. The ScottKit format makes more sense as a modern game format for the SA engine than .dat/.sao does, except for the the complexity of parsing the files.

Thanks. Makes me curious about which game had the 270 actions (that’s a lot to roll by hand!) and which game had 100 objects.

Just looking at the original SA adventures, it looks something like this:

Objects:

 49 Savage Island Part Two (1981)
 54 Secret Mission (1979)
...
 78 Ghost Town (1980)
 80 Golden Voyage (1981)[6]
101 Pyramid of Doom (1979)

Actions:

162	Secret Mission (1979)
170	Adventureland (1978)
...
268	Sorcerer of Claymorgue Castle (1984)
272	Savage Island (1980)
278	Return to Pirate's Isle (1984)

Assuming the encoding of the original .dat/.sao file format, and also assuming that the data type of the numbers is signed 16 bit integer, as in the Basic version of the interpreter, i would compile my own list of limitations imposed by the file format alone, as follows:

149 nouns (the first of the 150 nouns is reserved for "ANY")
218 verbs (32767/150)
1638 rooms (32767/20)
1638 objects (32767/20)
167 "odd" messages (32767/150-51)
1638 flags (32767/20)
1638 counters (32767/20)
32767 actions

This obviously doesn’t take the limited memory of a TRS-80 into account :slight_smile:

2 Likes