API Reference
This page provides a reference for the modules, functions, and types in ZebraPuzzles.jl.
Modules
ZebraPuzzles: The main module of the package.
Functions
add_clues!: Add clues to a puzzle.add_clue!: Add a single clue to a puzzle.attributes: Get the attributes of a puzzle.riddle: Get the clues of a puzzle.nclue: Get the number of clues in a puzzle.nattr: Get the number of attributes in a puzzle.solve!: Solve a zebra puzzle.
Types
ZebraPuzzle: Abstract type for any zebra puzzle.SolvedZebraPuzzle: A solved zebra puzzle.UnsolvedZebraPuzzle: An unsolved zebra puzzle.
Attribute: Abstract type for all attributes.Clue: Abstract type for all clues.DirectClue: A direct association between two attributes.PositionClue: A clue that specifies the position of an attribute.
ZebraPuzzles.ZebraPuzzles — ModuleZebraPuzzlesDefines logic puzzles of the zebra kind. This module has the logic to define, generate and solve these puzzles as well as phrase them in plain text and markdown.
ZebraPuzzles.EINSTEINS_ZEBRA — ConstantEINSTEINS_ZEBRA isa SolvedZebraPuzzleThis is the famous zebra puzzle that gave zebra puzzles their name. It is said to be created by Albert Einstein. You can learn more about it at the Wikipedia Zebra Puzzle entry.
julia> ZP.EINSTEINS_ZEBRA
SolvedZebraPuzzle{5, 5, Tuple{House, Nationality, Drink, Smoke, Pet}} with 14 clues
┌──────────────────────────────────────────────────────────┐
│ House Nationality Smoke Drink Pet │
├──────────────────────────────────────────────────────────┤
│ yellow Norwegian Kools water fox │
├──────────────────────────────────────────────────────────┤
│ blue Ukrainian Chesterfields tea horse │
├──────────────────────────────────────────────────────────┤
│ red Englishman Old Gold milk snails │
├──────────────────────────────────────────────────────────┤
│ ivory Spaniard Lucky Strike orange juice dog │
├──────────────────────────────────────────────────────────┤
│ green Japanese Parliaments coffee zebra │
└──────────────────────────────────────────────────────────┘
clues:
1) Nationality("Englishman") ⟹ House("red")
2) Nationality("Spaniard") ⟹ Pet("dog")
3) Drink("coffee") ⟹ House("green")
4) Nationality("Ukrainian") ⟹ Drink("tea")
5) Pos[House("green")] == Pos[House("ivory")] + 1
6) Smoke("Old Gold") ⟹ Pet("snails")
7) Smoke("Kools") ⟹ House("yellow")
8) Pos[Drink("milk")] == 3
9) Pos[Nationality("Norwegian")] == 1
10) abs(Pos[Smoke("Chesterfields")] - Pos[Pet("fox")]) == 1
11) abs(Pos[Smoke("Kools")] - Pos[Pet("horse")]) == 1
12) Smoke("Lucky Strike") ⟹ Drink("orange juice")
13) Nationality("Japanese") ⟹ Smoke("Parliaments")
14) abs(Pos[Nationality("Norwegian")] - Pos[House("blue")]) == 1ZebraPuzzles.UNSOLVED_EINSTEINS_ZEBRA — ConstantUNSOLVED_EINSTEINS_ZEBRA isa UnsolvedZebraPuzzleThis is the famous zebra puzzle that gave zebra puzzles their name. It is said to be created by Albert Einstein. You can learn more about it at the Wikipedia Zebra Puzzle entry.
The difference between this constant and EINSTEINS_ZEBRA is that this instance is not solved (does not contain the solution table).
See also EINSTEINS_ZEBRA
julia> ZP.UNSOLVED_EINSTEINS_ZEBRA
UnsolvedZebraPuzzle{5, 5, Tuple{Drink, House, Nationality, Pet, Smoke}} with 14 clues
┌────────────────────────────────────────────────────────────────────────┐
│ Drink coffee, milk, orange juice, tea, water │
├────────────────────────────────────────────────────────────────────────┤
│ House blue, green, ivory, red, yellow │
├────────────────────────────────────────────────────────────────────────┤
│ Nationality Englishman, Japanese, Spaniard, Ukrainian, Norwegian │
├────────────────────────────────────────────────────────────────────────┤
│ Pet dog, horse, snails, zebra, fox │
├────────────────────────────────────────────────────────────────────────┤
│ Smoke Chesterfields, Lucky Strike, Old Gold, Parliaments, Kools │
└────────────────────────────────────────────────────────────────────────┘
clues:
1) Nationality("Englishman") ⟹ House("red")
2) Nationality("Spaniard") ⟹ Pet("dog")
3) Drink("coffee") ⟹ House("green")
4) Nationality("Ukrainian") ⟹ Drink("tea")
5) Pos[House("green")] == Pos[House("ivory")] + 1
6) Smoke("Old Gold") ⟹ Pet("snails")
7) Smoke("Kools") ⟹ House("yellow")
8) Pos[Drink("milk")] == 3
9) Pos[Nationality("Norwegian")] == 1
10) abs(Pos[Smoke("Chesterfields")] - Pos[Pet("fox")]) == 1
11) abs(Pos[Smoke("Kools")] - Pos[Pet("horse")]) == 1
12) Smoke("Lucky Strike") ⟹ Drink("orange juice")
13) Nationality("Japanese") ⟹ Smoke("Parliaments")
14) abs(Pos[Nationality("Norwegian")] - Pos[House("blue")]) == 1ZebraPuzzles.AbsoluteDistance — TypeAbsoluteDistance{A,B} <: RelativePosition{A,B}A clue that states "a::A is d places from b::B"
julia> AbsoluteDistance(House("red"), Drink("coffee"), 2) |> string |> print
abs(Pos[House("red")] - Pos[Drink("coffee")]) == 2
julia> ZP.check(ZP.EINSTEINS_ZEBRA, AbsoluteDistance(House("red"), Drink("coffee"), 2); implied=false, duplicates=false, minimal=false)
true
ZebraPuzzles.AbsolutePosition — TypeAbsolutePosition{A,P} <: PositionClueA clue that states the absolute position p::P of a subject with the attribute a::A.
ZebraPuzzles.Attribute — TypeAttributeRepresents a possible attribute of a subject of type S.
The type parameters determine the phrase that is used for the clues and the subject strings used in the puzzle. S is the type of the subject that the Attribute can describe (Person, House, etc.)
ZebraPuzzles.AttributeError — TypeAttributeError <: ExceptionThrown when an attribute is passed in the context of a puzzle, where this attribute is not valid.
Subtypes defined in ZebraPuzzles are AttributeTypeError and AttributeVariantError
ZebraPuzzles.AttributeExprs — TypeAttributeExprs{K,N,P}The IntExprs about the Attributes of a ZebraPuzzle that are used to solve the puzzle.
ZebraPuzzles.AttributeQuestion — TypeAttributeQuestion{A,S} <: QuestionA question about a specific attribute of type A of the subject type S, or position if S<:Int.
The question that an AttributeQuestion asks can be loosely stated as "What is the attribute with type A linked to the subject s::S?" An answer is the attribute a::A which is linked to the subject s::S.
ZebraPuzzles.AttributeTypeError — TypeAttributeTypeError <: AttributeErrorAn error thrown when an attribute has an invalid type for the given puzzle
See also check_attrtype and AttributeVariantError
ZebraPuzzles.AttributeVariantError — TypeAttributeVariantError <: AttributeErrorAn error thrown when an attribute has a variant but the puzzle does not contain the variant of the attribute passed in the context of the puzzle.
See also check_attrvariant
ZebraPuzzles.Clue — TypeClueSupertype of any clue (DirectClue, PositionClue, etc.) for a ZebraPuzzle.
ZebraPuzzles.ClueError — TypeClueError <: ExceptionThrown when a clue is in conflict with puzzle rules or not compatible with current rules of a puzzle passed within the same context.
Subtypes defined in ZebraPuzzles are DuplicateClue and ImpliedClue
ZebraPuzzles.ConflictingClue — TypeConflictingClue <: ClueErrorThrown when trying to add a clue that is in conflict with the clues that are already in the puzzle.
ZebraPuzzles.DirectClue — TypeDirectClue <: ClueA direct relationship of two attributes. I.e. PositiveClue if they belong together or NegativeClue if they dont.
ZebraPuzzles.DirectionClue — TypeDirectionClue{A,B,D} <: RelativePosition{A,B}A clue that states "a::A is d::D of b::B".
julia> DirectionClue(House("red"), Drink("coffee"), ZP.d_left) |> string |> print
Pos[House("red")] < Pos[Drink("coffee")]
julia> ZP.check(ZP.EINSTEINS_ZEBRA, DirectionClue(House("red"), Drink("coffee"), ZP.d_left); implied=false, duplicates=false, minimal=false)
trueZebraPuzzles.Drink — TypeZebraPuzzles.Drive — TypeDrive <: AttributeWhat does the subject drive.
ZebraPuzzles.DuplicateClue — TypeDuplicateClue <: ClueErrorAn error thrown when trying to add a clue that is already included in the puzzle.
See also check_duplicate
ZebraPuzzles.ExactRelativePosition — TypeExactRelativePosition{A,B,D} <: RelativePosition{A,B}A clue that states "a::A is r::Int places in the direction of d::D from b::B".
ZebraPuzzles.Hat — TypeHat <: AttributeHat that the subject wears.
ZebraPuzzles.House — TypeHouse <: AttributeThe house description that the subject lives in.
In most puzzles given by colour.
See also EINSTEINS_ZEBRA
ZebraPuzzles.ImpliedClue — TypeImpliedClue <: ClueErrorAn error thrown when trying to add a clue that is implied by the rules and clues already included in the puzzle.
See also check_implied, MinimalityViolation
ZebraPuzzles.InvalidClue — TypeInvalidClue <: ClueErrorThrown when a rule is in conflict with the puzzle solution table of the SolvedZebraPuzzle
ZebraPuzzles.MinimalityViolation — TypeMinimalityViolation <: PuzzleErrorAn error thrown when the puzzle minimality would be violated by an operation.
Minimality of the puzzle means that there are no clues which could be discarded and still leave the puzzle solution intact and tractable.
See also check_minimal, ImpliedClue
ZebraPuzzles.Nationality — TypeZebraPuzzles.NegativeClue — Typeconst NegativeClue = DirectClue{ct_negative::ClueType}ZebraPuzzles.Person — TypeZebraPuzzles.Pet — TypeZebraPuzzles.PositionClue — TypePositionClue <: ClueA clue about the position of a subject with a specific attribute or the relation of the positions of a pair of subjects.
Direct subtypes are AbsolutePosition, RelativePosition
ZebraPuzzles.PositionQuestion — TypePositionQuestion{A}A question about the position of the subject with the attribute a::A.
The question that a PositionQuestion asks can be loosely stated as "What is the position of the subject with the attribute a::A?" An answer is the position of the subject with the attribute a::A.
ZebraPuzzles.PositiveClue — Typeconst PositiveClue = DirectClue{ct_possitive::ClueType}ZebraPuzzles.PuzzleError — TypePuzzleError <: ExceptionThrown when a puzzle would violate some optimality conditions by undergoing an operation.
Subtypes defined in ZebraPuzzles are MinimalityViolation
ZebraPuzzles.Question — TypeQuestionA question that can be asked about the solution of a ZebraPuzzle
Subtypes are AttributeQuestion, PositionQuestion
ZebraPuzzles.RelativePosition — TypeRelativePosition{A,B} <: PositionClueA clue about the relationship of the positions of two subjects with the attributes a::A and b::B.
Direct subtypes are ExactRelativePosition, DirectionClue
ZebraPuzzles.Smoke — TypeZebraPuzzles.SolvedZebraPuzzle — TypeSolvedZebraPuzzle{K,N,Attrs} <: ZebraPuzzle{K,N,Attrs}A ZebraPuzzle that is created with a known solution table.
ZebraPuzzles.UnsolvablePuzzle — TypeUnsolvablePuzzle <: PuzzleErrorThrown when calling riddle on a puzzle that is not solvable.
ZebraPuzzles.UnsolvedPuzzle — TypeUnsolvedPuzzle <: PuzzleErrorThrown when show_solution or truthtable is called on an unsolved puzzle.
ZebraPuzzles.UnsolvedZebraPuzzle — TypeUnsolvedZebraPuzzle{K,N,Attrs} <: ZebraPuzzle{K,N,Attrs}A ZebraPuzzle that is created without a solution table. It has a known set of attributes and possibly a list of clues.
See also fill_clues!, solve!
ZebraPuzzles.ZebraPuzzle — TypeZebraPuzzle{K,N,Attrs}Represents a Zebra Puzzle, a type of logic puzzle where one deduces relationships between different subjects and their attributes based on a set of clues.
Type Parameters
K: number of puzzle subjects and therefore also the number of distinct values of every attribute typeN: number of attributes of per subjectAttrs: attribute typesTuple{Attr1,Attr2,...}whereAttrican be e.g.Hat,Nationality,PersonorHouse. It has lengthN.
ZebraPuzzles.ZebraPuzzle — MethodZebraPuzzle(attribute_variants...; clues=Clue[], questions=Question[])::UnsolvedZebraPuzzleConstruct an unsolved zebra puzzle. The puzzle holds the variants of the attributes and a solution table with missing values. Clues can be later added by add_clues!.
julia> ZebraPuzzle(
House => ("red", "blue", "green"),
Person => ("Martin", "David", "Lucas"),
Pet => ("Weasel", "Cow", "Mammoth")
)
UnsolvedZebraPuzzle{3, 3, Tuple{House, Person, Pet}} with no clues
┌──────────────────────────────┐
│ House red, blue, green │
├──────────────────────────────┤
│ Person Martin, David, Lucas │
├──────────────────────────────┤
│ Pet Weasel, Cow, Mammoth │
└──────────────────────────────┘ZebraPuzzles.ZebraPuzzle — MethodZebraPuzzle(linked_attributes...; clues=Clue[], questions=Question[])::SolvedZebraPuzzleConstruct a solved zebra puzzle with the solution table zpairs with no clues. Clues can be later added by add_clues!.
julia> ZebraPuzzle(
(Person("Martin"), Hat("fedora"), Nationality("czech")),
(Person("David"), Hat("beanie"), Nationality("spanish")),
(Person("Lucas"), Hat("cap"), Nationality("german")),
(Person("Lucy"), Hat("baret"), Nationality("american"))
)
SolvedZebraPuzzle{4, 3, Tuple{Person, Hat, Nationality}} with no clues
┌─────────────────────────────┐
│ Nationality Hat Person │
├─────────────────────────────┤
│ czech fedora Martin │
├─────────────────────────────┤
│ spanish beanie David │
├─────────────────────────────┤
│ german cap Lucas │
├─────────────────────────────┤
│ american baret Lucy │
└─────────────────────────────┘Base.rand — MethodRandom.rand(UnsolvedZebraPuzzle{K,N}; clues=true)Generate a random unsolved zebra puzzle with K subjects each of which has N attributes.
If clues is true, the puzzle is filled with a minimal set of random clues ensuring that it is solvable. If question is true, the puzzle is accompanied with a random question about the puzzle.
DataAPI.ncol — Methodncol(z::ZebraPuzzle)Returns the number of columns of the solution table for the puzzle z which is equal to the number of attributes linked to each of the puzzle subjects.
julia> ZP.ncol(ZP.EINSTEINS_ZEBRA)
5DataAPI.nrow — Methodnrow(z::ZebraPuzzle)Returns the number of rows of the solution table for the puzzle z which is equal to the number of subjects the puzzle is about.
julia> ZP.nrow(ZP.EINSTEINS_ZEBRA)
5ZebraPuzzles.add_clue! — Methodadd_clue!(z::ZebraPuzzle, c::Clue; ischecked=false, kwargs...)Adds a single clue c to the ZebraPuzzle z.
The clue is checked (See check) prior to its addition if ischecked == false. Any other keyword arguments are passed to check.
See also add_clues!
julia> z = ZP.SIMPLE_ZEBRA |> deepcopy;
julia> add_clue!(z, Clue(House("green"), Smoke("Parliaments")))
SolvedZebraPuzzle{3, 4, Tuple{House, Nationality, Smoke, Drink}} with 1 clues
┌────────────────────────────────────────────────┐
│ House Nationality Smoke Drink │
├────────────────────────────────────────────────┤
│ red Englishman Old Gold tea │
├────────────────────────────────────────────────┤
│ green Japanese Parliaments coffee │
├────────────────────────────────────────────────┤
│ ivory Spaniard Lucky Strike orange juice │
└────────────────────────────────────────────────┘
clues:
1) House("green") ⟹ Smoke("Parliaments")ZebraPuzzles.add_clue! — Methodadd_clue!(z::ZebraPuzzle)Adds a randomly generated clue to the ZebraPuzzle z.
julia> z = ZP.SIMPLE_ZEBRA |> deepcopy;
julia> add_clue!(z)
SolvedZebraPuzzle{3, 4, Tuple{House, Nationality, Smoke, Drink}} with 1 clues
┌────────────────────────────────────────────────┐
│ House Nationality Smoke Drink │
├────────────────────────────────────────────────┤
│ red Englishman Old Gold tea │
├────────────────────────────────────────────────┤
│ green Japanese Parliaments coffee │
├────────────────────────────────────────────────┤
│ ivory Spaniard Lucky Strike orange juice │
└────────────────────────────────────────────────┘
clues:
1) Pos[Nationality("Japanese")] < Pos[Smoke("Lucky Strike")]ZebraPuzzles.add_clues! — Methodadd_clues!(z::ZebraPuzzle, cs::Vector{<:Clue}; ischecked=false, kwargs...)Adds clues from cs to the ZebraPuzzle z.
The clues are checked (See check) prior to their addition if ischecked == false. Any other keyword arguments are passed to check.
julia> z = ZP.SIMPLE_ZEBRA |> deepcopy;
julia> add_clues!(z, [
Clue(House("red"), Smoke("Old Gold")),
Clue(Nationality("Spaniard"), Drink("orange juice"))
])
SolvedZebraPuzzle{3, 4, Tuple{House, Nationality, Smoke, Drink}} with 2 clues
┌────────────────────────────────────────────────┐
│ House Nationality Smoke Drink │
├────────────────────────────────────────────────┤
│ red Englishman Old Gold tea │
├────────────────────────────────────────────────┤
│ green Japanese Parliaments coffee │
├────────────────────────────────────────────────┤
│ ivory Spaniard Lucky Strike orange juice │
└────────────────────────────────────────────────┘
clues:
1) House("red") ⟹ Smoke("Old Gold")
2) Nationality("Spaniard") ⟹ Drink("orange juice")ZebraPuzzles.add_question! — Methodadd_question!(z::ZebraPuzzle, q::Question; ischecked=false)Add a question q to the ZebraPuzzle z.
The question is checked (See check) prior to its addition if ischecked == false. Any other keyword arguments are passed to check.
julia> z = ZP.SIMPLE_ZEBRA |> deepcopy;
julia> add_question!(z, AttributeQuestion{Drink}(House("red")))
SolvedZebraPuzzle{3, 4, Tuple{House, Nationality, Smoke, Drink}} with no clues and 1 questions
┌────────────────────────────────────────────────┐
│ House Nationality Smoke Drink │
├────────────────────────────────────────────────┤
│ red Englishman Old Gold tea │
├────────────────────────────────────────────────┤
│ green Japanese Parliaments coffee │
├────────────────────────────────────────────────┤
│ ivory Spaniard Lucky Strike orange juice │
└────────────────────────────────────────────────┘
questions:
1) Drink[House("red")]?ZebraPuzzles.answer — Methodanswer(q::Question, z::ZebraPuzzle)Give the answer to the question q in the ZebraPuzzle z.
julia> aq = AttributeQuestion{Smoke}(Nationality("Englishman"));
julia> ZP.answer(aq, ZP.EINSTEINS_ZEBRA)
Smoke("Old Gold")
julia> pq = PositionQuestion(Drink("water"));
julia> ZP.answer(pq, ZP.EINSTEINS_ZEBRA)
1ZebraPuzzles.answer — Methodanswer(puzzle::ZebraPuzzle)Give the phrases for the answers to the puzzle questions.
julia> puz = ZP.EINSTEINS_ZEBRA |> deepcopy;
julia> add_question!(puz); puz.questions[1]
PositionQuestion{House}(House("ivory"))
julia> ZP.answer(puz)
"The ivory house is in the fourth position."ZebraPuzzles.attr_types — Methodattr_types(q::Question) => VectorGet the attribute types that the question q is about
Every subtype of Question must implement this interface.
julia> aq = AttributeQuestion{Smoke}(Nationality("Englishman"));
julia> ZP.attr_types(aq)
2-element Vector{DataType}:
Smoke
NationalityZebraPuzzles.attributes — Methodattributes(c::Clue)Retrieve the attributes that are involved in a clue.
This is mainly for checking for the correctness of the clue.
julia> ZP.EINSTEINS_ZEBRA.clues[1] |> string |> print
Nationality("Englishman") ⟹ House("red")
julia> attributes(ZP.EINSTEINS_ZEBRA.clues[1])
(Nationality("Englishman"), House("red"))ZebraPuzzles.attributes — Methodattributes(q::Question) => Vector{Attribute}Get the attributes that the question q is about
Every subtype of Question must implement this interface.
julia> aq = AttributeQuestion{Smoke}(Nationality("Englishman"));
julia> ZP.attributes(aq)
1-element Vector{Nationality}:
Nationality("Englishman")ZebraPuzzles.attributes — Methodattributes(puzzle, s::Attribute)
attributes(puzzle, s::Not{Attribute})Get the other attributes of the puzzle that are linked to the attribute s
julia> attributes(ZP.EINSTEINS_ZEBRA, Smoke("Old Gold"))
4-element Vector{ZebraPuzzles.Attribute}:
House("red")
Nationality("Englishman")
Drink("milk")
Pet("snails")
julia> attributes(ZP.EINSTEINS_ZEBRA, Not(House("red")))
20-element Vector{ZebraPuzzles.Attribute}:
House("yellow")
House("blue")
House("ivory")
House("green")
Nationality("Norwegian")
Nationality("Ukrainian")
Nationality("Spaniard")
Nationality("Japanese")
Smoke("Kools")
Smoke("Chesterfields")
Smoke("Lucky Strike")
Smoke("Parliaments")
Drink("water")
Drink("tea")
Drink("orange juice")
Drink("coffee")
Pet("fox")
Pet("horse")
Pet("dog")
Pet("zebra")ZebraPuzzles.attributes — Methodattributes(puzzle::ZebraPuzzle, S::Not{<:Type{<:Attribute}})Get the attributes of the puzzle other than the given type S.
Every subtype of ZebraPuzzle must implement this interface.
julia> attributes(ZP.EINSTEINS_ZEBRA, Not(Smoke))
20-element Vector{ZebraPuzzles.Attribute}:
House("yellow")
House("blue")
House("red")
House("ivory")
House("green")
Nationality("Norwegian")
Nationality("Ukrainian")
Nationality("Englishman")
Nationality("Spaniard")
Nationality("Japanese")
Drink("water")
Drink("tea")
Drink("milk")
Drink("orange juice")
Drink("coffee")
Pet("fox")
Pet("horse")
Pet("snails")
Pet("dog")
Pet("zebra")ZebraPuzzles.attributes — Methodattributes(puzzle::ZebraPuzzle, S::Type{<:Attribute})Get the attributes of the puzzle of the given type s.
Every subtype of ZebraPuzzle must implement this interface.
julia> attributes(ZP.EINSTEINS_ZEBRA, Smoke)
5-element Vector{Smoke}:
Smoke("Kools")
Smoke("Chesterfields")
Smoke("Old Gold")
Smoke("Lucky Strike")
Smoke("Parliaments")ZebraPuzzles.attributes — Methodattributes(puzzle::ZebraPuzzle)Get all the attributes of the puzzle
Every subtype of ZebraPuzzle must implement this interface.
julia> attributes(ZP.EINSTEINS_ZEBRA)
25-element Vector{ZebraPuzzles.Attribute}:
House("yellow")
House("blue")
House("red")
House("ivory")
House("green")
Nationality("Norwegian")
Nationality("Ukrainian")
Nationality("Englishman")
Nationality("Spaniard")
Nationality("Japanese")
⋮
Drink("tea")
Drink("milk")
Drink("orange juice")
Drink("coffee")
Pet("fox")
Pet("horse")
Pet("snails")
Pet("dog")
Pet("zebra")ZebraPuzzles.attrtypes — Methodattrtypes(puzzle)Get the attribute parameters of the puzzle
julia> ZP.attrtypes(ZP.EINSTEINS_ZEBRA)
5-element Vector{DataType}:
House
Nationality
Drink
Smoke
PetZebraPuzzles.check — Methodcheck(puzzle::SolvedZebraPuzzle, c::Clue; <kwargs>)Implementation of the check interface for ZebraPuzzle that includes a clue validity check against the solution table.
See also check_valid
Keyword Arguments
Keyword arguments control which of the checks are controlled for the clue.
duplicates=true– clue is not already included in the puzzle?valid=true– clue is valid under the solution table?types=true– clue has valid attribute types for the puzzle?variants=true– clue has valid attribute variants ("names") for the puzzle?implied=true– clue is not implied by the clues in the puzzle?minimal=true– clue is needed for the solution? I.e. current clues do not suffice for getting the full solution.
ZebraPuzzles.check — Methodcheck(puzzle::SolvedZebraPuzzle, c::Clue; <kwargs>)Implementation of the check interface for ZebraPuzzle that includes a clue validity check against the solution table.
See also check_valid
Keyword Arguments
Keyword arguments control which of the checks are controlled for the clue.
duplicates=true– clue is not already included in the puzzle?valid=true– clue is not in contradiction with the puzzle clues?types=true– clue has valid attribute types for the puzzle?variants=true– clue has valid attribute variants ("names") for the puzzle?implied=true– clue is not implied by the clues in the puzzle?minimal=true– clue is needed for the solution? I.e. current clues do not suffice for getting the full solution.
ZebraPuzzles.check — Methodcheck(z::ZebraPuzzle, c::Clue)Check that the clue c is compatible with the puzzle z.
All subtypes of ZebraPuzzle must implement this interface
See also check_attrtype, check_attrvariant, check_minimal, check_implied
julia> all(ZP.check.(fill(ZP.EINSTEINS_ZEBRA), ZP.EINSTEINS_ZEBRA.clues; duplicates=false, minimal=false, implied=false))
trueZebraPuzzles.check — Methodcheck(z::ZebraPuzzle, q::Question, types=true, variants=true)Implementation of the check interface for ZebraPuzzle that checks the questions q for valid types and attribute variants.
Keyword Arguments
Keyword arguments control which of the checks are controlled for the clue.
types=true– question has valid attribute types for the puzzle?variants=true– question has valid attribute variants ("names") for the puzzle?
ZebraPuzzles.check_attrtype — Methodcheck_attrtype(puzzle, s::Type{<:Attribute}; throw_error=true)::Bool
check_attrtype(puzzle, s::Attribute)Check that the attribute of type s is a valid attribute type in the puzzle
julia> ZP.check_attrtype(ZP.EINSTEINS_ZEBRA, House)
true
julia> ZP.check_attrtype(ZP.EINSTEINS_ZEBRA, House("red"))
true
julia> ZP.check_attrtype(ZP.EINSTEINS_ZEBRA, House("pink")) # !!! This only checks the type !!!
true
julia> ZP.check_attrtype(ZP.EINSTEINS_ZEBRA, Hat)
ERROR: AttributeTypeError: attribute type `Hat` is invalid for the given puzzle. Valid types are `DataType[House, Nationality, Drink, Smoke, Pet]`
[...]ZebraPuzzles.check_attrvariant — Methodcheck_attrvariant(puzzle, s::Attribute; throw_error=true)::BoolCheck that the attribute variant s is included in the puzzle.
ZebraPuzzles.check_duplicate — Methodcheck_duplicate(puzzle, c::Clue; throw_error=true)::BoolCheck that the clue c is not already included in the puzzle.
ZebraPuzzles.check_implied — Functioncheck_implied(puzzle, c::Clue, exprs::AttributeExprs=AttributeExprs(puzzle); throw_error=true)::BoolCheck that the clue c is not implied by the rules and clues already included in the puzzle.
julia> z = ZebraPuzzle(Drink => ("coffee", "tea"), Smoke => ("Old Gold", "Chesterfields"), House => ("green", "red"));
julia> ZP.add_clue!(z, Clue(House("red"), Smoke("Old Gold")));
julia> ZP.add_clue!(z, Clue(Smoke("Old Gold"), Drink("tea")))
UnsolvedZebraPuzzle{2, 3, Tuple{Drink, Smoke, House}} with 2 clues
┌────────────────────────────────┐
│ Drink coffee, tea │
├────────────────────────────────┤
│ Smoke Old Gold, Chesterfields │
├────────────────────────────────┤
│ House green, red │
└────────────────────────────────┘
clues:
1) House("red") ⟹ Smoke("Old Gold")
2) Smoke("Old Gold") ⟹ Drink("tea")
julia> ZP.check_implied(z, AbsolutePosition(House("red"), 1)) # independent clue
true
julia> ZP.check_implied(z, Clue(Drink("tea"), House("red"))) # (red => old gold) ∧ (old gold => tea) => (red => tea)
ERROR: ImpliedClue: clue Drink("tea") ⟹ House("red") is implied by the rules and clues already included in the puzzle.
[...]ZebraPuzzles.check_minimal — Functioncheck_minimal(puzzle, c::Clue, exprs::AttributeExprs=AttributeExprs(puzzle); throw_error=true)::BoolCheck that the clue c is not breaking the minimality of the puzzle clue set. I.e. that adding the additional clue is not necessary for finding the solution.
ZebraPuzzles.check_valid — Functioncheck_valid(z::UnsolvedZebraPuzzle, c::Clue, exprs=AttributeExprs(z); throw_error=true)::BoolValidate the truth of the clue under the assertion of puzzle rules and the current puzzle clues.
julia> puzzle = ZP.UNSOLVED_EINSTEINS_ZEBRA |> deepcopy;
julia> empty!(puzzle.clues)
Clue[]
julia> add_clue!(puzzle, Clue(House("blue"), Pet("horse")))
UnsolvedZebraPuzzle{5, 5, Tuple{Drink, House, Nationality, Pet, Smoke}} with 1 clues
┌────────────────────────────────────────────────────────────────────────┐
│ Drink coffee, milk, orange juice, tea, water │
├────────────────────────────────────────────────────────────────────────┤
│ House blue, green, ivory, red, yellow │
├────────────────────────────────────────────────────────────────────────┤
│ Nationality Englishman, Japanese, Spaniard, Ukrainian, Norwegian │
├────────────────────────────────────────────────────────────────────────┤
│ Pet dog, horse, snails, zebra, fox │
├────────────────────────────────────────────────────────────────────────┤
│ Smoke Chesterfields, Lucky Strike, Old Gold, Parliaments, Kools │
└────────────────────────────────────────────────────────────────────────┘
clues:
1) House("blue") ⟹ Pet("horse")
julia> ZP.check_valid(puzzle, Clue(House("red"), Pet("horse")))
ERROR: ConflictingClue: clue House("red") ⟹ Pet("horse") is in conflict with the current rules/clues in the puzzle.
[...]
julia> ZP.check_valid(puzzle, Clue(House("blue"), Pet("horse")))
true
julia> ZP.check_valid(puzzle, Clue(House("ivory"), Pet("horse")); throw_error=false)
falseZebraPuzzles.check_valid — Methodcheck_valid(z::SolvedZebraPuzzle, c::Clue; throw_error=true)::BoolValidate the truth of the clue against the solution table of the puzzle.
ZebraPuzzles.col — Methodcol(S::Type{<:Attribute})
col(s::Attribute)Get the name of the column of attribute s / of the type S in the solution table.
julia> ZP.col(House("red"))
:House
julia> ZP.col(House)
:HouseZebraPuzzles.fill_clues! — Methodfill_clues!(puzzle::ZebraPuzzle)Add random clues until the puzzle has a minimal set and ensures a unique solution.
julia> z = ZebraPuzzle(
Drink => ("coffee", "tea"),
Smoke => ("Old Gold", "Chesterfields"),
House => ("green", "red")
);
julia> ZP.fill_clues!(z)
UnsolvedZebraPuzzle{2, 3, Tuple{Drink, Smoke, House}} with 3 clues
┌────────────────────────────────┐
│ Drink coffee, tea │
├────────────────────────────────┤
│ Smoke Old Gold, Chesterfields │
├────────────────────────────────┤
│ House green, red │
└────────────────────────────────┘
clues:
1) Pos[Drink("coffee")] > Pos[Drink("tea")]
2) Pos[House("green")] == 2
3) Drink("tea") ⟹ ¬Smoke("Chesterfields")ZebraPuzzles.has_unique_solution — Functionhas_unique_solution(z::ZebraPuzzle)Check whether the clues of the puzzle lead to one unique solution.
julia> ZP.has_unique_solution(ZP.UNSOLVED_EINSTEINS_ZEBRA)
trueZebraPuzzles.id — Methodid(a::Attribute)Satisfiability variable name.
ZebraPuzzles.indexof — Methodindexof(z::ZebraPuzzle, A::Type{<:Attribute})
indexof(z::ZebraPuzzle, a::Attribute)Return the internal index of the attribute / attribute type
This internal index is used indexing into the AttributeExprs of the puzzle, which holds the Satisfiability.jl variable used for solving the puzzle.
Every subtype of ZebraPuzzle must implement this interface.
julia> ZP.indexof(ZP.EINSTEINS_ZEBRA, Smoke("Chesterfields"))
(2, 3)
julia> ZP.indexof(ZP.EINSTEINS_ZEBRA, Smoke)
3ZebraPuzzles.issolved — Methodissolved(puzzle::UnsolvedZebraPuzzle)Check whether the puzzle was solved by solve! and has a solution without missing keys.
julia> z = ZP.UNSOLVED_EINSTEINS_ZEBRA |> deepcopy;
julia> ZP.issolved(z)
false
julia> solve!(z);
julia> ZP.issolved(z)
trueZebraPuzzles.nattr — Methodnattr(z::ZebraPuzzle)Returns the total number of attributes of the puzzle z which is equal to nrow(z) * ncol(z)
julia> ZP.nattr(ZP.EINSTEINS_ZEBRA)
25ZebraPuzzles.nclue — Methodnclue(puzzle)Get the number of clues of the puzzle.
ZebraPuzzles.nquestion — Methodnquestion(puzzle)Get the number of questions of the puzzle.
ZebraPuzzles.phrase — Methodphrase(q::Question) => StringGet a string representation of the question q
Every subtype of Question must implement this interface
julia> aq = AttributeQuestion{Smoke}(Nationality("Englishman"));
julia> ZP.phrase(aq)
"What cigarette brand does the Englishman prefer?"ZebraPuzzles.riddle — Methodriddle(puzzle::ZebraPuzzle; <kwargs>)Return the string of the zebra puzzle introduction and clues in natural language.
The string uses markdown for bullet points or numbers and prints the Markdown using the context IO.
If introduction=false is (default true) the introduction such as "There are five houses." is skipped. If bulletpoints=true, the clues are prefixed with "- " and if numbers=true the clues are prefixed with "i)". If both are false the clues only have a space between them.
If quiet=true the riddle is not printed but only returned as a string.
See also solve!
ZebraPuzzles.rules — Methodrules(exprs::AttributeExprs)::BoolExpr
rules(puzzle::ZebraPuzzle)::Tuple{AttributeExprs, BoolExpr}Generate the zebra puzzle Satisfiability.jl rules for the attributes return the rule assertion BoolExpr. If a ZebraPuzzle is passed, it first generates the AttributeExprs and then returns the (exprs, rules) tuple.
julia> z = ZebraPuzzle(Drink => ("coffee", "tea"), House => ("green", "red"))
UnsolvedZebraPuzzle{2, 2, Tuple{Drink, House}} with no clues
┌────────────────────┐
│ Drink coffee, tea │
├────────────────────┤
│ House green, red │
└────────────────────┘
julia> exprs, rules = ZP.rules(z);
julia> rules
4-element Vector{Satisfiability.BoolExpr}:
and_4a3b8ce4f0c5932b
| leq_13a07d362557ecbd
| | const_1 = 1
| | Drink_tea
| leq_5f49e791afe246a5
| | const_1 = 1
| | House_green
| leq_a27b1741a1f9008e
| | const_1 = 1
| | House_red
| leq_e94b7d8032d90182
| | const_1 = 1
| | Drink_coffee
and_9b58b2ca1f5f8553
| leq_20b3312716ae8085
| | House_red
| | const_2 = 2
| leq_2349e5270bfc6484
| | Drink_tea
| | const_2 = 2
| leq_4674d87e645a7ee8
| | Drink_coffee
| | const_2 = 2
| leq_e343fa1b3fccf18b
| | House_green
| | const_2 = 2
distinct_f63a38a3190ef4f4
| Drink_coffee
| Drink_tea
distinct_7f6fa8abcc538373
| House_green
| House_redZebraPuzzles.show_solution — Methodshow_solution(puzzle::UnsolvedZebraPuzzle)If the puzzle was solved by solve!, converts the puzzle to a SolvedZebraPuzzle shows it.
julia> puzzle = ZP.UNSOLVED_EINSTEINS_ZEBRA |> deepcopy;
julia> ZP.show_solution(puzzle)
ERROR: UnsolvedPuzzle: puzzle is not solved
[...]
julia> solve!(puzzle);
julia> ZP.show_solution(puzzle)
SolvedZebraPuzzle{5, 5, NTuple{5, Union{Missing, ZebraPuzzles.Attribute}}}
┌──────────────────────────────────────────────────────────┐
│ Drink House Nationality Pet Smoke │
├──────────────────────────────────────────────────────────┤
│ water yellow Norwegian fox Kools │
├──────────────────────────────────────────────────────────┤
│ tea blue Ukrainian horse Chesterfields │
├──────────────────────────────────────────────────────────┤
│ milk red Englishman snails Old Gold │
├──────────────────────────────────────────────────────────┤
│ orange juice ivory Spaniard dog Lucky Strike │
├──────────────────────────────────────────────────────────┤
│ coffee green Japanese zebra Parliaments │
└──────────────────────────────────────────────────────────┘ZebraPuzzles.solve! — Methodsolve!(puzzle::UnsolvedZebraPuzzle)Solve the zebra puzzle and fill in the solution table.
See also show_solution
julia> puz = ZP.UNSOLVED_EINSTEINS_ZEBRA |> deepcopy;
julia> solve!(puz);
julia> puz |> ZP.show_solution
SolvedZebraPuzzle{5, 5, NTuple{5, Union{Missing, ZebraPuzzles.Attribute}}}
┌──────────────────────────────────────────────────────────┐
│ Drink House Nationality Pet Smoke │
├──────────────────────────────────────────────────────────┤
│ water yellow Norwegian fox Kools │
├──────────────────────────────────────────────────────────┤
│ tea blue Ukrainian horse Chesterfields │
├──────────────────────────────────────────────────────────┤
│ milk red Englishman snails Old Gold │
├──────────────────────────────────────────────────────────┤
│ orange juice ivory Spaniard dog Lucky Strike │
├──────────────────────────────────────────────────────────┤
│ coffee green Japanese zebra Parliaments │
└──────────────────────────────────────────────────────────┘If the puzzle is not solvable an error is thrown
julia> puz = ZP.UNSOLVED_EINSTEINS_ZEBRA |> deepcopy;
julia> pop!(puz.clues) |> ZP.phrase
"The Norwegian lives immediately next to the blue house."
julia> push!(puz.clues, ExactRelativePosition(Nationality("Norwegian"), House("yellow"), 1));
julia> solve!(puz);
ERROR: Failed to find a solution. Got `UNSAT` from the solver.
[...]ZebraPuzzles.titlecase — Methodtitlecase(s::String)Upper-case the first letter of the string
julia> ZP.titlecase("red house")
"Red house"
julia> ZP.titlecase("apple")
"Apple"ZebraPuzzles.toclue — Methodtoclue(q::Question, puz::ZebraPuzzle)Get the clue that corresponds to the question q in the puzzle.
Throws an UnsolvedPuzzle error if the puzzle is not solved.
julia> ZP.toclue(AttributeQuestion{Smoke}(Nationality("Englishman")), ZP.EINSTEINS_ZEBRA)
PositiveClue{Nationality, Smoke}(Nationality("Englishman"), Smoke("Old Gold"))
julia> ZP.toclue(PositionQuestion(Smoke("Parliaments")), ZP.EINSTEINS_ZEBRA)
AbsolutePosition{Smoke, Int64}(Smoke("Parliaments"), 5, 5)ZebraPuzzles.truthtable — Methodtruthtable(puzzle::ZebraPuzzle)Get the truth table of the puzzle.
Throws a PuzzleError if the puzzle is not solved yet.
ZebraPuzzles.variants — Methodvariants(A::Type{<:Attribute})Returns known variants of the attribute type A.
These are used for generating random puzzles.