Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Satisfactory check #78

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 37 additions & 11 deletions SudoScript.Core.Test/SolverTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,7 @@ namespace SudoScript.Core.Test;

internal sealed class SolverTests
{
[Test()]
public void CanSolveEmptySudoku()
{
Board board = Util.CreateStandardEmpty();
Assert.DoesNotThrow(() => board = Solver.Solve(board));
Assert.IsTrue(board.ValidateRules());
Assert.IsFalse(board.Cells().Any(c => c.Digit == Cell.EmptyDigit));
}

[Test]
public void CanSolveGeneratedSudoku()
public Board CreateEasyBoard()
{
Board board = Util.CreateStandardEmpty();
// Sudoku givens generated by https://sudoku.com/
Expand All @@ -39,6 +29,7 @@ public void CanSolveGeneratedSudoku()
board[8, 4].Digit = 8;

board[3, 5].Digit = 4;
board[4, 7].Digit = 2;

board[3, 6].Digit = 6;
board[5, 6].Digit = 7;
Expand All @@ -50,6 +41,7 @@ public void CanSolveGeneratedSudoku()
board[8, 7].Digit = 3;
board[9, 7].Digit = 5;

board[3, 9].Digit = 1;
board[2, 8].Digit = 9;
board[4, 8].Digit = 7;
board[5, 8].Digit = 4;
Expand All @@ -59,6 +51,23 @@ public void CanSolveGeneratedSudoku()
board[7, 9].Digit = 9;
board[8, 9].Digit = 7;

return board;
}

[Test()]
public void CanSolveEmptySudoku()
{
Board board = Util.CreateStandardEmpty();
Assert.DoesNotThrow(() => board = Solver.Solve(board));
Assert.IsTrue(board.ValidateRules());
Assert.IsFalse(board.Cells().Any(c => c.Digit == Cell.EmptyDigit));
}

[Test]
public void CanSolveGeneratedSudoku()
{
Board board = CreateEasyBoard();

Console.WriteLine(board.ToString());

Console.WriteLine("-------------------------------------------------");
Expand All @@ -68,4 +77,21 @@ public void CanSolveGeneratedSudoku()

Console.WriteLine(board.ToString());
}

[Test]
public void IsSatisfactoryTest()
{
Board board = CreateEasyBoard();

Assert.IsTrue(Solver.IsSatisfactory(board));
}

[Test]
public void IsNotSatisfactoryTest()
{
Board board = Util.CreateStandardEmpty();

Assert.IsFalse(Solver.IsSatisfactory(board));
}

}
18 changes: 13 additions & 5 deletions SudoScript.Core/Solver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public static Board Solve(Board board)

private static bool SolveRec(Board board, [NotNullWhen(true)] out Board? solvedBoard)
{
// Eliminate candidates from all rules untill nothing changes.
// Eliminate candidates from all rules until nothing changes.
while (board.EliminateCandidates()) ;

// We hit an invalid state, and must backtrack.
Expand All @@ -42,14 +42,14 @@ private static bool SolveRec(Board board, [NotNullWhen(true)] out Board? solvedB
orderedCells = orderedCells.SkipWhile(c => c.CandidateCount <= 1);
// The first cell contains the smallest amount of candidates.
int lowestCandidateCount = orderedCells.FirstOrDefault()?.CandidateCount ?? 1;
// Take all cells with the least amount of candidates.
orderedCells = orderedCells.TakeWhile(c => c.CandidateCount == lowestCandidateCount);
// If there are no cells with more than 1 candidate, the board is solved.
if (lowestCandidateCount == 1)
{
solvedBoard = board;
return true;
}
// Take all cells with the least amount of candidates.
orderedCells = orderedCells.TakeWhile(c => c.CandidateCount == lowestCandidateCount);

Cell cell = orderedCells.First();
foreach (int candidate in cell.Candidates())
Expand All @@ -76,9 +76,17 @@ public static Board GenerateSolveable(Board board)
throw new NotImplementedException();
}

public static bool IsSatisfactory(Board board)
/// <summary>
/// Checks if the board can be solved by just using the EliminateCandidates methods from units.
/// </summary>
/// <param name="board"></param>
/// <returns>True if the board can be solved without trial and error guessing.</returns>
public static bool IsSatisfactory(Board board) // Certain methods for eliminating candidates using inference are not currently implemented. Implementing them would make this function more acurate.
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Commented on this already in #79

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Satisfactory means you can get the solutions without guess-work. Proper is when there is only one solution.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As we discussed in the report both of those actually mean the same thing. If you dont need guess work you can always arrive at the single solution. The only time you would ever need guess work is if there is more than one solution.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A sudoku with a single solution does not have to be satisfactory. It depends how you define satisfactory. How much inference is enough to make it unsatisfactory? I feel like using this method as a qualifier for satisfaction is fine, since the IsProper method is separate anyways.

{
throw new NotImplementedException();
// Eliminate candidates from all rules untill nothing changes.
while (board.EliminateCandidates());
// If the board is solved, it does not require trial and error.
return board.IsSolved();
}

public static bool IsProper(Board board)
Expand Down
Loading