forked from microsoft/Quantum
-
Notifications
You must be signed in to change notification settings - Fork 0
/
SudokuQuantum.cs
144 lines (138 loc) · 6.51 KB
/
SudokuQuantum.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
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#nullable enable
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Quantum.Simulation.Common;
using Microsoft.Quantum.Simulation.Core;
namespace Microsoft.Quantum.Samples.SudokuGrover
{
/// <summary>
/// Code to solve a Sudoku puzzle by transforming it into a graph problem
/// and calling the Quantum SolvePuzzle operation to solve it
/// </summary>
class SudokuQuantum
{
/// <summary>
/// QuantumSolve will call Q# code to solve the Sudoku puzzle and display the solution.
/// </summary>
/// <param name="puzzle">The puzzle to solve</param>
/// <param name="sim">The QuantumSimulator to use</param>
/// <returns>Returns true if a solution was found</returns>
public async Task<bool> QuantumSolve(int[,] puzzle, SimulatorBase sim)
{
int size = puzzle.GetLength(0);
FindEdgesAndInitialNumberConstraints(
puzzle,
size,
out var emptySquareEdges,
out var startingNumberConstraints,
out var emptySquares
);
var emptySquareEdgesQArray = new QArray<(long, long)>(emptySquareEdges);
var startingNumberConstraintsQArray = new QArray<(long, long)>(startingNumberConstraints);
var (foundSolution, solution) = await SolvePuzzle.Run(sim, emptySquares.Count, size, emptySquareEdgesQArray, startingNumberConstraintsQArray);
if (foundSolution)
{
foreach (var (emptySquare, completion) in Enumerable.Zip(emptySquares, solution))
{
puzzle[emptySquare.Row, emptySquare.Column] = (int)completion + 1;
}
Console.WriteLine("Solved puzzle.");
}
return foundSolution;
}
/// <summary>
/// An Empty square with its row and column.
/// </summary>
struct EmptySquare
{
public int Row;
public int Column;
}
/// <summary>
/// Find the puzzle empty square edges, and starting number constraints
/// for those empty squares.
/// </summary>
/// <param name="puzzle">The Sudoku puzzle</param>
/// <param name="size">The size of the puzzle. For example, 9 for a 9x9 puzzle</param>
/// <param name="emptySquareEdges">The list of empty square edges specifying Vertices in the same row/col/subgrid</param>
/// <param name="startingNumberConstraints">The set of numbers that this empty square can not have</param>
/// <param name="emptySquares">List of empty squares with their i,j locations</param>
void FindEdgesAndInitialNumberConstraints(int[,] puzzle, int size,
out List<(long, long)> emptySquareEdges, out HashSet<(long, long)> startingNumberConstraints,
out List<EmptySquare> emptySquares)
{
// find color edges ... i.e. edges between horizontal, vertical
// and diagonal empty squares.
// Note that for size=4, we will subtract 1 from all puzzle
// numbers so that they fit in 2 bits i.e. 1 to 4 becomes 0 to 3.
int subSize = size == 9 ? 3 : 2; // subsize is 2 for size=4 and 3 for size=9
var emptyIndexes = new int[size, size];
emptySquares = new List<EmptySquare>();
emptySquareEdges = new List<(long, long)>();
// Starting Number constraints on blank squares.
startingNumberConstraints = new HashSet<(long, long)>();
for (int i = 0; i < size; i++)
{
for (int j = 0; j < size; j++)
{
if (puzzle[i, j] == 0)
{
var emptyIndex = emptySquares.Count;
emptyIndexes[i, j] = emptyIndex;
emptySquares.Add(new EmptySquare
{
Row = i,
Column = j
});
// Add all existing number constraints in
// subSize x subSize to hashset of constraints for this cell.
// Also, add edge to any previous empty cells in the
// subSize x subSize box.
int iSubGridStart = i / subSize * subSize;
int jSubGridStart = j / subSize * subSize;
for (int iSub = iSubGridStart; iSub < i; iSub++)
{
for (int jSub = jSubGridStart; jSub < j; jSub++)
{
if (puzzle[iSub, jSub] != 0)
startingNumberConstraints.Add(
(emptyIndex, puzzle[iSub, jSub] - 1));
else if ((iSub < jSub) && (iSub != i) && (jSub != j))
emptySquareEdges.Add(
(emptyIndex, emptyIndexes[iSub, jSub]));
}
}
for (int ii = 0; ii < size; ii++)
{
if (puzzle[ii, j] != 0)
startingNumberConstraints.Add(
(emptyIndex, puzzle[ii, j] - 1));
else if (ii < i)
emptySquareEdges.Add(
(emptyIndex, emptyIndexes[ii, j]));
}
for (int jj = 0; jj < size; jj++)
{
if (puzzle[i, jj] != 0)
startingNumberConstraints.Add(
(emptyIndex, puzzle[i, jj] - 1));
else if (jj < j)
emptySquareEdges.Add(
(emptyIndex, emptyIndexes[i, jj]));
}
if (size == 9)
{ // Invalidate numbers that are illegal on a 9x9 board
for (int invalid = 9; invalid < 16; invalid++)
startingNumberConstraints.Add(
(emptyIndex, invalid));
}
}
}
}
}
}
}