Problem (Smullyan). On an island, knights always tell the truth and knaves always lie. From what the inhabitants say, deduce who is what.

Method. A statement made by means exactly . Each puzzle is a boolean formula over the unknown types: try all assignments (or hand it to a SAT solver), and keep the consistent ones.

PuzzleSolution
A: “We are both knaves.”A knave, B knight
A: “At least one of us is a knave.”A knight, B knave
A: “I am a knave.”nobody can say it (no consistent assignment)
A: “I am a knight.”no information (both assignments consistent)
A: “B is a knave.” B: “A and C are the same type.” What is C?C is a knave
two doors, two guards (one always lies), one questionask either: “Which door would the other guard say leads out?” and take the other door
a road fork, one islander of unknown type, one question”If I asked you whether the left road leads to the village, would you say yes?“
three gods (Boolos): True, False and Random answer “ja”/“da”, meaning of “ja” unknown; three yes/no questionsbelow

Embedded question lemma. Let = “If I asked you , would you say ja?”. Both a truth-teller and a liar answer ja to iff is true, whatever “ja” means.

Three gods solution.

  1. Ask B: . If ja, C is not Random; if da, A is not Random. Call that god X.
  2. Ask X: : ja means X is False, da means X is True.
  3. Ask X: : ja means B is Random, da means the god other than X and B is Random. The last god is the remaining type.
// Knights (bit 1) and knaves: each statement (speaker, claim) means knight(speaker) == claim(types).
vector<int> knightsAndKnaves(int n, const vector<pair<int, function<bool(int)>>>& st) {
    vector<int> res;
    for (int m = 0; m < (1 << n); m++) {
        bool ok = true;
        for (auto& [s, f] : st) if (bool(m >> s & 1) != f(m)) { ok = false; break; }
        if (ok) res.push_back(m);
    }
    return res;
}
 
// Three gods. type: 0 True, 1 False, 2 Random; jaYes: "ja" means yes. Returns true if the god says "ja".
bool says(int type, bool p, bool jaYes, bool rnd) {
    if (type == 2) return rnd;
    bool yes = type == 0 ? p : !p;
    return yes == jaYes;
}
bool embedded(int type, bool q, bool jaYes, bool rnd) {     // "If I asked you q, would you say ja?"
    if (type == 2) return rnd;
    return says(type, says(type, q, jaYes, false), jaYes, false);
}
array<int, 3> threeGods(const array<int, 3>& g, bool jaYes, int rndBits) {   // returns the deduced types of A, B, C
    int q = 0; auto ask = [&](int who, bool prop) { return embedded(g[who], prop, jaYes, rndBits >> q++ & 1); };
    int X = ask(1, g[0] == 2) ? 2 : 0;                        // a god who is surely not Random
    int tX = ask(X, g[X] == 1) ? 1 : 0;
    int R = ask(X, g[1] == 2) ? 1 : 3 - X - 1;               // the Random god
    array<int, 3> res; res[X] = tX; res[R] = 2; res[3 - X - R] = 1 - tX;
    return res;
}