Skip to content

Commit

Permalink
Added hidden tripples
Browse files Browse the repository at this point in the history
  • Loading branch information
kris701 committed Apr 9, 2024
1 parent 5b95f81 commit 4034a35
Show file tree
Hide file tree
Showing 9 changed files with 140 additions and 47 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
using SudokuSolver.Models;
using SudokuSolver.Solvers.BacktrackSolvers.Pruners;
using SudokuSolver.Solvers.Preprocessors;

namespace SudokuSolver.Tests.Solvers.BacktrackSolvers.Pruners
{
[TestClass]
public class HiddenTripplePrunerTests
{
[TestMethod]
[DataRow("000001030231090000065003100678924300103050006000136700009360570006019843300000000", 15)]
public void Can_PruneCorrectly(string board, int expectedChange)
{
// ARRANGE
var values = new List<byte>();
foreach (var c in board)
values.Add(byte.Parse($"{c}"));
var context = Preprocessor.Preprocess(new SudokuBoard(values.ToArray()));
IPruner pruner1 = new HiddenTripplePruner();
var preCount = context.Cardinalities.Sum(x => x.Possibilities);

// ACT
while (pruner1.Prune(context)) { }
context.Cardinalities = Preprocessor.GenerateCardinalities(context.Board, context.Candidates);

// ASSERT
Assert.AreEqual(expectedChange, preCount - context.Cardinalities.Sum(x => x.Possibilities));
}
}
}
1 change: 1 addition & 0 deletions SudokuSolver/Solvers/BacktrackSolvers/BacktrackSolver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ public class BacktrackSolver : BaseSolver
new NakedPairPruner(),
new NakedTripplePruner(),
new HiddenPairPruner(),
new HiddenTripplePruner(),
new PointingPairsPruner()
};

Expand Down
16 changes: 16 additions & 0 deletions SudokuSolver/Solvers/BacktrackSolvers/Pruners/BasePruner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,22 @@ internal List<CellAssignment> GetAssignmentsFromBlock(SearchContext context, byt
return cellPossibilities;
}

internal List<CellAssignment> GetAssignmentsFromRow(SearchContext context, byte row)
{
var cellPossibilities = new List<CellAssignment>();
for (int x = 0; x < SudokuBoard.BoardSize; x++)
cellPossibilities.AddRange(context.Candidates[x, row]);
return cellPossibilities;
}

internal List<CellAssignment> GetAssignmentsFromColumn(SearchContext context, byte column)
{
var cellPossibilities = new List<CellAssignment>();
for (int y = 0; y < SudokuBoard.BoardSize; y++)
cellPossibilities.AddRange(context.Candidates[column, y]);
return cellPossibilities;
}

internal int PruneValueCandidatesFromRow(SearchContext context, List<CellAssignment> ignore, byte value)
{
var pruned = 0;
Expand Down
34 changes: 16 additions & 18 deletions SudokuSolver/Solvers/BacktrackSolvers/Pruners/CertainsPruner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,28 +5,26 @@ namespace SudokuSolver.Solvers.BacktrackSolvers.Pruners
public class CertainsPruner : BasePruner
{
public override bool Prune(SearchContext context)
{
bool any = false;
while (PruneCertains(context)) { any = true; }
return any;
}

private bool PruneCertains(SearchContext context)
{
var pruned = 0;
for (byte x = 0; x < SudokuBoard.BoardSize; x++)
for (byte y = 0; y < SudokuBoard.BoardSize; y++)
if (context.Candidates[x, y].Count == 1)
pruned += RemoveCandidate(context, context.Candidates[x, y][0]);

for (byte blockX = 0; blockX < SudokuBoard.Blocks; blockX++)
var pre = 1;
while (pruned - pre != 0)
{
for (byte blockY = 0; blockY < SudokuBoard.Blocks; blockY++)
pre = pruned;
for (byte x = 0; x < SudokuBoard.BoardSize; x++)
for (byte y = 0; y < SudokuBoard.BoardSize; y++)
if (context.Candidates[x, y].Count == 1)
pruned += RemoveCandidate(context, context.Candidates[x, y][0]);

for (byte blockX = 0; blockX < SudokuBoard.Blocks; blockX++)
{
var cellPossibilities = GetAssignmentsFromBlock(context, blockX, blockY);
for (byte i = 1; i <= SudokuBoard.BoardSize; i++)
if (cellPossibilities.Count(x => x.Value == i) == 1)
pruned += RemoveCandidate(context, cellPossibilities.First(x => x.Value == i));
for (byte blockY = 0; blockY < SudokuBoard.Blocks; blockY++)
{
var cellPossibilities = GetAssignmentsFromBlock(context, blockX, blockY);
for (byte i = 1; i <= SudokuBoard.BoardSize; i++)
if (cellPossibilities.Count(x => x.Value == i) == 1)
pruned += RemoveCandidate(context, cellPossibilities.First(x => x.Value == i));
}
}
}
if (pruned > 0)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,6 @@ namespace SudokuSolver.Solvers.BacktrackSolvers.Pruners
public class HiddenPairPruner : BasePruner
{
public override bool Prune(SearchContext context)
{
bool any = false;
while (PruneHiddenPairs(context)) { any = true; }
return any;
}

private bool PruneHiddenPairs(SearchContext context)
{
var pruned = 0;
for (byte blockX = 0; blockX < SudokuBoard.Blocks; blockX++)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
using SudokuSolver.Models;
using System;
using System.Collections.Generic;
using System.Data.Common;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace SudokuSolver.Solvers.BacktrackSolvers.Pruners
{
public class HiddenTripplePruner : BasePruner
{
public override bool Prune(SearchContext context)
{
var pruned = 0;

for (byte row = 0; row < SudokuBoard.BoardSize; row++)
{
var candidates = GetAssignmentsFromRow(context, row);
var removed = 1;
while (removed > 0)
{
removed = 0;
for (int i = 1; i <= SudokuBoard.BoardSize; i++)
if (candidates.Where(x => x.Value == i).Count() == 1 || candidates.Where(x => x.Value == i).Count() > 3)
removed += candidates.RemoveAll(x => x.Value == i);
for (int i = 1; i <= SudokuBoard.BoardSize; i++)
if (candidates.Where(x => x.Value == i).Any(y => !candidates.Any(z => z != y && z.X == y.X)))
removed += candidates.RemoveAll(x => x.Value == i);
}

if (candidates.DistinctBy(x => x.Value).Count() == 3 && candidates.DistinctBy(x => x.X).Count() == 3)
foreach (var candidate in candidates)
pruned += context.Candidates[candidate.X, candidate.Y].RemoveAll(x => !candidates.Contains(x));
}

for (byte column = 0; column < SudokuBoard.BoardSize; column++)
{
var candidates = GetAssignmentsFromColumn(context, column);
var removed = 1;
while(removed > 0)
{
removed = 0;
for (int i = 1; i <= SudokuBoard.BoardSize; i++)
if (candidates.Where(x => x.Value == i).Count() == 1 || candidates.Where(x => x.Value == i).Count() > 3)
removed += candidates.RemoveAll(x => x.Value == i);
for (int i = 1; i <= SudokuBoard.BoardSize; i++)
if (candidates.Where(x => x.Value == i).Any(y => !candidates.Any(z => z != y && z.Y == y.Y)))
removed += candidates.RemoveAll(x => x.Value == i);
}

if (candidates.DistinctBy(x => x.Value).Count() == 3 && candidates.DistinctBy(x => x.Y).Count() == 3)
foreach (var candidate in candidates)
pruned += context.Candidates[candidate.X, candidate.Y].RemoveAll(x => !candidates.Contains(x));
}

if (pruned > 0)
Console.WriteLine($"Removed {pruned} candidates because of hidden tripples");
return pruned > 0;
}

private List<CellAssignment> LegalTripple(List<CellAssignment> assignments)
{
if (assignments.DistinctBy(x => x.Value).Count() != 3)
return new List<CellAssignment>();

for(int i = 1; i <= SudokuBoard.BoardSize; i++)
{
if (assignments.Any(x => x.Value == i) && assignments.Where(x => x.Value == i).Count() < 2)
return new List<CellAssignment>();
}

return assignments;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,6 @@ namespace SudokuSolver.Solvers.BacktrackSolvers.Pruners
public class NakedPairPruner : BasePruner
{
public override bool Prune(SearchContext context)
{
var any = false;
while (PruneNakedPairs(context)) { any = true; }
return any;
}

private bool PruneNakedPairs(SearchContext context)
{
var pruned = 0;
// Prune from columns
Expand Down Expand Up @@ -70,7 +63,7 @@ private bool PruneNakedPairs(SearchContext context)
var fromY = blockY * SudokuBoard.Blocks;
var toY = (blockY + 1) * SudokuBoard.Blocks;
var cellPossibilities = new List<List<CellAssignment>>();
for (int i = 0; i <= SudokuBoard.BoardSize; i++)
for (int i = 0; i < SudokuBoard.BoardSize; i++)
cellPossibilities.Add(new List<CellAssignment>());

int offset = 0;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,6 @@ namespace SudokuSolver.Solvers.BacktrackSolvers.Pruners
public class NakedTripplePruner : BasePruner
{
public override bool Prune(SearchContext context)
{
var any = false;
while (PruneNakedTripples(context)) { any = true; }
return any;
}

private bool PruneNakedTripples(SearchContext context)
{
var pruned = 0;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,6 @@ namespace SudokuSolver.Solvers.BacktrackSolvers.Pruners
public class PointingPairsPruner : BasePruner
{
public override bool Prune(SearchContext context)
{
var any = false;
while (PrunePointingPairs(context)) { any = true; }
return any;
}

private bool PrunePointingPairs(SearchContext context)
{
var pruned = 0;

Expand Down

0 comments on commit 4034a35

Please sign in to comment.