The inputs are the statements for each box.
Basically, the tool is going to go through all possible combinations and evaluate each of the statements with the assumed truthfulness, to see if it leads to a contradiction.
So, for the If TRUE condition - you write the logic that would follow if the statement is assumed to be true.
For the If FALSE condition - you write the logic that would follow if the statement is assumed to be false.
You might ask, aren't the FALSE conditions just the negation of the TRUE conditions? Yes. But sometimes, the negation is a logical paradox - you can write false
for those cases, for example.
That said, in most cases, the negation is just the same as the TRUE condition but negated, so it will be generated automatically for you as you write the If TRUE condition- but you can override it.
The expressions are JEXL (JavaScript Expression Language) expressions, which is pretty similar to JavaScript. For example, you can use white.isEmpty || (black.isAllTrue && black.hasGems)
to represent a statement like "the white box is empty or the black box is true AND contains gems".
You can find more examples in the Help modal.
If you'd like to read up more on how to write JEXL statements, you can do so here, though the examples should be more than enough to get you started, and most of the basic JS syntax will work as you would expect it.
Your JEXL condition will have access to the following objects (and each will have a different value when considering each possibility) - blue, white, black, each object with:
{
"color": "blue" or "white" or "black",
"oneIsTrue": true if blue 1 statement is assumed true in this case, false otherwise,
"oneIsFalse": true if blue 1 statement is assumed false in this case, false otherwise,
"twoIsTrue": true if blue 2 statement is assumed true in this case, false otherwise,
"twoIsFalse": true if blue 2 statement is assumed false in this case, false otherwise,
"threeIsTrue": true if blue 3 statement is assumed true in this case, false otherwise,
"threeIsFalse": true if blue 3 statement is assumed false in this case, false otherwise,
"labels": array of strings, each representing the label of the statement in this box (labels[0] = blue 1 label, labels[1] = blue 2 statement, etc.)
"bools": array of booleans, each representing the truthiness of the statement in this box (bools[0] = blue.oneIsTrue, bools[1] = blue.twoIsTrue, etc.)
"isAllTrue" or "isTrue" or "allIsTrue": true if all of the statements in this box are assumed true in this case, false otherwise
"isAllFalse" or "isFalse" or "allIsFalse": true if all of the statements in this box are assumed false in this case, false otherwise
"isMixed": true if this box has at least one true and at least one false statement (so it's neither an all-true nor all-false box)
"trueCount": number of statements that are assumed true in this case
"falseCount": number of statements that are assumed false in this case
"isEmpty" or "empty" or "hasNoGems": true if this sub-case assumes it's empty
"hasGems" or "notEmpty" or "isNotEmpty": true if this sub-case assumes it has gems
}
You will also have access to the object "context" with the following properties:
{
"trueBoxCount" or "allTrueBoxCount": number of boxes that are assumed to have only true statements in this case
"falseBoxCount" or "allFalseBoxCount": number of boxes that are assumed to have only false statements in this case
"trueStatementCount": number of statements that are assumed true in this case across all boxes
"falseStatementCount": number of statements that are assumed false in this case across all boxes
"boxWithGems": a reference to the box object that is assumed to have gems in this sub-case
"boxesWithoutGems" or "emptyBoxes": an array of references to the box objects that are assumed to be empty in this sub-case
}
A statement like "this box is of no help at all", for example, right?
That's somewhat of a subjective box, but most of those boxes are usually false, or paradoxical enough to be considered false.
For those, you can provide false
as the condition for the TRUE case, and true
as the condition for the FALSE case, which would mean that this statement cannot be true (but it logically works if we assume it's false).
Essentially, it goes through all 6-18+ possible combinations of 1-3 true/false statements.
Then, for each case, goes through all 3 possible gem placements.
It evaluates each statement with its assumed truthfulness.
If any of the statements evaluate to false - that's a contradiction.
If any of the 3 possible gem placements work out with no contradictions across all statements - that's a valid answer.
In the end, if there's only one valid answer - that's your definitive answer.
If there's multiple - there's some sort of ambiguity that you can resolve by looking at the page's reasoning for each of the cases.
If there are none - there must be some logical error in how some of the statements are constructed.
The parlor puzzles are designed in a way that they can be solved with logic, without any guessing necessary.
This means, if you are getting an ambiguity - there is something missing: perhaps you made a typo in one of the statements, perhaps you missed a statement, maybe you missed a constraint that a statement implies, perhaps you misinterpreted the statement.
If you have any questions, or if you feel like the tool isn't working as expected, feel free to post an issue on Github.
The parlor puzzles always have at least one possible answer, so if you're getting a 0 possible answers error - there is something wrong with the statements.
Double-check all your statements and make sure they are all logically consistent, and that you're not adding some constraint that isn't implied by the statements.
For example, think about the statement "a box next to this box has only true statements". That does not imply that there are no false boxes next to the box - just that there is SOME box next to it that has only true statements.
You must have made some syntax error in the JEXL expression, or perhaps you tried accessing a property that doesn't exist.
To prevent common typos, I've made it so that access to properties that don't exist will throw an error, rather than returning undefined.
That's because you wouldn't want white.iSTrue
to return undefined (falsy), even though white.isTrue
may have been true.
JEXL is more of a subset of JavaScript, so it doesn't support all of the functions you might be used to.
Though, you can always add your own functions to JEXL, if you are familiar enough with JavaScript.
For example, you could add a function countAllWords
by opening the devtools console and typing like this:
Jexl.addFunction('countAllWords', box => {
return box.labels.reduce((acc, label) => acc + (label ? label.split(' ').length : 0), 0);
});
You could then use it in your JEXL expressions like this:
countAllWords(blue) > 1
The labels are just for your own reference. They don't affect the logic of the statements in any way.
Basically, if you plan to look through each of the possible cases to troubleshoot - you can write the labels there to help you remember what each statement means.
You can also access these labels through the JEXL statements in case you got a statement like "The box with more than 8 words" and you don't want to count by hand.
Usually, even when it feels like it's hard to translate it into a logical statement - it is still possible, without any loss of data.
For example, if it says "a box next to this box has only true statements" - you don't need to try and figure out how to represent "next to" in a logical statement.
Instead, you can simply look at which boxes are next to this box, then, let's say it's the blue and black boxes - write: blue.isAllTrue || black.isAllTrue
.
You can share your inputs with someone by copying the URL of the page after you have filled out the inputs and pressed Calculate.
When you hit Calculate, the page will generate a unique URL that contains all of your inputs and the calculated results and append it to the URL.
This is also edited into your navigation history, so if you refresh or go back - your inputs will be preserved.
You're more than welcome to open an issue or a pull request in the Github repository!
You know the rules for the parlor already from Blue Prince.
You need to choose the number of statements you have for each of the three boxes, as well as filling out the statements:
The script go through each possible combination of which statements are true/false, e.g. blue statement 1 is True, blue statement 2 is True, white statement 1 is False, white statement 2 is True, black has one statement and it's False.
For each combination (case), it will consider 3 possibilities: gems are in the blue box, gems are in the white box, and gems are in the black box.
For each of those possibilities, it will evaluate the JEXL conditions for the assumption and if any of the possibilities has all of the JEXL conditions returning true - that's a valid case. Else, it shows the contradiction.
Your JEXL condition will have access to the following objects (and each will have a different value when considering each possibility) - blue
, white
, black
, each object with:
{
"color": "blue" or "white" or "black",
"oneIsTrue": true if blue 1 statement is assumed true in this case, false otherwise,
"oneIsFalse": true if blue 1 statement is assumed false in this case, false otherwise,
"twoIsTrue": true if blue 2 statement is assumed true in this case, false otherwise,
"twoIsFalse": true if blue 2 statement is assumed false in this case, false otherwise,
"threeIsTrue": true if blue 3 statement is assumed true in this case, false otherwise,
"threeIsFalse": true if blue 3 statement is assumed false in this case, false otherwise,
"labels": array of strings, each representing the label of the statement in this box (labels[0] = blue 1 label, labels[1] = blue 2 statement, etc.)
"bools": array of booleans, each representing the truthiness of the statement in this box (bools[0] = blue.oneIsTrue, bools[1] = blue.twoIsTrue, etc.)
"isAllTrue" or "isTrue" or "allIsTrue": true if all of the statements in this box are assumed true in this case, false otherwise
"isAllFalse" or "isFalse" or "allIsFalse": true if all of the statements in this box are assumed false in this case, false otherwise
"isMixed": true if this box has at least one true and at least one false statement (so it's neither an all-true nor all-false box)
"trueCount": number of statements that are assumed true in this case
"falseCount": number of statements that are assumed false in this case
"isEmpty" or "empty" or "hasNoGems": true if this sub-case assumes it's empty
"hasGems" or "notEmpty" or "isNotEmpty": true if this sub-case assumes it has gems
}
You will also have access to the object "context" with the following properties:
{
"trueBoxCount" or "allTrueBoxCount": number of boxes that are assumed to have only true statements in this case
"falseBoxCount" or "allFalseBoxCount": number of boxes that are assumed to have only false statements in this case
"trueStatementCount": number of statements that are assumed true in this case across all boxes
"falseStatementCount": number of statements that are assumed false in this case across all boxes
"boxWithGems": a reference to the box object that is assumed to have gems in this sub-case
"boxesWithoutGems" or "emptyBoxes": an array of references to the box objects that are assumed to be empty in this sub-case
}
If you're doing the TRUE js condition for the statement "the white box is empty", then use the following JEXL condition:
white.isEmpty
If you're doing the FALSE js condition for the statement "every statement with the word "empty" is false", the negation of which would be "at least one statement with the word "empty" is true", and assuming the statements that contain the word "empty' are blue 1, white 1, white 2 and black 1, then use the following JEXL condition:
blue.oneIsTrue || white.oneIsTrue || white.twoIsTrue || black.oneIsTrue