"Combination of &&s and ||s is too complex" error after simplifying a subclause
I have checkpointed achievement code that checks to make sure the player never leaves a particular subset of areas, as shown below:
function IsAlwaysInTraverseTownKeyhole() => never(!IsInLocation("TraverseKeyholeFirst") && !IsInLocation("TraverseKeyholeSecond")
&& !IsInLocation("TraverseKeyholeThird") && !IsInLocation("TraverseKeyholeFall") && !IsInLocation("TraverseKeyholeTerminus"))
achievement(title = "Saved a Framerule", points = 25,
description = "Complete the Traverse Town Keyhole without falling into pits, getting caught behind blox, or dying (Critical Mode; Level 7 or below).",
trigger = IsAtLeastOnDifficulty("Critical") && level() <= 7 && IsAlwaysInTraverseTownKeyhole()
&& once(KeyholeCheckpointPassed() && IsInLocation("TraverseKeyholeFirst"))
&& never(hp() <= 0) && JustDefeatedEnemy("Guard Armor") && never(JustFellOrGotCaughtInSidescrollingStages())
)
Removing one of the operands from the IsAlwaysInTraverseTown() function results in a "Combination of &&s and ||s is too complex for subclause" error, which is odd when one considers the fact that the latter is a simpler subclause than the former, and yet the former compiles just fine.
I have attached a version of the script that demonstrates the behavior. Kingdom Hearts Recoded - Copy.rascript.txt
There's several things leading up to this failure. The logic can effectively be represented as follows:
function f(n) => byte(0x19836D) == n && byte(0x19836C) == 1
function f2() => never(!f(5) && !f(6) && !f(7) && !f(8) && !f(9))
the never() parameter is correctly expanded to:
(byte(0x19836D) != 5 || byte(0x19836C) != 1) &&
(byte(0x19836D) != 6 || byte(0x19836C) != 1) &&
(byte(0x19836D) != 7 || byte(0x19836C) != 1) &&
(byte(0x19836D) != 8 || byte(0x19836C) != 1) &&
(byte(0x19836D) != 9 || byte(0x19836C) != 1)
But after that, things go wrong.
The logic mathematically determines that the cross product would result in 32 alts, so it opts to use OrNext, which generates five OrNext clauses and no alts. To convert that internal structure into a ResetIf clause, it has to join the OrNexts with AndNexts, resulting in this:

Which is not what was intended, and should error. This is the bug I intend to fix.
When you comment out any single subclause, the cross product is only 16 alts, which is below the arbitrary threshold of 20 alts, so it chooses to generate alts for the cross product. However, the resulting logic requires using an even more complex chain of AndNexts and OrNexts. In this case, it does detect the interaction between the AndNexts and OrNexts and generates the "too complex" error.
I believe this particular requirement can be correctly represented by:
AndNext byte(0x19836D) != 5
AndNext byte(0x19836D) != 6
AndNext byte(0x19836D) != 7
AndNext byte(0x19836D) != 8
OrNext byte(0x19836D) != 9
ResetIf byte(0x19836C) != 1
But I'm not certain that the code can reasonably figure that out.
Incidentally, I can work around the issue by adding redundant checks that force it to result in a larger cross product. It appears to work as expected, but it's certainly not optimal.
But I'm not certain that the code can reasonably figure that out.
It looks like the current code can figure that out. Closing.