First of all, to summarize how this works, 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.
There are two main ways to use this tool, a simple method and an advanced method.
For the simple method, you don't even need to understand what these inputs and labels are: you can just follow the steps and everything should work on its own.
Please note that the simple method will use AI, and AI is unreliable. Maybe my prompt can be improved - I'd be happy to check out any suggestions.
The advanced method is the same, except you write the JEXL expressions yourself, rather than using the AI to generate them for you.
You can read the other relevant FAQ entries below to learn more about how to write the JEXL expressions yourself, if that's something you'd like to do.
For the If TRUE condition - you write the logic that would follow if the statement is assumed to be true.
The FALSE conditions are always direct negations of the TRUE condition (not opposite, though - negation). They are automatically filled in for you when you enter the true statements (but they represent the assumption that follows 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.
It's recommended, because AI can be pretty stupid, but it's not absolutely necessary - you can check out the first FAQ entry "How do I use this tool?" to see how you can automate it all with AI.
Essentially, the AI will translate the statements for you, and you can just copy-paste the generated codeblock into the "Import from JSON" modal.
Here is the full prompt, if you'd like to use it directly (and pass the screenshots directly to the AI, rather than pasting them here for OCR):
You are a parlor puzzle logic translator for a restricted subset of JEXL. Your output must ONLY use this limited grammar. If a statement cannot be expressed using only these allowed constructs, fail explicitly or hard-code as needed.
The user will supply three boxes-blue, white, black-and a numbered list of natural-language statements for each. Your job is to translate logical, freeform statements into JEXL expressions.
Available JEXL context objects and properties:
Each of `blue`, `white`, `black` (also available as `this` in its own box):
* `.color`: `"blue"` / `"white"` / `"black"`
* `.statements[]`: array of statements (strings)
* `.bools[]`: assumed truth values (bools) for the statements
* `.oneIsTrue`, `.twoIsTrue`, `.threeIsTrue`: true if the 1st / 2nd / 3rd statement is true
* `.oneIsFalse`, `.twoIsFalse`, `.threeIsFalse`: true if the 1st / 2nd / 3rd statement is false
* `.trueCount`, `.falseCount`: number of true/false statements
* `.isAllTrue`, `.isAllFalse`, `.isMixed`: box has only true, only false, or a mix
* `.hasGems`, `.isNotEmpty`, `.notEmpty`: box is assumed to contain gems
* `.isEmpty`, `.hasNoGems`, `.empty`: box is assumed empty
From the shared `context` object:
* `.trueBoxCount`, `.falseBoxCount`: count of boxes with all-true or all-false statements
* `.trueStatementCount`, `.falseStatementCount`: total number of true/false statements across all boxes
* `.boxWithGems`: reference to the box assumed to hold the gems
* `.boxesWithoutGems`, `.emptyBoxes[]`: array of boxes assumed to be empty
When solving, please ensure to consider the following:
1. Each box can have up to 3 statements. The user will provide every statement.
2. A box is considered "true" if it ONLY has true statements, and "false" if it ONLY has false statements. If it has a mix of both, it's mixed.
Notes:
* Statements may reference other statements in the puzzle. 'That', 'this', and similar references typically refer to immediately preceding statements or contextually obvious referents (like "this box" is the box the statement is in, "that's not true" is likely referring to the previous statement of the same box).
* If certain statements are impossible or difficult to reprent as JEXL (such as "any statement with an odd number of words"), pre-compute or hard-code the logic. E.g, if a statement says "A statement with an odd number of words is always true", you can look at the statements, gather in your head which ones have an odd number of words, and then write the JEXL expression where you hard-code which statements are expected to be true (the ones with an odd number of words).
* For cases where JEXL statements had to be inferred and weren't "direct" translations, please provide a comment in notes under the output codeblock on what inferrences you made and what they were based on.
* Only use JEXL-safe expressions. Do NOT use string methods (e.g., .split(), .join(), .length), loops, or any operations not explicitly listed in the available context or standard JEXL syntax. Any property used must be directly accessible from the provided context objects. If a statement requires analysis (like word counts), compute those manually during translation and hard-code their truth conditions.
* You have four additional allowed custom functions: "join(array, separator)", "len(arr_or_str)", "count(arr_or_str_haystack, needle)" and "split(string, separator)". These are the only functions you can use. You can use them to join, count or split strings and arrays, but nothing else. You cannot call them as methods, you must call them like join(blue.statements, ", ")
* Only reference .statements[n], .bools[n], etc. for existing statements. Do not assume all three statements exist in every box, look at the provided counts and stay within them.
Boolean: &&, ||, !
Comparison: ==, !=, >, <, >=, <=
Avoid comparing by reference, as objects are copied.
Examples:
"exactly two true in blue" -> blue.trueCount == 2
"the empty boxes has at least 1 false statement" -> context.emptyBoxes[0].falseCount > 0 && context.emptyBoxes[1].falseCount > 0
"gems are in the blue box" -> blue.hasGems or context.boxWithGems.color == "blue"
"this box is blue" -> this.color == "blue"
If you can successfully translate anything, you must respond with a codeblock containing JSON data matching the schema above, without including the schema itself. If you have any notes, you may add them below the codeblock, but the codeblock itself must be valid JSON that matches the schema.
If a statement cannot be accurately translated using only the allowed JEXL grammar due to unclear meaning, you must not return JSON. Instead, respond with only a follow-up question, asking the user to clarify or guide the translation. Do not assume or attempt to resolve ambiguous phrasing on your own.
Schema:
```
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"properties": {
"blue": { "$ref": "#/definitions/box" },
"white": { "$ref": "#/definitions/box" },
"black": { "$ref": "#/definitions/box" }
},
"required": ["blue", "white", "black"],
"definitions": {
"statement": {
"type": "object",
"properties": {
"statement_text": { "type": "string" },
"jexl_code": {
"type": "string",
"description": "A valid JEXL expression using only boolean operators, comparisons, direct property access, array indexing, and these functions: split(string, separator), join(array, separator), len(arr_or_str). Do NOT use any unlisted method calls like .split(), .join(), .length, or any unlisted functions."
}
},
"required": ["statement", "jexl_code"]
},
"box": {
"type": "object",
"properties": {
"count": { "type": "number" },
"statements": {
"type": "array",
"items": { "$ref": "#/definitions/statement" }
}
},
"required": ["count", "statements"]
}
}
}
```
User input:
Blue:
1. Statement 1
2. Statement 2 (if any)
White:
1. Statement 1
2. Statement 2 (if any)
Black:
1. Statement 1
2. Statement 2 (if any)
3. Statement 3 (if any)
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.
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,
"statements": array of strings, each representing the text of the statement in this box (statements[0] = blue 1 statement, statements[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
}
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.statements.reduce((acc, statement) => acc + (statement ? statement.split(' ').length : 0), 0);
});
You could then use it in your JEXL expressions like this:
countAllWords(blue) > 1
The labels (statements) 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 (or after you've scanned a screenshot, or after you've imported JSON).
When you do any of those things, the page generates a unique URL that contains all of your inputs and the calculated results and appends 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!