各セルにランダムな有効な数値を入れる再帰的なバックトラッキング アルゴリズムを使用できます。有効なセルがない場合は、バックトラックして前のセルの別の番号を選択します。列挙子メソッドを使用すると、バックトラッキング システムを簡単に構築できます。
class Generator
{
public int Width { get; private set; }
public int Height { get; private set; }
public int Radius { get; private set; }
private List<int> _numbers;
private bool[] _picked;
private int[] _grid;
private Random _rnd;
public Generator(int width, int height, int radius)
{
Width = width;
Height = height;
Radius = radius;
_rnd = new Random();
_numbers = Enumerable.Range(0,Width*Height).OrderBy(_ => _rnd.Next()).ToList();
_picked = _numbers.Select(n => false).ToArray();
_grid = new int[width*height];
}
public int[] Generate()
{
return Generate(0)
.Select(a => a.ToArray()) // copy
.FirstOrDefault();
}
private IEnumerable<int[]> Generate(int index)
{
if (index >= Width * Height)
{
yield return _grid;
yield break;
}
int xmid = index%Width;
int xlow = Math.Max(0, xmid - Radius);
int xhigh = Math.Min(xmid + Radius, Width - 1);
int ymid = index/Width;
int ylow = Math.Max(0, ymid - Radius);
int yhigh = ymid;
var validNumbers = _numbers
.Where(n =>
!_picked[n] &&
Enumerable.Range(xlow, xhigh - xlow + 1).All(x =>
Enumerable.Range(ylow, yhigh-ylow+1).All(y =>
y*Width + x >= index // Not generated yet
|| Math.Abs(x - xmid) + Math.Abs(y - ymid) > Radius // Outside radius
|| Math.Abs(_grid[y*Width+x] - n) > 1 // Out of range
)
)
)
.ToList();
foreach (var n in validNumbers)
{
_grid[index] = n;
_picked[n] = true;
foreach (var sol in Generate(index + 1))
{
yield return sol;
}
_picked[n] = false;
}
}
}
50 ミリ秒で生成された半径 4 の 10x10 グリッド:
74 6 72 1 82 64 41 66 96 17
61 24 12 93 35 86 52 19 47 10
42 48 69 45 79 88 31 43 28 36
15 38 4 40 54 33 13 7 90 68
34 67 62 83 99 59 50 22 73 77
44 18 0 8 20 81 26 37 98 87
29 71 58 75 14 65 55 85 57 80
84 32 91 25 5 78 95 9 2 53
60 23 11 63 49 39 70 89 27 46
97 16 3 30 56 92 76 51 21 94
多くの場合、それは迅速で、1 秒以内に完了します。場合によっては、最初は悪い選択をし、何度も後戻りしなければならないこともあります。