Skip to content

Commit

Permalink
.NET 8
Browse files Browse the repository at this point in the history
  • Loading branch information
Kermalis committed Nov 19, 2023
1 parent bf77dc0 commit 97d9e61
Show file tree
Hide file tree
Showing 42 changed files with 2,365 additions and 1,697 deletions.
18 changes: 15 additions & 3 deletions SudokuSolver.sln
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.27428.2002
# Visual Studio Version 17
VisualStudioVersion = 17.4.33110.190
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SudokuSolver", "SudokuSolver\SudokuSolver.csproj", "{C3556DE7-3124-4995-8BBB-FD9DFF460193}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SudokuSolver", "SudokuSolver\SudokuSolver.csproj", "{C3556DE7-3124-4995-8BBB-FD9DFF460193}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SudokuSolverTests", "SudokuSolverTests\SudokuSolverTests.csproj", "{80009691-BE65-4F91-91DB-DA244CBA5527}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBD}") = "SudokuSolverWinForms", "SudokuSolverWinForms\SudokuSolverWinForms.csproj", "{9B84A03B-038F-4FE9-AFFA-DD64D749C86B}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Expand All @@ -15,6 +19,14 @@ Global
{C3556DE7-3124-4995-8BBB-FD9DFF460193}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C3556DE7-3124-4995-8BBB-FD9DFF460193}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C3556DE7-3124-4995-8BBB-FD9DFF460193}.Release|Any CPU.Build.0 = Release|Any CPU
{80009691-BE65-4F91-91DB-DA244CBA5527}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{80009691-BE65-4F91-91DB-DA244CBA5527}.Debug|Any CPU.Build.0 = Debug|Any CPU
{80009691-BE65-4F91-91DB-DA244CBA5527}.Release|Any CPU.ActiveCfg = Release|Any CPU
{80009691-BE65-4F91-91DB-DA244CBA5527}.Release|Any CPU.Build.0 = Release|Any CPU
{9B84A03B-038F-4FE9-AFFA-DD64D749C86B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{9B84A03B-038F-4FE9-AFFA-DD64D749C86B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9B84A03B-038F-4FE9-AFFA-DD64D749C86B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9B84A03B-038F-4FE9-AFFA-DD64D749C86B}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down
257 changes: 257 additions & 0 deletions SudokuSolver/Cell.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,257 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
#if DEBUG
using System.Diagnostics;
#endif

namespace Kermalis.SudokuSolver;

#if DEBUG
[DebuggerDisplay("{DebugString()}", Name = "{ToString()}")]
#endif
public sealed class Cell
{
public const int EMPTY_VALUE = 0;
/// <summary>6 in Column, 6 in Row, 8 in Block</summary>
public const int NUM_VISIBLE_CELLS = 6 + 6 + 8;

internal readonly Puzzle Puzzle;
public int OriginalValue { get; private set; }
public SPoint Point { get; }
public Region Block { get; private set; }
public Region Column { get; private set; }
public Region Row { get; private set; }
/// <summary>The <see cref="NUM_VISIBLE_CELLS"/> cells this cell is grouped with. Block, Row, Column</summary>
public ReadOnlyCollection<Cell> VisibleCells { get; private set; }

public int Value { get; private set; }
public HashSet<int> Candidates { get; }

public List<CellSnapshot> Snapshots { get; }

internal Cell(Puzzle puzzle, int value, SPoint point)
{
Puzzle = puzzle;

OriginalValue = value;
Value = value;
Point = point;

Candidates = new HashSet<int>(Utils.OneToNine);
Snapshots = [];

// Will be set in InitRegions
Block = null!;
Column = null!;
Row = null!;
// Will be set in InitVisibleCells
VisibleCells = null!;
}
internal void InitRegions()
{
Block = Puzzle.Blocks[Point.BlockIndex];
Column = Puzzle.Columns[Point.Column];
Row = Puzzle.Rows[Point.Row];
}
internal void InitVisibleCells()
{
int counter = 0;
var neighbors = new Cell[NUM_VISIBLE_CELLS];
for (int i = 0; i < 9; i++)
{
// Add 8 neighbors from block
Cell other = Block[i];
if (other != this)
{
neighbors[counter++] = other;
}

// Add 6 neighbors from row
other = Row[i];
if (other.Block != Block)
{
neighbors[counter++] = other;
}

// Add 6 neighbors from column
other = Column[i];
if (other.Block != Block)
{
neighbors[counter++] = other;
}
}
VisibleCells = new ReadOnlyCollection<Cell>(neighbors);
}

// TODO: Remove
public bool ChangeCandidates(int candidate, bool remove = true)
{
return remove ? Candidates.Remove(candidate) : Candidates.Add(candidate);
}
internal bool ChangeCandidates(IEnumerable<int> candidates, bool remove = true)
{
bool changed = false;
foreach (int candidate in candidates)
{
if (remove ? Candidates.Remove(candidate) : Candidates.Add(candidate))
{
changed = true;
}
}
return changed;
}
internal static bool ChangeCandidates(IEnumerable<Cell> cells, int candidate, bool remove = true)
{
bool changed = false;
foreach (Cell cell in cells)
{
if (remove ? cell.Candidates.Remove(candidate) : cell.Candidates.Add(candidate))
{
changed = true;
}
}
return changed;
}
internal static bool ChangeCandidates(IEnumerable<Cell> cells, IEnumerable<int> candidates, bool remove = true)
{
bool changed = false;
foreach (Cell cell in cells)
{
foreach (int candidate in candidates)
{
if (remove ? cell.Candidates.Remove(candidate) : cell.Candidates.Add(candidate))
{
changed = true;
}
}
}
return changed;
}

/// <summary>Changes the current value to <paramref name="newValue"/>. <see cref="Candidates"/> is updated.
/// If <paramref name="refreshOtherCellCandidates"/> is true, the entire puzzle's candidates are refreshed.</summary>
internal void Set(int newValue, bool refreshOtherCellCandidates = false)
{
int oldValue = Value;
Value = newValue;

if (newValue == EMPTY_VALUE)
{
for (int i = 1; i <= 9; i++)
{
Candidates.Add(i);
}
ChangeCandidates(VisibleCells, oldValue, remove: false);
}
else
{
Candidates.Clear();
ChangeCandidates(VisibleCells, newValue);
}

if (refreshOtherCellCandidates)
{
Puzzle.RefreshCandidates();
}
}
public void ChangeOriginalValue(int value)
{
if (value is < EMPTY_VALUE or > 9)
{
throw new ArgumentOutOfRangeException(nameof(value), value, null);
}

OriginalValue = value;
Set(value, refreshOtherCellCandidates: true);
}
/*internal void CreateSnapshot(bool isCulprit, bool isSemiCulprit)
{
Span<int> cache = stackalloc int[9];
Snapshots.Add(new CellSnapshot(Value, new ReadOnlyCollection<int>(Candidates.AsInt(cache).ToArray()), isCulprit, isSemiCulprit));
}*/
internal void CreateSnapshot(bool isCulprit, bool isSemiCulprit)
{
Snapshots.Add(new CellSnapshot(Value, new ReadOnlyCollection<int>(Candidates.ToArray()), isCulprit, isSemiCulprit));
}

public override int GetHashCode()
{
return Point.GetHashCode();
}
public override bool Equals(object? obj)
{
if (obj is Cell other)
{
return other.Point.Equals(Point);
}
return false;
}
public override string ToString()
{
return Point.ToString();
}
#if DEBUG
public string DebugString()
{
string s = Point.ToString() + " ";
if (Value == EMPTY_VALUE)
{
s += "has candidates: " + Candidates.Print();
}
else
{
s += "- " + Value.ToString();
}
return s;
}
#endif

/*/// <summary>Returns the visible cells that this cell and <paramref name="other"/> share.
/// Result length is 2 (1 row + 1 column) or 7 (7 row/column) or 13 (7 block + 6 row/column)</summary>
internal Span<Cell> IntersectVisibleCells(Cell other, Span<Cell> cache)
{
int counter = 0;
for (int i = 0; i < NUM_VISIBLE_CELLS; i++)
{
Cell cell = VisibleCells[i];
if (other.VisibleCells.Contains(cell))
{
cache[counter++] = cell;
}
}
return cache.Slice(0, counter);
}
/// <summary>Returns the visible cells that this cell, <paramref name="otherA"/>, and <paramref name="otherB"/> all share.
/// Result length is 0 or 1 (1 row/column) or 6 (6 block/row/column) or 12 (6 block + 6 row/column)</summary>
internal Span<Cell> IntersectVisibleCells(Cell otherA, Cell otherB, Span<Cell> cache)
{
int counter = 0;
for (int i = 0; i < NUM_VISIBLE_CELLS; i++)
{
Cell cell = VisibleCells[i];
if (otherA.VisibleCells.Contains(cell) && otherB.VisibleCells.Contains(cell))
{
cache[counter++] = cell;
}
}
return cache.Slice(0, counter);
}*/

/*/// <summary>Result length is 12 or 14</summary>
internal Span<Cell> VisibleCellsExceptRegion(Region except, Span<Cell> cache)
{
int counter = 0;
for (int i = 0; i < 8 + 6 + 6; i++)
{
Cell cell = VisibleCells[i];
if (except.IndexOf(cell) == -1)
{
// Add if "except" region does not contain the visible cell
cache[counter++] = cell;
}
}
return cache.Slice(0, counter);
}*/
}
19 changes: 19 additions & 0 deletions SudokuSolver/CellSnapshot.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using System.Collections.ObjectModel;

namespace Kermalis.SudokuSolver;

public sealed class CellSnapshot
{
public int Value { get; }
public ReadOnlyCollection<int> Candidates { get; }
public bool IsCulprit { get; }
public bool IsSemiCulprit { get; }

internal CellSnapshot(int value, ReadOnlyCollection<int> candidates, bool isCulprit, bool isSemiCulprit)
{
Value = value;
Candidates = candidates;
IsCulprit = isCulprit;
IsSemiCulprit = isSemiCulprit;
}
}
Loading

0 comments on commit 97d9e61

Please sign in to comment.