Incomprehensible bug - interaction between if statement and boolean set in collection_iterate?

Consider the following demo game:

start_at = place

locations {
   place: location "You're in a place.";
}

objects {
   koala: object "a koala" at="place";
}

booleans {
   noun_is_manipulable: boolean "false";
}

collections {
   objects_to_check: list {
      items = ["koala"];
   }
}

on_command {
   : match "hug _" {
      /* This block causes the bug to appear: */
      : set_false "noun_is_manipulable";
      : collection_iterate "objects_to_check" {
         : if (noun1_is {(item())}) {
            : set_true "noun_is_manipulable";
         }
      }
      
      /* This block does not: */
      // : execute_one_at_random {
      //    : set_false "noun_is_manipulable";
      //    : set_true "noun_is_manipulable";
      // }
      
      /* When this code runs after the first block, "True branch taken." is printed regardless of whether noun_is_manipulable is false or true. */
      : print {("noun_is_manipulable = " + noun_is_manipulable)};
      : if (noun_is_manipulable) {
         : print "True branch taken.";
      }
      : else {
         : print "False branch taken.";
      }
   }
}

Run it, and type the following commands (the output shown is what I get in the Adventuron Classroom web IDE):

You're in a place.

You see a koala.

>HUG KOALA

noun_is_manipulable = true

True branch taken.

> HUG ELEPHANT

noun_is_manipulable = false

True branch taken.

I do not understand how it’s possible for (something equivalent to) : if (false) to execute the code inside of it. I don’t think it’s related to the idiosyncratic control flow in on_command; the same thing happens in a subroutine. As the comments in the source explain, switching out the collection_iterate block for an execute_one_at_random block no longer causes the issue to manifest.

Even weirder is the fact that replacing : if (noun_is_manipulable) with : if (!noun_is_manipulable) still causes the true branch to run both times.

Is this a bug, or is Adventuron fundamentally different from other programming languages in some regard?

2 Likes

I don’t know Adventuron but I’ve long been kind of intrigued by it, so I poked at this for a while and tried some things. For what it’s worth, IMO it certainly does seem to be a bug rather than any kind of intentional behavior.

I’ve been playing around with this for the last hour or two and I’ll be stuffed if I can work out why it’s not working. I can’t see anything wrong with your code. It’s as though the value of the boolean is somehow magically being changed in between printing it and testing it or the test is being ignored. This has got to be a bug, but I’ve been using if…then…else for yonks and never struck anything like this.

The workaround is to do something like this:

      : if (noun_is_manipulable) {
         : print "True branch taken.";
         : done;
      }
      : print "False branch taken.";
      : done;

I’ve seen weird stuff with more complex collection_iterate code. Sometimes having null code in it helps it work, like so. It’s maybe a long shot, but it’s maybe worth a look.

         : if (noun1_is {(item())}) {
            : set_true "noun_is_manipulable";
         } : else {
         }

(question removed – Garry noticed the OP did indeed say the code was written in Adventuron Classroom)

details of my code
      : collection_iterate "ask_list" {
         : if (int(item()) < 3) {
            : return ;
         } : else {
         } # the "else" is necessary. Note to self: DO NOT DELETE
      }

Amusingly I had put in a lot of debug code, including an “else” at first, because it was my first time working with collection_iterate. So it worked okay, then I said “ha! Don’t want that debug text in release!” and once I zapped it the odd bug popped up.

1 Like

The code is Adventuron Classroom. Coincidentally, I just tried it in Adventuron betabeta and it works fine. The only difference is that collection_iterate has to be changed to iterate. This convinces me that it must have been a bug that has been fixed in a later version.

1 Like

Incidentally, I should point out that collections aren’t really appropriate for this sort of thing. When you’re interested in the properties of an individual object, you should be using traits. Here’s how:

start_at = place

locations {
   place: location "You're in a place.";
}

objects {
   koala: object "a koala" at = "place" {traits = [huggable_t]};
}

traits {
   huggable_t : trait;
}

on_command {
   : match "hug _" {
      : disambiguate_s1 "universal" with_trait = "huggable_t";
      : if (s1_has_trait "huggable_t") {
         : print "True branch taken.";
      }
      : else {
         : print "False branch taken.";
      }
   }
}

EDIT: If you’re not familiar with traits, see Traits.

1 Like

Thank you both for taking a look at this problem.

(I should note that this is a stripped-down example to illustrate the bug, and traits wouldn’t work in my actual use case, which involves generating the collection with some look_inside and collection_union calls. Thank you for the suggestion, though.)

Anyway, since everyone agrees that it’s a bug, I’ll download the betabeta version and see if that works instead. If not, I’ll try @aschultz’s workaround.

1 Like

After migrating my code to betabeta, I have now solved my original problem. I think using a newer version where the bug is fixed is the best solution here. Thank you for investigating.

1 Like