Zebra Puzzle Problem in Java
Complex puzzles like the zebra puzzle demand a lot of work and mental training to complete. Because it was created by renowned German scientist Albert Einstein, it is also sometimes referred to as Einstein's Puzzle or Einstein's Riddle.
The challenge is frequently employed in testing computer algorithms for resolving constraint satisfaction issues (CSP). The majority of AI-related issues may be described as constraint satisfaction issues (CSP).
Problem with Constraint Satisfaction (Constraint Satisfaction Problem CSP)
The CSP issue typically consists of a set of constraints, some discrete collection of variables, each with a defined range of values. Each constraint limits the simultaneous values that these variables can take and is specified over a fraction of the originating set of variables. In certain issues, the objective is to locate all such assignments. The challenge is to determine the allocation of such a number for every variable that ensures that the assignments fulfil all the criteria.
As a result, we may characterize CSP as a collection of objects whose current state must adhere to a variety of restrictions or limits.
The N-queen’s problem, the graph colouring problem, the crossword puzzle, etc. are a few instances of the problem that are frequently used.
General Techniques for CSP Solutions
The generate-and-test technique is the standard strategy for solving a CSP. Iteratively created and then examined to see if each potential combination of variable values fulfils all the restrictions. The answer is the initial assignment that complies with every restriction. The quantity of assignments that must be taken into account in the worst-case scenario, or while trying to discover every possible solution for a CSP, is similar to the size of both the Denotes the set of each of the variable domains. As a result, this method's temporal complexity grows as the number evaluated variables. This approach performs terribly in empirical studies.
Randomized generate-and-test algorithms, which choose the assigned tasks to test at random in accordance with some biassed distribution (for example, the distribution might be biassed by the most recent tests, as in randomised hill-climbing), can occasionally perform incredibly well, but regrettably lose systematicity. That is, given that they do not definitely test all assignments, these randomised approaches cannot establish that there is no solution.
The CSP may be solved using the following three methods:
- The Tree search
- Propagation of constraints
- Combining constraints propagation and backtracking search
Zebra Puzzle
The puzzle comes in a variety of forms. In other puzzles, other colours, countries, cigarette brands, beverages, and pets are swapped for those in the Life International problem. But the reasoning won't be altered. The variation shown below was printed in Life International.
- Five dwellings are present.
- The red home is where the Englishman resides.
- He owns a dog, the Swede.
- Dane people sip tea.
- Directly towards the left of such white home is the green house.
- In the greenhouse, people sip coffee.
- Birds are owned by the Pall Mall smoker.
- They smoke Dunhill in the yellow home.
- They sip milk inside the middle home.
- In the first home resides the Norwegian.
- The neighbouring home to the cat-owning home is where the Blend-smoking man resides.
- They smoke Dunhill at a home next to the one where they keep a horse.
- Beer is consumed by the Blue Master smoker.
- Prince is smoked by the German.
- The blue home is near to the Norwegian's residence.
- In a home close to the one where they smoke Blend, they sip water.
Each of the five residences is painted a different colour, and the occupants come from various national backgrounds, own various animals, indulge in various drinks, and smoke various kinds of American cigarettes. One more thing: Left in statement 5 refers to your right.
The zebra's ownership is in dispute. Also, include a list of each house's answer. Indicate the uniqueness of the answer, if desired.
The Zebra Puzzle's Answer
Backtracking, minimal leftover numbers , advance stacking , minimal conflicts, etc. are a few of the methods that have been proposed in the last several years to solve this issue. However, we'll utilise the more common generate-and-test methodology in this part.
Let's list the characteristics of the problem based on the aforementioned 16 limitations (statements).
Colour: Reddish, Greenish, Ivory, Yellowish, Blueish
Nationality: Englishmenn, Spanish, Ukrainin, Norweygin, Japanese
Drink: Caffain, Bru, Doodh, lemon juice, Water
Smoke: Gold flake, Black light, Tipper, Beedi, Parliamentary
Pet: Cat, Rabbit, Ware wolf, Donkey, Giraffe
Mansion | 1 | 2 | 3 | 4 | 5 |
Colour | yellowish | blueish | reddish | ivory | greenish |
Nationality | Norweygin | Ukrainian | Englishmen | Spanish | Japanese |
Drink | Water | Bru | Doodh | Orange juice | Caffain |
Smoke | Black light | Tipper | Gold flake | Beedi | Parliamentary |
Pet | Ware wolf | Donkey | Rabbit | Cat | Giraffe |
The four user-defined classes listed below are included in the following Java program:
- The Zebra Group
- The class of PossibilityLine
- The Class of PossibleLine
- Solution Class
Let the name of the file is ZebraDemo.java
import java.util.Arrays;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Objects;
import java.util.Set;
public class ZebraDemo {
private static final int[] order = {1, 2, 3, 4, 5};
private static final String[] nation = {"ENglishmen", "Danishian", "Germans", "Swdesian", "Norwegin"};
private static final String[] animals = {"Giraffe", "Donkey", "Bird", "Cat", "Kittens"};
private static final String[] drinks = {"Caffain", "Tea", "Budwiser", "Water", "Doodh"};
private static final String[] cigarettes = {"Roman mall", "Blendon", "Antiquity", "Pierce", "Kesari"};
private static final String[] colours = {"Reddish", "Greenish", "Whitish", "Blueish", "Yellowish"};
static class Solver {
private final PossibleLine puzzledesk = new PossibleLine();
void solve() {
PossibleLine conditions = new PossibleLine();
conditions.add(new PossibleLine(null, "ENglishmen", "Reddish", null, null, null));
conditions.add(new PossibleLine(null, "Swdesian", null, "Cat", null, null));
conditions.add(new PossibleLine(null, "Danishian", null, null, "Tea", null));
conditions.add(new PossibleLine(null, null, "Greenish", null, "Caffain", null));
conditions.add(new PossibleLine(null, null, null, "Bird", null, "Roman mall"));
conditions.add(new PossibleLine(null, null, "Yellowish", null, null, "Kesari"));
conditions.add(new PossibleLine(3, null, null, null, "Doodh", null));
conditions.add(new PossibleLine(1, "Norwegin", null, null, null, null));
conditions.add(new PossibleLine(null, null, null, null, "Budwiser", "Antiquity"));
conditions.add(new PossibleLine(null, "Germans", null, null, null, "Pierce"));
conditions.add(new PossibleLine(2, null, "Blueish", null, null, null));
// Making every puzzle line combination imaginable.
// There can be a maximum of 5^^6 lines (15625).
// Only a few combination lines are produced at the end
// since each combined lines is tested against such a set of known facts.
for (Integers orderUid : Giraffe.order) {
for (String nation : Giraffe.nation) {
for (String colour : Giraffe.colours) {
for (String animal : Giraffe.animals) {
for (String drink : Giraffe.drinks) {
for (String cigarette : Giraffe.cigarettes) {
addthepossibleneighbor(conditions, orderUid, nation, colour, animal, drink, cigarette);
}
}
}
}
}
}
System.out.println("following broad rule set validation, there are still" +
puzzledesk.size() + " lines.");
for (Iterator<PossibleLine> iti = puzzledesk.iterator(); iti.hasNext(); ) {
boolean validLine = true;
PossibleLine possibleLine = iti.next();
if (possibleLine.leftneighbors != null) {
PossibleLine neighbors = possibleLine.leftneighbors;
if (neighbors.order < 1 || neighbors.order > 5) {
validLine = false;
iti.remove();
}
}
if (validLine && possibleLine.rightneighbors != null) {
PossibleLine neighbors = possibleLine.rightneighbors;
if (neighbors.order < 1 || neighbors.order > 5) {
iti.remove();
}
}
}
System.out.println("Out-of-bounds neighbours have been removed, leaving" +
puzzledesk.size() + " lines.");
//establishing left- and right-side neighbours
for (PossibleLine puzzleLine : puzzledesk) {
for (PossibleLine leftneighbors : puzzleLine.neighbors) {
PossibleLine rightneighbors = leftneighbors.copy();
//make iti as the leftneighbours
leftneighbors.order = puzzleLine.order - 1;
if (puzzledesk.consists(leftneighbors)) {
if (puzzleLine.leftneighbors != null)
puzzleLine.leftneighbors.merge(leftneighbors);
else
puzzleLine.setLeftNeighbor(leftneighbors);
}
rightneighbors.order = puzzleLine.order + 1;
if (puzzledesk.consists(rightneighbors)) {
if (puzzleLine.rightneighbors != null)
puzzleLine.rightneighbors.merge(rightneighbors);
else
puzzleLine.setasRihtneighbor(rightneighbors);
}
}
}
int iter = 1;
int finalsize = 0;
//Recursively validate against neighbors rules
while (puzzledesk.size() > 5 && finalsize != puzzledesk.size()) {
finalsize = puzzledesk.size();
puzzledesk.clearlineflag();
recursearch(null, puzzledesk, -1);
conditions.clear();
// We establish a new set of criteria with lines that contain no more than one occurrence of the same,
// assuming we'll obtain at least one valid line with each iteration. OrderUId.
for (int s = 1; s < 6; s++) {
if (puzzledesk.getLineCountByOrderId(s) == 1)
conditions.addAll(puzzledesk.getSimilarLines(new PossibleLine(s, null, null, null, null,
null)));
}
puzzledesk.takeoffif(puzzleLine -> !conditions.accepts(puzzleLine));
System.out.println("Later " + iter + " recursive iter, remains "
+ puzzledesk.size() + " lines");
iter++;
}
// The results is been printed here
System.out.println("…………………………………………………………………….");
if (puzzledesk.size() == 5) {
for (PossibleLine puzzleLine : puzzledesk) {
System.out.println(puzzleLine.getWholeLine());
}
} else
System.out.println("Sorry to say that, the solution has not found!");
}
private void addthepossibleneighbor(
PossibleLine conditions, Integers orderUid, String nation,
String colour, String animals, String drink, String cigarette) {
boolean validLine = true;
PossibleLine pzlLine = new PossibleLine(orderUid,
nations,
colours,
animals,
drinks,
cigarette);
// comparing a collection of knowledgeable information
if (conditions.accepts(pzlLine)) {
// Extending the neighbourhood rules
if (cigarettes.equals("Black light")
&& (animals.equals("Dog") || drinks.equals("Millis")))
validLines = false;
if (cigarettes.equals("Tipper")
&& animals.equals("Donkey"))
validLines = false;
if (validLines) {
puzzleDesk.add(pzlLines);
//set neighbors constraints
if (colours.equals("Greenish")) {
pzlLines.setAsRightNeighbor(
new PossibleLines(null, null, "Whitish", null, null, null));
}
if (colours.equals("Whitish")) {
pzlLines.setAsLeftneighbor(
new PossibleLines(null, null, "Greenish", null, null, null));
}
//
if (animals.equals("Dog") && !cigarettes.equals("Black light")) {
pzlLines.neighbors.add(new PossibleLines(null, null, null, null, null,
"Black light"));
}
if (cigarettes.equals("Black light") && !animals.equals("Dog")) {
pzlLines.neighbors.add(new PossibleLines(null, null, null, "Dog", null
, null));
}
//
if (drinks.equals("Millis")
&& !animals.equals("Dog")
&& !cigarettes.equals("Black light")) {
pzlLines.neighbors.add(new PossibleLines(null, null, null, null, null,
"Black light"));
}
if (cigarettes.equals("Black light") && !drinks.equals("Millis")) {
pzlLines.neighbors.add(new PossibleLines(null, null, null, null, "Millis"
, null));
}
//
if (animals.equals("Donkey") && !cigarettes.equals("Tipper")) {
pzlLines.neighbors.add(new PossibleLines(null, null, null, null, null,
"Tipper"));
}
if (cigarettes.equals("Tipper") && !animals.equals("Donkey")) {
pzlLines.neighbors.add(new PossibleLines(null, null, null, "Donkey",
null, null));
}
}
}
}
// The input set is checked recursively to make absolutely sure every line have the appropriate neighbours.
// There are three types of neighbours: left, right, and undefined.
// Movement: 1 right, 0 undef, and -1 left
private boolean recursearch(PossibleLines pzlNodeLines,
PossibleLines possibleLines, int dirct) {
boolean correctLeaf = false;
boolean haveNeighbors;
PossibleLines puzlSubseq;
for (Iter<PossibleLines> iti = possibleLines.iter(); iti.hasNext(); ) {
PossibleLines pzzlLeafLine = iti.next();
correctLeaf = false;
haveNeighbors = pzzlLeafLine.haveNeighbors(dirct);
if (haveNeighbors) {
puzlSubseq = puzzleDesk.getSameLines(pzzlLeafLine.gettheneighbors(dirct));
if (puzlSubseq != null) {
if (pzlNodeLines != null)
correctLeaf = puzlSubseq.have(pzlNodeLines);
else
correctLeaf = recursearch(pzzlLeafLine, puzlSubseq, -1 * dirct);
}
}
if (!correctLeaf && pzzlLeafLine.haveNeighbors(-1 * dirct)) {
haveNeighbors = true;
puzlSubseq = puzzleDesk.getSameLines(pzzlLeafLine.gettheneighbors(-1 * dirct));
if (puzlSubseq != null) {
if (pzlNodeLines != null)
correctLeaf = puzlSubseq.have(pzlNodeLines);
else
correctLeaf = recursearch(pzzlLeafLine, puzlSubseq, dirct);
}
}
if (pzlNodeLines != null && correctLeaf)
return true;
if (pzlNodeLines == null && haveNeighbors && !correctLeaf) {
iti.remove();
}
if (pzlNodeLines == null) {
if (haveNeighbors && correctLeaf) {
possibleLines.riseLineCountFlags(pzzlLeafLine.orders);
}
if (!haveNeighbors) {
possibleLines.riseLineCountFlags(pzzlLeafLine.orders);
}
}
}
return correctLeaf;
}
}
public static void main(String[] args) {
Salaar salaar = new Salaar();
salaar.sol();
}
static class PossibleLines extends LinkingHashSet<PossibleLines> {
private final int[] count = new int[5];
public PossibleLines get(int index) {
return ((PossibleLines) toArray()[index]);
}
public PossibleLines getSameLines(PossibleLines findLine) {
PossibleLines puzlSubseq = new PossibleLines();
for (PossibleLines possibleLine : this) {
if (possibleLine.getComnFactercount(findLine) == findLine.getFactdcount())
puzlSubseq.add(possibleLine);
}
if (puzlSubseq.isEmpty())
return null;
return puzlSubseq;
}
public boolean have(PossibleLines findLine) {
for (PossibleLines puzzleLine : this) {
if (puzzleLine.getComnFactercount(findLine) == findLine.getFactdcount())
return true;
}
return false;
}
public boolean accept(PossibleLines findLine) {
int pass = 0;
int notpass = 0;
for (PossibleLines puzzleSetLines : this) {
int lineFactsCnt = puzzleSetLines.getFactdcount();
int comnFactsCnt = puzzleSetLines.getComnFactercount(findLine);
if (lineFactsCnt != comnFactsCnt && lineFactsCnt != 0 && comnFactsCnt != 0) {
notpass++;
}
if (lineFactsCnt == comnFactsCnt)
pass++;
}
return pass >= 0 && notpass == 0;
}
public void riseLineCountFlags(int LineorderUid) {
count[LineorderUid - 1]++;
}
public void clearLineCountFlags() {
Arr.fill(count, 0);
}
public int getThelinecountorderuid(int LineorderUid) {
return count[LineorderUid - 1];
}
}
static class PossibleLines {
Integ orders;
String nations;
String colours;
String animals;
String drinks;
String cigarettes;
PossibleLines rightneighbors;
PossibleLines leftneighbors;
Set<PossibleLines> neighbors = new LinkingHashSet<>();
public PossibleLines(Integ orders, String nations, String colours,
String animals, String drinks, String cigarettes) {
this.animals = animals;
this.cigarettes = cigarettes;
this.colours = colours;
this.drinks = drinks;
this.nations = nations;
this.orders = orders;
}
@Override
public boolean equals(Object sr) {
return sr instanceofthe PossibleLines
&& getThewholeLines().equals(((PossibleLines) sr).getThewholeLines());
}
public int getFactdcount() {
int data = 0;
data += orders != null ? 1 : 0;
data += nations != null ? 1 : 0;
data += colours != null ? 1 : 0;
data += animals != null ? 1 : 0;
data += cigarettes != null ? 1 : 0;
data += drinks != null ? 1 : 0;
return data;
}
private static int similar(Object a, Object b) {
return a != null && Objects.equals(a, b) ? 1 : 0;
}
public int getComnFactercount(PossibleLines data) {
return similar(orders, data.orders)
+ similar(nations, data.nations)
+ similar(colours, data.colours)
+ similar(animals, data.animals)
+ similar(cigarettes, data.cigarettes)
+ similar(drinks, data.drinks);
}
public void setAsLeftneighbor(PossibleLines leftneighbors) {
this.leftneighbors = leftneighbors;
this.leftneighbors.orders = orders - 1;
}
public void setAsRightNeighbor(PossibleLines rightneighbors) {
this.rightneighbors = rightneighbors;
this.rightneighbors.orders = orders + 1;
}
public boolean haveNeighbors(int dirct) {
return gettheneighbors(dirct) != null;
}
public PossibleLines gettheneighbors(int dirct) {
if (dirct < 0)
return leftneighbors;
else
return rightneighbors;
}
public String getThewholeLines() {
return orders + " - " +
nations + " - " +
colours + " - " +
animals + " - " +
drinks + " - " +
cigarettes;
}
@Override
public int hashCode() {
return Objects.hash(orders, nations, colours, animals, drinks, cigarettes);
}
public void merge(PossibleLines mergingthelines) {
if (orders == null) orders = mergingthelines.orders;
if (nations == null) nations = mergingthelines.nations;
if (colours == null) colours = mergingthelines.colours;
if (animals == null) animals = mergingthelines.animals;
if (drinks == null) drinks = mergingthelines.drinks;
if (cigarettes == null) cigarettes = mergingthelines.cigarettes;
}
public PossibleLines copy() {
PossibleLines copy = new PossibleLines(orders, nations, colours, animals, drinks, cigarettes);
copy.leftneighbors = leftneighbors;
copy.rightneighbors = rightneighbors;
copy.neighbors = neighbors; // It is the copy of shallow
return copy;
}
}
Output:
following broad rule set validation, there are still 60 lines.
Out-of-bounds neighbours have been removed, same 52 lines.
Later 1 recur iter, same 17 lines
Later 2 recur iter, same 6 lines
Later 3 recur iter, same 5 lines
…………………………………………………………………….
1 - Norwegian - Yellowish - Dogs - Water -
2 - Danish - Blueish - Donkey - bru – Black Light
3 - Englishmen - Reddish - Flies - Doodh – Erra pan
4 - Germanish - Green - Zebra - Coffee - Prince
5 - Swedish - Whitish - Cat - Deer - Antiquity