/** * Two ALS are equal if they contain the same indices * * @param o * @return */ @Override public boolean equals(Object o) { if (o == null) { return false; } if (!(o instanceof Als)) { return false; } Als a = (Als) o; return indices.equals(a.indices); }
/** * Creates a step for a W-Wing. <code>cand1</code> is the candidate for which eliminations can be * made, <code>cand2</code> is the connecting candidate. <code>index1</code> and <code>index2 * </code> are the bivalue cells, {@link #wIndex1} and {@link #wIndex2} are the strong link. * <code>elimSet</code> holds all cells where candidates can be eliminated. * * @param cand1 * @param cand2 * @param index1 * @param index2 * @param elimSet * @param onlyOne * @return */ private SolutionStep createWWingStep( int cand1, int cand2, int index1, int index2, SudokuSet elimSet, boolean onlyOne) { globalStep.reset(); globalStep.setType(SolutionType.W_WING); globalStep.addValue(cand1); globalStep.addValue(cand2); globalStep.addIndex(index1); globalStep.addIndex(index2); globalStep.addFin(index1, cand2); globalStep.addFin(index2, cand2); globalStep.addFin(wIndex1, cand2); globalStep.addFin(wIndex2, cand2); for (int i = 0; i < elimSet.size(); i++) { globalStep.addCandidateToDelete(elimSet.get(i), cand1); } SolutionStep step = (SolutionStep) globalStep.clone(); if (onlyOne) { return step; } else { steps.add(step); } return null; }
/** * Computes all the additional fields; is done after the initial search to optimize finding * doubles. * * @param finder */ public void computeFields(SudokuStepFinder finder) { this.buddies = new SudokuSet(); for (int i = 1; i <= 9; i++) { if ((candidates & Sudoku2.MASKS[i]) != 0) { SudokuSet sudokuCandidates = finder.getCandidates()[i]; indicesPerCandidat[i] = new SudokuSet(indices); indicesPerCandidat[i].and(sudokuCandidates); buddiesPerCandidat[i] = new SudokuSet(); Sudoku2.getBuddies(indicesPerCandidat[i], buddiesPerCandidat[i]); buddiesPerCandidat[i].andNot(indices); buddiesPerCandidat[i].and(finder.getCandidates()[i]); buddiesAlsPerCandidat[i] = new SudokuSet(buddiesPerCandidat[i]); buddiesAlsPerCandidat[i].or(indicesPerCandidat[i]); buddies.or(buddiesPerCandidat[i]); } } }
/** * Searches for W-Wings: look for all combinations of bivalue cells with the same candidates. If * one is found and it could theoretically eliminate something, a connecting strong link is * searched for. * * @param onlyOne * @return */ private SolutionStep getWWing(boolean onlyOne) { for (int i = 0; i < sudoku.getCells().length; i++) { if (sudoku.getValue(i) != 0 || sudoku.getAnzCandidates(i) != 2) { continue; } // bivalue cell found short cell1 = sudoku.getCell(i); int cand1 = sudoku.getAllCandidates(i)[0]; int cand2 = sudoku.getAllCandidates(i)[1]; // prepare for elimination checks preCalcSet1.setAnd(Sudoku2.buddies[i], finder.getCandidates()[cand1]); preCalcSet2.setAnd(Sudoku2.buddies[i], finder.getCandidates()[cand2]); // check all other cells for (int j = i + 1; j < sudoku.getCells().length; j++) { if (sudoku.getCell(j) != cell1) { // doesnt fit! continue; } // ok, we have a pair; can anything be eliminated? elimSet.setAnd(preCalcSet1, Sudoku2.buddies[j]); if (!elimSet.isEmpty()) { // check for W-Wing for candidate cand1 SolutionStep step = checkLink(cand1, cand2, i, j, elimSet, onlyOne); if (onlyOne && step != null) { return step; } } elimSet.setAnd(preCalcSet2, Sudoku2.buddies[j]); if (!elimSet.isEmpty()) { // check for W-Wing for candidate cand2 SolutionStep step = checkLink(cand2, cand1, i, j, elimSet, onlyOne); if (onlyOne && step != null) { return step; } } } } return null; }
/** * Try all combinations of three bivalue cells (for xyz: one trivalue and two bivalue cells). The * following restrictions are in place: * * <ul> * <li>The three cells must have exactly three candidates together * <li>The first cell (pivot) must see both other cells (pincers) * <li>The pincers must have exactly one candidate that is the same (candidate z) * <li>z can be excluded from all cells that see both pincers (for xyz they must see the pivot * as well) * </ul> * * @param xyz * @param onlyOne * @return */ private SolutionStep getWing(boolean xyz, boolean onlyOne) { // first get all bivalue/trivalue cells int biValueCount = 0; int triValueCount = 0; for (int i = 0; i < Sudoku2.LENGTH; i++) { if (sudoku.getAnzCandidates(i) == 2) { biCells[biValueCount++] = i; } if (xyz && sudoku.getAnzCandidates(i) == 3) { triCells[triValueCount++] = i; } } // now iterate them; use local variables to cover xy and xyz int endIndex = xyz ? triValueCount : biValueCount; int[] biTri = xyz ? triCells : biCells; // we check all combinations of bivalue cells (one tri + 2 bi for xyz) for (int i = 0; i < endIndex; i++) { for (int j = xyz ? 0 : i + 1; j < biValueCount; j++) { // any given combination of two cells must give exactly three // candidates; if that is not the case, skip it right away if (Sudoku2.ANZ_VALUES[sudoku.getCell(biTri[i]) | sudoku.getCell(biCells[j])] != 3) { // cant become a wing continue; } for (int k = j + 1; k < biValueCount; k++) { int index1 = biTri[i]; int index2 = biCells[j]; int index3 = biCells[k]; int cell1 = sudoku.getCell(index1); int cell2 = sudoku.getCell(index2); int cell3 = sudoku.getCell(index3); // all three cells combined must have exactly three candidates if (Sudoku2.ANZ_VALUES[cell1 | cell2 | cell3] != 3) { // incorrect number of candidates continue; } // none of the cells may be equal if (cell1 == cell2 || cell2 == cell3 || cell3 == cell1) { // cant be a wing continue; } // three possibilities for XY-Wing: each cell could be the pincer // XYZ-Wing exits the loop after the first iteration int maxTries = xyz ? 1 : 3; for (int tries = 0; tries < maxTries; tries++) { // swap cells accordingly if (tries == 1) { index1 = biCells[j]; index2 = biTri[i]; cell1 = sudoku.getCell(index1); cell2 = sudoku.getCell(index2); } else if (tries == 2) { index1 = biCells[k]; index2 = biCells[j]; index3 = biTri[i]; cell1 = sudoku.getCell(index1); cell2 = sudoku.getCell(index2); cell3 = sudoku.getCell(index3); } // the pivot must see the pincers if (!Sudoku2.buddies[index1].contains(index2) || !Sudoku2.buddies[index1].contains(index3)) { // doesnt see them -> try another continue; } // the pincers must have exactly one candidate that is the same in both cells short cell = (short) (cell2 & cell3); if (Sudoku2.ANZ_VALUES[cell] != 1) { // no wing, sorry continue; } int candZ = Sudoku2.CAND_FROM_MASK[cell]; // are there candidates that can see the pincers? elimSet.setAnd(Sudoku2.buddies[index2], Sudoku2.buddies[index3]); elimSet.and(finder.getCandidates()[candZ]); if (xyz) { // the pivot as well elimSet.and(Sudoku2.buddies[index1]); } if (elimSet.isEmpty()) { // no candidates to delete continue; } // ok, wing found! globalStep.reset(); if (xyz) { globalStep.setType(SolutionType.XYZ_WING); } else { globalStep.setType(SolutionType.XY_WING); } int[] cands = sudoku.getAllCandidates(index1); globalStep.addValue(cands[0]); globalStep.addValue(cands[1]); if (xyz) { globalStep.addValue(cands[2]); } else { globalStep.addValue(candZ); } globalStep.addIndex(index1); globalStep.addIndex(index2); globalStep.addIndex(index3); if (xyz) { globalStep.addFin(index1, candZ); } globalStep.addFin(index2, candZ); globalStep.addFin(index3, candZ); for (int l = 0; l < elimSet.size(); l++) { globalStep.addCandidateToDelete(elimSet.get(l), candZ); } SolutionStep step = (SolutionStep) globalStep.clone(); if (onlyOne) { return step; } else { steps.add(step); } } } } } return null; }