import logos
import logos.tactics as tKnights and Knaves
The classic puzzles of Raymond Smullyan: an island where every inhabitant is either a knight (always tells the truth) or a knave (always lies). Given what the inhabitants say, deduce who is who.
Logos can verify these deductions formally — you write the proof, logos checks it.
Encoding
Each inhabitant has two proposition variables:
a_kn— “A is a knight”a_kv— “A is a knave”
Every puzzle rests on four structural constraints shared by all inhabitants:
- Exhaustivity: everyone is a knight or a knave —
a_kn | a_kv - Exclusivity: no one is both —
a_kn >> ~a_kv,a_kv >> ~a_kn - Knight rule: if a person is a knight, their statement is true —
a_kn >> <statement> - Knave rule: if a person is a knave, their statement is false —
a_kv >> ~<statement>
Because Python’s >> is left-associative — A >> B >> C parses as (A >> B) >> C — theorems with many premises use logos.chain(h₁, h₂, ..., conclusion), which builds the correct right-associative chain h₁ >> (h₂ >> ...).
Puzzle 3 — Three People
A says: “B is a knave.” B says: “A and C are the same type” (both knights or both knaves). C says: “A is a knight.”
Reasoning.
- If A = knight: A says B=knave → B=knave. B=knave → B’s statement is false → A and C are different types → C=knave (since A=knight). C=knave → C’s statement is false → A=knave. Contradiction.
- If A = knave: A says B=knave → false → B=knight. B=knight → B’s statement is true: A and C are the same type. A=knave, so C=knave. C=knave → C’s statement is false → A is not a knight. Consistent ✓
Conclusion: A=knave, B=knight, C=knave.
@logos.theorem
def puzzle3(a_kn: bool, a_kv: bool,
b_kn: bool, b_kv: bool,
c_kn: bool, c_kv: bool):
b_says = (a_kn & c_kn) | (a_kv & c_kv) # "A and C are the same type"
return logos.chain(
a_kn | a_kv, # exhaustivity
b_kn | b_kv,
c_kn | c_kv,
a_kn >> ~a_kv, # exclusivity
a_kv >> ~a_kn,
b_kn >> ~b_kv,
b_kv >> ~b_kn,
c_kn >> ~c_kv,
c_kv >> ~c_kn,
a_kn >> b_kv, # A=knight: "B is knave" is true
a_kv >> b_kn, # A=knave: "B is knave" is false
b_kn >> b_says, # B=knight: "A,C same type" is true
b_kv >> ~b_says, # B=knave: "A,C same type" is false
c_kn >> a_kn, # C=knight: "A is knight" is true
c_kv >> ~a_kn, # C=knave: "A is knight" is false
a_kv & b_kn & c_kv, # conclusion: A=knave, B=knight, C=knave
)logos.check(puzzle3.proof(
t.intro("a_kn", "a_kv", "b_kn", "b_kv", "c_kn", "c_kv",
"h_a_ex", "h_b_ex", "h_c_ex",
"h_akn_exc", "h_akv_exc",
"h_bkn_exc", "h_bkv_exc",
"h_ckn_exc", "h_ckv_exc",
"h_a_kn_says", "h_a_kv_says",
"h_b_kn_says", "h_b_kv_says",
"h_c_kn_says", "h_c_kv_says"),
t.cases("h_a_ex", "h_a"),
# ── Case A = knight (h_a : a_kn) ──────────────────────────────────
# A=knight → B=knave. B=knave → b_says is false.
# In either sub-case for C, we reach a contradiction.
[
t.have("h_bkv", "h_a_kn_says", "h_a"), # B is knave
t.have("h_not_bsays","h_b_kv_says", "h_bkv"), # ~((a_kn&c_kn)|(a_kv&c_kv))
t.cases("h_c_ex", "h_c"),
# Sub-case C = knight (h_c : c_kn)
# a_kn & c_kn satisfies the left disjunct of b_says → contradicts ~b_says
[
t.conj("h_a", "h_c", "h_ac"), # h_ac : a_kn & c_kn
t.contradiction(), # ~(A|B) with A present
],
# Sub-case C = knave (h_c : c_kv)
# C=knave says "A is knight" is false → ~a_kn, contradicts h_a : a_kn
[
t.have("h_not_akn", "h_c_kv_says", "h_c"),
t.contradiction(), # a_kn and ~a_kn
],
],
# ── Case A = knave (h_a : a_kv) ───────────────────────────────────
# A=knave → B=knight. B=knight → b_says is true.
# C=knight sub-case leads to contradiction; C=knave is the solution.
[
t.have("h_bkn", "h_a_kv_says", "h_a"), # B is knight
t.cases("h_c_ex", "h_c"),
# Sub-case C = knight (h_c : c_kn)
# C=knight says A=knight → a_kn. But h_a says a_kv.
# Exclusivity: a_kv → ~a_kn. So a_kn and ~a_kn.
[
t.have("h_akn", "h_c_kn_says", "h_c"), # a_kn
t.have("h_not_akn", "h_akv_exc", "h_a"), # ~a_kn
t.contradiction(),
],
# Sub-case C = knave (h_c : c_kv) — the solution
[
t.split(),
[t.split(), [t.assumption()], [t.assumption()]], # a_kv & b_kn
[t.assumption()], # c_kv
],
],
))
print("Puzzle 3: A=knave, B=knight, C=knave ✓")Puzzle 3: A=knave, B=knight, C=knave ✓