C#
(通过LINQPad,在“ C#程序”模式下;)
在任何高尔夫运动中都必须应用公平的行程控制,但这是我在C#中的方法(好吧,LINQPad,但是谁想要使整个C#应用程序正常工作的所有样板?)。
网格定义是可变的,具有许多垂直管道和整个结构的高度,并且通过传递种子可重复地随机(请参阅PipeGrid
构造函数)。
在没有关于对象可能以哪种方向流动的确切方式的明确答案的情况下,我允许您从多个选项中指定行为(请参见SolveBehavior
枚举/ PipeSolver
构造函数)。
垂直开始是可以定义的(请参阅参考资料PipeSolver.Solve
)。
我假设水平管始终位于两个相邻的垂直管之间,即没有水平管可以绕过水平管。
///<summary>Entry point</summary>
void Main()
{
var grid = new PipeGrid(vertical:10, height:10, seed:5);
var solver = new PipeSolver(grid, SolveBehavior.FlipFlop);
solver.Solve(start:2);
}
///<summary>Represents the direction the object is travelling</summary>
enum Direction
{
Down = 0,
Left = 1,
Right = 2
}
///<summary>Determines the route to take if a junction yields both horizontal directions</summary>
enum SolveBehavior
{
///<summary>Throws an <see cref="InvalidOperationException" /></summary>
Fail = 0,
///<summary>Prefers the left-most direction (screen-relative)</summary>
FavorLeft = 1,
///<summary>Prefers the right-most direction (screen-relative)</summary>
FavorRight = 2,
///<summary>Alternates preferred direction, based on the number of turns</summary>
FlipFlop = 3,
///<summary>Prefers the same direction the object travelled, on its last horizontal movement</summary>
SameDirection = 4,
///<summary>Prefers the opposite direction the object travelled, on its last horizontal movement</summary>
Uturn = 5
}
///<summary>Provides the logic for solving a <see cref="PipeGrid" /></summmary>
class PipeSolver
{
///<summary>Creates a new <see cref="PipeSolver" /> for the supplied <paramref name="grid" />,
///with the given <paramref name="behavior" /> used to resolve junctions with both horizontal
///paths</summary>
public PipeSolver(PipeGrid grid, SolveBehavior behavior = SolveBehavior.FlipFlop)
{
if (grid == null) throw new ArgumentNullException("grid");
_grid = grid;
_behavior = behavior;
}
private readonly PipeGrid _grid;
private readonly SolveBehavior _behavior;
///<summary>Simulate the dropping of an object to run through the grid, at the top of a
///given <paramref name="start" /> vertical pipe</summary>
public void Solve(int start = 1, bool dumpFrames = false, string tag = "Result")
{
if (start < 1) start = 1;
if (start > _grid.Verticals) start = _grid.Verticals;
int x, y;
Direction?[,] path = new Direction?[_grid.Width, _grid.Height];
x = (start - 1) * 2;
y = 0;
Direction dir = Direction.Down, lastDir = Direction.Down;
int turns = 0;
do
{
path[x, y] = dir; // we moved through this pipe
// rule 1: when moving through horizontal pipe, object will go down when possible
if ((dir == Direction.Left || dir == Direction.Right) && (x % 2 == 0))
{
lastDir = dir;
dir = Direction.Down;
++turns;
}
// rule 2: when moving through start pipe, object will turn into horizontal pipe when possible
else if (dir == Direction.Down)
{
bool hasLeft = (x > 0 && _grid[x - 1, y]);
bool hasRight = (x < _grid.Width - 1 && _grid[x + 1, y]);
if (hasLeft && hasRight)
{
switch (_behavior)
{
case SolveBehavior.FavorLeft:
hasRight = false; // "forget" about right pipe
break;
case SolveBehavior.FavorRight:
hasLeft = false; // "forget" about left pipe
break;
case SolveBehavior.FlipFlop:
if (turns % 2 == 0) hasLeft = false;
else hasRight = false; // "forget" about left on the even moves, or right on the odd moves
break;
case SolveBehavior.SameDirection: // force staying in the same direction
if (lastDir == Direction.Left) hasRight = false;
else if (lastDir == Direction.Right) hasLeft = false;
else goto case SolveBehavior.FlipFlop; // use the flip-flop behaviour to determine first turn
break;
case SolveBehavior.Uturn: // force turning back on itself
if (lastDir == Direction.Left) hasLeft = false;
else if (lastDir == Direction.Right) hasRight = false;
else goto case SolveBehavior.FlipFlop; // use the flip-flop behaviour to determine first turn
break;
default: throw new InvalidOperationException(
"Failed to find distinct path, with no resolving behavior defined"
);
}
}
if (hasLeft) dir = Direction.Left;
else if (hasRight) dir = Direction.Right;
if (hasLeft || hasRight) ++turns;
}
switch (dir) // update position, based on current direction
{
case Direction.Left: if (x > 0) --x; break;
case Direction.Right: if (x < _grid.Width - 1) ++x; break;
default: ++y; break;
}
if (dumpFrames)
{
DumpFrame(path, start, tag:string.Concat("Frame #", turns, " (", _grid.Seed, ")"));
DrawFrame(path, start, tag:string.Concat("Frame #", turns));
}
}
while (y < _grid.Height);
int end = (x / 2) + 1;
DumpFrame(path, start, end, turns, tag);
DrawFrame(path, start, end, turns, tag);
}
///<summary>Internal method for drawing a given frame</summary>
private void DumpFrame(Direction?[,] path, int start, int? end = null, int? turns = null, string tag = null)
{
var builder = new StringBuilder();
builder.Append(' ', --start * 5).AppendLine("v");
for (int y = 0; y < _grid.Height; y++)
{
for (int x = 0; x < _grid.Width; x++)
{
builder.Append(
(x % 2 == 0)
? path[x, y].HasValue ? ":" : _grid[x, y] ? "|" : " "
: path[x, y].HasValue ? "====" : _grid[x, y] ? "----" : " "
);
}
builder.AppendLine();
}
if (end.HasValue) builder.Append(' ', (end.Value - 1) * 5).AppendLine("^");
if (turns.HasValue) builder.Append(turns.Value)
.Append(" turns were taken to get to ")
.AppendLine(end.HasValue ? "the end." : "this point.");
builder.ToString().Dump(string.IsNullOrWhiteSpace(tag) ? "Frame" : tag);
}
///<summary>Internal method for rendering a frame as a bitmap</summary>
private void DrawFrame(Direction?[,] path, int start, int? end = null, int? turns = null, string tag = null)
{
using (var sprites = new Sprites())
using (var canvas = new Bitmap(16 * _grid.Width, 16 * (_grid.Height + 3)))
using (var graphics = Graphics.FromImage(canvas))
{
graphics.FillRectangle(Brushes.Green, 0, 16, 16 * _grid.Width, 16 * _grid.Height);
_grid.Draw(graphics, sprites, offsetX:0, offsetY:16);
// draw the start position
start = (start - 1) * 32;
graphics.DrawImageUnscaled(sprites.RoadVertical, start, 0);
graphics.DrawImageUnscaled(sprites.CarVertical, start, 0);
graphics.DrawImageUnscaled(sprites.StartFlag, start, 0);
// draw the path
for (int y = 0; y < _grid.Height; y++)
for (int x = 0; x < _grid.Width; x++)
{
if (path[x, y].HasValue)
{
Image car;
switch (path[x, y])
{
case Direction.Left:
// if even, then on a vertical, so turning left; otherwise travelling left
car = (x % 2 == 0) ? sprites.CarTurnLeft : sprites.CarLeft;
break;
case Direction.Right:
// if even, then on a vertical, so turning right; otherwise travelling right
car = (x % 2 == 0) ? sprites.CarTurnRight: sprites.CarRight;
break;
default:
car = sprites.CarVertical;
if (x == 0 && path[x + 1, y].HasValue) // far-left and will move right = turn-right
car = sprites.CarTurnRight;
else if (x == _grid.Width - 1 && path[x - 1, y].HasValue) // far-right and will move left = turn-left
car = sprites.CarTurnLeft;
else if (x > 0 && x < _grid.Width - 1)
{
car = sprites.CarVertical; // if not right or left, then down
if (path[x + 1, y].HasValue && !path[x - 1, y].HasValue) // if came from the left, then turn right
car = sprites.CarTurnRight;
else if (path[x - 1, y].HasValue && !path[x + 1, y].HasValue) // if came from the right, then turn left
car = sprites.CarTurnLeft;
}
break;
}
graphics.DrawImageUnscaled(car, 16 * x, 16 * (y + 1));
}
}
// draw the end position, if we are at the end
if (end.HasValue)
{
end = (end - 1) * 32;
graphics.DrawImageUnscaled(sprites.RoadVertical, end.Value, 16 * (_grid.Height + 1));
graphics.DrawImageUnscaled(sprites.CarVertical, end.Value, 16 * (_grid.Height + 1));
graphics.DrawImageUnscaled(sprites.EndFlag, end.Value, 16 * (_grid.Height + 1));
}
if (turns.HasValue)
{
string s = string.Concat(turns.Value, " turns were taken to get to ",
end.HasValue ? "the end." : "this point.");
graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAliasGridFit;
graphics.DrawString(s, SystemFonts.DefaultFont, Brushes.Black, 0, 16 * (_grid.Height + 2));
}
canvas.Dump(tag ?? "Bonus");
}
}
}
///<summary>Represents a configuration of pipes</summary>
class PipeGrid
{
///<summary>Creates a new <see cref="PipeGrid" />, of a given <paramref name="height" />
///with the given number of <paramref name="vertical" /> pipes, and randomly distributes
///horizontal pipes between them, based on a repeatable <paramref name="seed" />.</summary>
public PipeGrid(int vertical = 4, int height = 8, int? seed = null)
{
if (vertical < 2) vertical = 2;
if (height < 2) height = 2;
Width = (2 * vertical) - 1;
Height = height;
Verticals = vertical;
Seed = seed ?? Environment.TickCount;
var rnd = new Random(Seed);
_nodes = new bool[Width,Height];
for (int x = 0, xw = Width; x < xw; x++)
for (int y = 0; y < height; y++)
{
// place verticals in every even column, and randomly place horizontals in odd columns
if (x % 2 == 0 || rnd.Next(0, 2) == 1)
_nodes[x, y] = true;
}
}
private readonly bool[,] _nodes;
public int Width { get; private set; }
public int Height { get; private set; }
public int Verticals { get; private set; }
public int Seed { get; private set; }
public bool this[int x, int y] { get { return _nodes[x, y]; } }
///<summary>Renders the grid to the LINQPad results pane, for inspection</summary>
public PipeGrid Dump(string tag = null)
{
var builder = new StringBuilder();
for (int y = 0; y < Height; y++)
{
for (int x = 0; x < Width; x++)
{
builder.Append(
(x % 2 == 0)
? _nodes[x, y] ? "|" : " "
: _nodes[x, y] ? "----" : " "
);
}
builder.AppendLine();
}
builder.ToString().Dump(string.IsNullOrWhiteSpace(tag) ? "Grid" : tag);
return this;
}
///<summary>Render the grid as a bitmap image</summary>
public void Draw(Graphics g, Sprites s, int offsetX = 0, int offsetY = 0)
{
for (int y = 0; y < Height; y++)
{
for (int x = 0; x < Width; x++)
{
if (_nodes[x, y])
{
Image sprite = sprite = s.RoadVertical;
if (x % 2 != 0)
sprite = s.RoadHorizontal;
else if (x == 0 && _nodes[1, y])
sprite = s.JunctionTeeRight;
else if (x == Width - 1 && _nodes[x - 1, y])
sprite = s.JunctionTeeLeft;
else if (x > 0 && x < Width - 1)
{
if (_nodes[x - 1, y] && _nodes[x + 1, y])
sprite = s.JunctionCross;
else if (_nodes[x + 1, y] && !_nodes[x - 1, y])
sprite = s.JunctionTeeRight;
else if (_nodes[x - 1, y] && !_nodes[x + 1, y])
sprite = s.JunctionTeeLeft;
}
g.DrawImageUnscaled(sprite,
x:(16 * x) + offsetX,
y:(16 * y) + offsetY);
}
}
}
}
///<summary>Creates a <see cref="PipeGrid" /> with horizontal pipes at all possible positions</summary>
public static PipeGrid CreateAllOpen(int verticals = 4, int height = 8)
{
var grid = new PipeGrid(verticals, height, 0);
for (int y = 0; y < height; y++)
for (int x = 0, xw = grid.Width; x < xw; x++)
grid._nodes[x, y] = true;
return grid;
}
}
///<summary>Store tile sprites, to be used in the graphical rendering of the result</summary>
class Sprites : IDisposable
{
public Sprites()
{
byte[,] car = new byte[,] {
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0 },
{ 0, 0, 0, 1, 3, 3, 3, 3, 3, 3, 3, 3, 1, 0, 0, 0 },
{ 0, 0, 0, 1, 3, 1, 1, 1, 1, 1, 1, 3, 1, 0, 0, 0 },
{ 0, 0, 0, 1, 3, 1, 1, 1, 1, 1, 1, 3, 1, 0, 0, 0 },
{ 0, 0, 0, 0, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 3, 1, 1, 1, 1, 1, 1, 3, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 3, 1, 1, 1, 1, 1, 1, 3, 0, 0, 0, 0 },
{ 0, 0, 0, 1, 3, 1, 1, 1, 1, 1, 1, 3, 1, 0, 0, 0 },
{ 0, 0, 0, 1, 3, 3, 3, 3, 3, 3, 3, 3, 1, 0, 0, 0 },
{ 0, 0, 0, 1, 3, 3, 3, 3, 3, 3, 3, 3, 1, 0, 0, 0 },
{ 0, 0, 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0 },
{ 0, 0, 0, 0, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
};
byte[,] road = new byte[,] {
{ 0, 6, 2, 2, 2, 2, 2, 6, 6, 2, 2, 2, 2, 2, 6, 0 },
{ 0, 6, 2, 2, 2, 2, 2, 6, 6, 2, 2, 2, 2, 2, 6, 0 },
{ 0, 6, 2, 2, 2, 2, 2, 6, 6, 2, 2, 2, 2, 2, 6, 0 },
{ 0, 6, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 0 },
{ 0, 6, 2, 2, 2, 2, 2, 6, 6, 2, 2, 2, 2, 2, 6, 0 },
{ 0, 6, 2, 2, 2, 2, 2, 6, 6, 2, 2, 2, 2, 2, 6, 0 },
{ 0, 6, 2, 2, 2, 2, 2, 6, 6, 2, 2, 2, 2, 2, 6, 0 },
{ 0, 6, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 0 },
{ 0, 6, 2, 2, 2, 2, 2, 6, 6, 2, 2, 2, 2, 2, 6, 0 },
{ 0, 6, 2, 2, 2, 2, 2, 6, 6, 2, 2, 2, 2, 2, 6, 0 },
{ 0, 6, 2, 2, 2, 2, 2, 6, 6, 2, 2, 2, 2, 2, 6, 0 },
{ 0, 6, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 0 },
{ 0, 6, 2, 2, 2, 2, 2, 6, 6, 2, 2, 2, 2, 2, 6, 0 },
{ 0, 6, 2, 2, 2, 2, 2, 6, 6, 2, 2, 2, 2, 2, 6, 0 },
{ 0, 6, 2, 2, 2, 2, 2, 6, 6, 2, 2, 2, 2, 2, 6, 0 },
{ 0, 6, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 0 },
};
byte[,] roadNESW = new byte[,] {
{ 0, 6, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 0 },
{ 6, 6, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 6 },
{ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 },
{ 2, 2, 2, 5, 2, 2, 5, 2, 2, 5, 2, 2, 5, 2, 2, 2 },
{ 2, 2, 2, 2, 5, 5, 2, 5, 5, 2, 5, 5, 2, 2, 2, 2 },
{ 2, 2, 2, 2, 5, 5, 2, 5, 5, 2, 5, 5, 2, 2, 2, 2 },
{ 2, 2, 2, 5, 2, 2, 5, 2, 2, 5, 2, 2, 5, 2, 2, 2 },
{ 2, 2, 2, 2, 5, 5, 2, 5, 5, 2, 5, 5, 2, 2, 2, 2 },
{ 2, 2, 2, 2, 5, 5, 2, 5, 5, 2, 5, 5, 2, 2, 2, 2 },
{ 2, 2, 2, 5, 2, 2, 5, 2, 2, 5, 2, 2, 5, 2, 2, 2 },
{ 2, 2, 2, 2, 5, 5, 2, 5, 5, 2, 5, 5, 2, 2, 2, 2 },
{ 2, 2, 2, 2, 5, 5, 2, 5, 5, 2, 5, 5, 2, 2, 2, 2 },
{ 2, 2, 2, 5, 2, 2, 5, 2, 2, 5, 2, 2, 5, 2, 2, 2 },
{ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 },
{ 6, 6, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 6 },
{ 0, 6, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 0 },
};
byte[,] roadNES = new byte[,] {
{ 0, 6, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 0 },
{ 0, 6, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 6 },
{ 0, 6, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 },
{ 0, 6, 2, 5, 2, 2, 5, 2, 2, 5, 2, 2, 5, 2, 2, 2 },
{ 0, 6, 2, 2, 5, 5, 2, 5, 5, 2, 5, 5, 2, 2, 2, 2 },
{ 0, 6, 2, 2, 5, 5, 2, 5, 5, 2, 5, 5, 2, 2, 2, 2 },
{ 0, 6, 2, 5, 2, 2, 5, 2, 2, 5, 2, 2, 5, 2, 2, 2 },
{ 0, 6, 2, 2, 5, 5, 2, 5, 5, 2, 5, 5, 2, 2, 2, 2 },
{ 0, 6, 2, 2, 5, 5, 2, 5, 5, 2, 5, 5, 2, 2, 2, 2 },
{ 0, 6, 2, 5, 2, 2, 5, 2, 2, 5, 2, 2, 5, 2, 2, 2 },
{ 0, 6, 2, 2, 5, 5, 2, 5, 5, 2, 5, 5, 2, 2, 2, 2 },
{ 0, 6, 2, 2, 5, 5, 2, 5, 5, 2, 5, 5, 2, 2, 2, 2 },
{ 0, 6, 2, 5, 2, 2, 5, 2, 2, 5, 2, 2, 5, 2, 2, 2 },
{ 0, 6, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 },
{ 0, 6, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 6 },
{ 0, 6, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 0 },
};
byte[,] start = new byte[,] {
{ 0, 0, 1, 1, 1, 0, 4, 4, 4, 0, 0, 0, 4, 0, 0, 0 },
{ 0, 0, 1, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0 },
{ 0, 0, 1, 1, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0 },
{ 0, 0, 1, 1, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0 },
{ 0, 0, 1, 1, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0 },
{ 0, 0, 1, 1, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0 },
{ 0, 0, 1, 1, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0 },
{ 0, 0, 1, 1, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0 },
{ 0, 0, 1, 4, 4, 4, 0, 0, 0, 4, 4, 4, 0, 0, 0, 0 },
{ 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
};
byte[,] end = new byte[,] {
{ 0, 0, 1, 1, 1, 0, 1, 1, 6, 0, 0, 0, 6, 0, 0, 0 },
{ 0, 0, 1, 6, 6, 6, 1, 1, 6, 6, 1, 1, 6, 0, 0, 0 },
{ 0, 0, 1, 1, 6, 6, 6, 6, 1, 6, 1, 1, 1, 0, 0, 0 },
{ 0, 0, 1, 1, 1, 1, 6, 6, 1, 1, 6, 6, 1, 0, 0, 0 },
{ 0, 0, 1, 1, 1, 1, 1, 1, 6, 1, 6, 6, 0, 0, 0, 0 },
{ 0, 0, 1, 1, 6, 6, 1, 1, 6, 6, 1, 1, 0, 0, 0, 0 },
{ 0, 0, 1, 1, 6, 6, 6, 6, 1, 6, 1, 1, 0, 0, 0, 0 },
{ 0, 0, 1, 1, 1, 1, 6, 6, 1, 1, 6, 6, 0, 0, 0, 0 },
{ 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 6, 6, 0, 0, 0, 0 },
{ 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
};
RoadVertical = Sprite(road);
RoadHorizontal = RotateSprite(RoadVertical, 90);
JunctionCross = Sprite(roadNESW);
JunctionTeeRight = Sprite(roadNES);
JunctionTeeLeft = FlipSprite(JunctionTeeRight, horizontal:true);
CarVertical = Sprite(car);
CarLeft = RotateSprite(CarVertical, 90);
CarRight = FlipSprite(CarLeft, horizontal:true);
CarTurnLeft = RotateSprite(CarVertical, 45);
CarTurnRight = FlipSprite(CarTurnLeft, horizontal:true);
StartFlag = Sprite(start);
EndFlag = Sprite(end);
}
public Image RoadVertical { get; private set; }
public Image RoadHorizontal { get; private set; }
public Image JunctionCross { get; private set; }
public Image JunctionTeeLeft { get; private set; }
public Image JunctionTeeRight { get; private set; }
public Image CarVertical { get; private set; }
public Image CarLeft { get; private set; }
public Image CarRight { get; private set; }
public Image CarTurnLeft { get; private set; }
public Image CarTurnRight { get; private set; }
public Image StartFlag { get; private set; }
public Image EndFlag { get; private set; }
///<summary>Create a sprite from the byte data</summary>
private Image Sprite(byte[,] data)
{
int width = data.GetLength(0);
int height = data.GetLength(1);
var image = new Bitmap(width, height);
for (int y = 0; y < height; y++)
for (int x = 0; x < width; x++)
{
Color c;
switch (data[y,x])
{
case 1: c = Color.Black; break;
case 2: c = Color.DarkGray; break;
case 3: c = Color.Red; break;
case 4: c = Color.LimeGreen; break;
case 5: c = Color.Yellow; break;
case 6: c = Color.White; break;
default: continue;
}
image.SetPixel(x, y, c);
}
return image;
}
///<summary>Rotate an image by a number of <paramref name="degrees" /> around the centre</summary>
private Image RotateSprite(Image source, float deg)
{
var b = new Bitmap(source.Width, source.Height);
using (var g = Graphics.FromImage(b))
{
float tx = (float)source.Width / 2.0f;
float ty = (float)source.Height / 2.0f;
g.TranslateTransform(tx, ty);
g.RotateTransform(deg);
g.TranslateTransform(-tx, -ty);
g.DrawImageUnscaled(source, 0, 0);
}
return b;
}
///<summary>Flip an image about its centre</summary>
private Image FlipSprite(Image source, bool horizontal = false, bool vertical = false)
{
var b = new Bitmap(source);
RotateFlipType rft = ( horizontal && vertical) ? RotateFlipType.RotateNoneFlipXY
: ( horizontal && !vertical) ? RotateFlipType.RotateNoneFlipX
: (!horizontal && vertical) ? RotateFlipType.RotateNoneFlipY
: RotateFlipType.RotateNoneFlipNone;
b.RotateFlip(rft);
return b;
}
#region IDisposable implementation
public void Dispose() { Dispose(true); }
~Sprites() { Dispose(false); }
protected void Dispose(bool disposing)
{
if (disposing)
{
GC.SuppressFinalize(this);
using (RoadVertical) { }
using (RoadHorizontal) { }
using (JunctionCross) { }
using (JunctionTeeLeft) { }
using (JunctionTeeRight) { }
using (CarVertical) { }
using (CarLeft) { }
using (CarRight) { }
using (CarTurnLeft) { }
using (CarTurnRight) { }
using (StartFlag) { }
using (EndFlag) { };
}
RoadVertical = null;
RoadHorizontal = null;
JunctionCross = null;
JunctionTeeLeft = null;
JunctionTeeRight = null;
CarVertical = null;
CarLeft = null;
CarRight = null;
CarTurnLeft = null;
CarTurnRight = null;
StartFlag = null;
EndFlag = null;
}
#endregion
}
更新:
在这种流行情况下,由于担心我的普通旧文本输出可能有些单调,因此我提供了扩展版本,该版本也绘制了作为图像的路径。我将其建模为汽车,穿越了可怕的道路网络,试图探底,但是使用了世界上最糟糕的GPS,迫使您在每个路口转弯。
另外,这也使“转弯”更加清晰。
享受-vroom vroom !!
结果示例:
(垂直:10,高度:10,随机种子:5,起始管道:2,解决行为:FlipFlop})