作为一个快速练习,我制作了完整的版本。如果有人有兴趣,这里是:
enum Directions { Right, Down, Left, Up }
Dictionary<Directions, Point> moves = new Dictionary<Directions, Point>
{
{ Directions.Right, new Point(1, 0) },
{ Directions.Down, new Point(0, 1) },
{ Directions.Left, new Point(-1,0) },
{ Directions.Up, new Point(0,-1) },
};
int?[,] MakeSpiral(int rows, int columns)
{
int?[,] field = new int?[rows, columns];
Point current = new Point(0, 0);
Directions direction = Directions.Right;
while(true)
{
field[current.Y, current.X] = current.X + 1;
Point next = current + moves[direction];
if(!IsFieldPositionValid(next, current))
{
// If we can't make a move, change direction clockwise
direction = (Directions)((int)(direction + 1) % 4);
}
next = current + moves[direction];
if(!IsFieldPositionValid(next, current))
{
// If we can't make a move after changing the direction
// it means that we are stuck and we completed the spiral
return field;
}
current = next;
}
// Checks whether we can put a value in a given position
// taking into consideration whether it is out of bounds of the field
// and if it's too close to other values
bool IsFieldPositionValid(Point position, Point previous)
{
if(IsFieldPositionOutOfBounds(position))
return false;
if(!CheckSurroundings(position, point => !field[point.Y, point.X].HasValue, previous))
return false;
return true;
}
// Checks the positions in 4 directions using the isValid predicate.
// Doesn't check the point that is given as the exclude parameter:
// this is used so that we don't count the point we just moved from
bool CheckSurroundings(Point position, Predicate<Point> isValid, Point? exclude = null)
{
foreach(Point move in moves.Values)
{
Point newPosition = position + move;
if(IsFieldPositionOutOfBounds(newPosition))
continue;
if(!isValid(newPosition) && (!exclude.HasValue || exclude.Value != newPosition))
return false;
}
return true;
}
bool IsFieldPositionOutOfBounds(Point position)
{
return position.Y >= field.GetLength(0) || position.X >= field.GetLength(1) || position.Y < 0 || position.X < 0;
}
}
并打印结果:
var spiral = MakeSpiral(9, 6);
for(int i = 0; i < spiral.GetLength(0); i++)
{
for(int j = 0; j < spiral.GetLength(1); j++)
{
Console.Write(spiral[i, j]?.ToString() ?? " ");
Console.Write(" ");
}
Console.WriteLine();
}
输出:
1 2 3 4 5 6
6
1 2 3 4 6
1 4 6
1 4 6
1 4 6
1 3 4 6
1 6
1 2 3 4 5 6
它不适用于大于 11 的值。下一个版本会更复杂一些:
var spiral = Spiral(14, 24);
for(int i = 0; i < spiral.GetLength(0); i++)
{
for(int j = 0; j < spiral.GetLength(1); j++)
{
Console.Write(spiral[i, j]?.ToString() ?? new string(' ', (j + 1).ToString().Length));
Console.Write(" ");
}
Console.WriteLine();
}
输出:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
24
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 24
1 22 24
1 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 22 24
1 3 20 22 24
1 3 5 6 7 8 9 10 11 12 13 14 15 16 17 18 20 22 24
1 3 5 18 20 22 24
1 3 5 20 22 24
1 3 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 22 24
1 3 22 24
1 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 24
1 24
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
它需要来自某个地方的 Point 结构,如果您不想导入任何依赖项,我也为此做了一个基本实现:
struct Point : IEquatable<Point>
{
public int X { get; set; }
public int Y { get; set; }
public Point(int x, int y) { X = x; Y = y; }
public static Point operator +(Point left, Point right) => new Point(left.X + right.X, left.Y + right.Y);
public static Point operator -(Point point) => new Point(-point.X, -point.Y);
public static Point operator -(Point left, Point right) => left + -right;
public static bool operator ==(Point left, Point right) => left.Equals(right);
public static bool operator !=(Point left, Point right) => !left.Equals(right);
public bool Equals(Point point) => this.X == point.X && this.Y == point.Y;
public override bool Equals(object other) => other is Point p ? Equals(p) : false;
public override int GetHashCode() => unchecked(17 * (X + 31 * Y));
}