/
PuzzleWithPossibleValues.cs
162 lines (148 loc) · 6.98 KB
/
PuzzleWithPossibleValues.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
namespace SudokuSpice.RuleBased
{
/// <summary>Stores a puzzle's data and associated possible values.</summary>.
public class PuzzleWithPossibleValues : IPuzzleWithPossibleValues<PuzzleWithPossibleValues>
{
private readonly Puzzle _puzzle;
private readonly PossibleValues _possibleValues;
/// <inheritdoc cref="IReadOnlyPuzzle"/>
public int Size => _puzzle.Size;
/// <inheritdoc cref="IReadOnlyPuzzle"/>
public int NumSquares => _puzzle.NumSquares;
/// <inheritdoc cref="IReadOnlyPuzzle"/>
public int NumEmptySquares => _puzzle.NumEmptySquares;
/// <inheritdoc cref="IReadOnlyPuzzle"/>
public int NumSetSquares => _puzzle.NumSetSquares;
/// <inheritdoc cref="IReadOnlyPuzzle"/>
public BitVector UniquePossibleValues => _possibleValues.AllPossible;
/// <inheritdoc/>
public ReadOnlySpan<int> AllPossibleValuesSpan => _puzzle.AllPossibleValuesSpan;
/// <inheritdoc/>
public IReadOnlyDictionary<int, int> CountPerUniqueValue => _puzzle.CountPerUniqueValue;
/// <summary>
/// Constructs a new puzzle of the given side length.
/// </summary>
/// <param name="size">
/// The side-length for this Sudoku puzzle. Must be in the inclusive range [1, 31].
/// </param>
/// <exception cref="ArgumentException">
/// Thrown if size is not the square of a whole number, or is outside the range [1, 31].
/// </exception>
public PuzzleWithPossibleValues(int size)
{
if (size < 1 || size >= BitVector.NumBits)
{
throw new ArgumentException($"Puzzle size must be in the range [1, {BitVector.NumBits - 1}].");
}
_puzzle = new Puzzle(size);
var possibleValues = BitVector.CreateWithSize(size + 1);
possibleValues.UnsetBit(0);
_possibleValues = new PossibleValues(size, possibleValues);
}
/// <summary>
/// Constructs a new puzzle backed by the given array.
///
/// The puzzle is backed directly by this array (i.e. modifying the array
/// modifies the puzzle, and vice-versa). If this is not what you want, see
/// <see cref="CopyFrom(int?[,])"/> and <see cref="CopyFrom(int?[][])"/>. Note that all
/// future modifications should be done through this puzzle object, else this will be in an
/// incorrect state.
/// </summary>
/// <param name="puzzleMatrix">
/// The data for this Sudoku puzzle. Preset squares should be set, and unset squares should
/// be null. The puzzle maintains a reference to this array.
/// </param>
public PuzzleWithPossibleValues(int?[][] puzzleMatrix)
{
_puzzle = new Puzzle(puzzleMatrix);
int size = _puzzle.Size;
if (size < 1 || size >= BitVector.NumBits)
{
throw new ArgumentException($"Puzzle size must be in the range [1, {BitVector.NumBits - 1}].");
}
var possibleValues = BitVector.CreateWithSize(size + 1);
possibleValues.UnsetBit(0);
_possibleValues = new PossibleValues(size, possibleValues);
}
/// <summary>
/// Constructs a puzzle backed by the given <see cref="Puzzle"/> object, but now with the
/// ability to track possible values.
/// </summary>
/// <param name="puzzle">
/// The puzzle data to use. The puzzle maintains a reference to this object.
/// </param>
public PuzzleWithPossibleValues(Puzzle puzzle)
{
_puzzle = puzzle;
var possibleValues = new BitVector();
foreach (var possibleValue in puzzle.AllPossibleValuesSpan)
{
if (possibleValue >= BitVector.NumBits)
{
throw new ArgumentException(
$"Puzzle must have possible values in the range [0, {BitVector.NumBits - 1}]. Received value {possibleValue}.");
}
possibleValues.SetBit(possibleValue);
}
_possibleValues = new PossibleValues(Size, possibleValues);
}
/// <summary>
/// A deep copy constructor for an existing puzzle.
/// </summary>
public PuzzleWithPossibleValues(PuzzleWithPossibleValues existing)
{
_puzzle = existing._puzzle.DeepCopy();
_possibleValues = new PossibleValues(existing._possibleValues);
}
/// <summary>Creates a new puzzle with a copy of the given matrix.</summary>
[SuppressMessage("Performance", "CA1814:Prefer jagged arrays over multidimensional", Justification = "Provided to ease migration.")]
public static PuzzleWithPossibleValues CopyFrom(int?[,] matrix)
{
return new PuzzleWithPossibleValues(matrix.CopyToJagged2D());
}
/// <summary>Creates a new puzzle with a copy of the given matrix.</summary>
public static PuzzleWithPossibleValues CopyFrom(int?[][] matrix)
{
return new PuzzleWithPossibleValues(matrix.Copy2D());
}
/// <inheritdoc/>
public PuzzleWithPossibleValues DeepCopy() => new PuzzleWithPossibleValues(this);
/// <inheritdoc/>
public int? this[int row, int col]
{
get => _puzzle[row, col];
set {
_puzzle[row, col] = value;
if (!value.HasValue) {
_possibleValues.Reset(new Coordinate(row, col));
}
}
}
/// <inheritdoc cref="IPuzzle{T}"/>
[SuppressMessage("Design", "CA1043:Use Integral Or String Argument For Indexers", Justification = "This makes sense with Coordinate, which removes any ambiguity between first and second arguments")]
public int? this[in Coordinate c]
{
get => _puzzle[in c];
set => _puzzle[in c] = value;
}
/// <summary>Gets a span of coordinates for all the unset squares.</summary>
public ReadOnlySpan<Coordinate> GetUnsetCoords() => _puzzle.GetUnsetCoords();
/// <summary>
/// Returns the puzzle in a pretty string format, with boxes and rows separated by pipes
/// and dashes.
/// </summary>
public override string ToString() => Puzzles.ToString(_puzzle);
/// <inheritdoc/>
public void IntersectPossibleValues(in Coordinate c, BitVector possibleValues) =>
_possibleValues.Intersect(in c, possibleValues);
/// <inheritdoc/>
public void ResetPossibleValues(in Coordinate c) => _possibleValues.Reset(in c);
/// <inheritdoc/>
public BitVector GetPossibleValues(in Coordinate c) => _possibleValues[in c];
/// <inheritdoc/>
public void SetPossibleValues(in Coordinate c, BitVector possibleValues) => _possibleValues[in c] = possibleValues;
}
}