Introduction
This is a sample application for training of algorithm, multi-thread, multi-language, GDI+, parsing, file-IO, ...
Background
An algorithm with multi-thread for best path found in the maze.
Using the Code
The "Kernel.Engine
" class and the "FindInputAndOutputPoints
/FindNextPoint
/AsyncStart
" methods are the main class/methods for the algorithm processing.
FindInputAndOutputPoints Method
pt.Row = 0;
for (pt.Column = (byte)(matrix.m_Size.Column - 2); pt.Column > 0; --pt.Column)
{
if (matrix.m_Buffer[pt.Row, pt.Column] == CellType.None)
{
switch (++iFindCount)
{
case 1:
inside1 = value1 = pt;
++inside1.Row;
break;
case 2:
inside2 = value2 = pt;
++inside2.Row;
break;
default:
return false;
}
}
}
pt.Column = (byte)(matrix.m_Size.Column - (byte)1);
for (pt.Row = (byte)(matrix.m_Size.Row - 2); pt.Row > 0; --pt.Row)
{
if (matrix.m_Buffer[pt.Row, pt.Column] == CellType.None)
{
switch (++iFindCount)
{
case 1:
inside1 = value1 = pt;
--inside1.Column;
break;
case 2:
inside2 = value2 = pt;
--inside2.Column;
break;
default:
return false;
}
}
}
pt.Row = (byte)(matrix.m_Size.Row - (byte)1);
for (pt.Column = (byte)(matrix.m_Size.Column - 2); pt.Column > 0; --pt.Column)
{
if (matrix.m_Buffer[pt.Row, pt.Column] == CellType.None)
{
switch (++iFindCount)
{
case 1:
inside1 = value1 = pt;
--inside1.Row;
break;
case 2:
inside2 = value2 = pt;
--inside2.Row;
break;
default:
return false;
}
}
}
pt.Column = 0;
for (pt.Row = (byte)(matrix.m_Size.Row - 2); pt.Row > 0; --pt.Row)
{
if (matrix.m_Buffer[pt.Row, pt.Column] == CellType.None)
{
switch (++iFindCount)
{
case 1:
inside1 = value1 = pt;
++inside1.Column;
break;
case 2:
inside2 = value2 = pt;
++inside2.Column;
break;
default:
return false;
}
}
}
The "FindInputAndOutputPoints
" method finds the start and end points in the matrix.
If only two blank points (hole) exists on the border of the matrix, this method works successfully and returns a "true
" value.
So, matrices that in their borders have less or more blank points (hole), are invalid and are not processed.
FindNextPoint Method
private static int FindNextPoint
(Matrix matrix, PointSpeedBuffer buffer, Point end, int index, bool first)
{
if ((index + 1) >= buffer.m_Capacity) return -1;
PointSide side = PointSide.None;
Point point = buffer.m_Buffer[index];
Point back = buffer.m_Buffer[index - 1];
Point next;
if (first)
{
next = point; --next.Row; if ((next != back) && (buffer.IndexOf(next) >= 0)) return -1;
if (next == end)
{
buffer.m_Buffer[index + 1] = next;
return +1;
}
next = point; ++next.Column; if ((next != back) && (buffer.IndexOf(next) >= 0)) return -1;
if (next == end)
{
buffer.m_Buffer[index + 1] = next;
return +1;
}
next = point; ++next.Row; if ((next != back) && (buffer.IndexOf(next) >= 0)) return -1;
if (next == end)
{
buffer.m_Buffer[index + 1] = next;
return +1;
}
next = point; --next.Column; if ((next != back) && (buffer.IndexOf(next) >= 0)) return -1;
if (next == end)
{
buffer.m_Buffer[index + 1] = next;
return +1;
}
}
else
{
next = buffer.m_Buffer[index + 1];
if (point.Column == next.Column)
{
if ((point.Row - 1) == next.Row)
side = PointSide.Top;
else if ((point.Row + 1) == next.Row)
side = PointSide.Bottom;
}
else if (point.Row == next.Row)
{
if ((point.Column - 1) == next.Column)
side = PointSide.Left;
else if ((point.Column + 1) == next.Column)
side = PointSide.Right;
}
}
byte maxRow = (byte)(matrix.m_Size.Row - 2);
byte maxCol = (byte)(matrix.m_Size.Column - 2);
while (true)
{
++side;
next = point.GetSidePoint(side);
if (next == point) return -1;
if ((next.Row <= 0) || (next.Column <= 0) ||
(next.Row > maxRow) || (next.Column > maxCol)) continue;
if (matrix.m_Buffer[next.Row, next.Column] == CellType.Fix) continue;
if (next == back) continue;
buffer.m_Buffer[index + 1] = next;
return +1;
}
}
The "FindNextPoint
" method chooses the next point for the path.
Each point in the matrix, has four sides ...
Top, Right, Bottom, Left (clock work[CW]).
The "side
" variant specifies the direction start for scanning and processing the next point.
The "first
" argument specifies that the current point (that its next point, should be chosen) ...
Is it a new point?
Or ...
Does it return back algorithm to this point?
[first==true]
If this point is a new point...
So, method will ensure that this point isn't adjacent to other points in the current path (because, it can't be a member of the shortest path!)
side = Top = first direction. (clock work[CW])
[first==false]
If this point isn't a new point...
So, "side
" variant, calculate and set with consideration in the last forward point (for the current point)
Finally, "while
" loop with consideration to "side
" and clock work (top, right, bottom, left) chooses the new next point.
If the "left
" direction has been tested and is not effective...
So, testing was processed in four directions and does not have a favorable response, the algorithm should go back to a point.
AsyncStart
Point ptStart, ptEnd;
Point ptStartInside, ptEndInside; if (!FindInputAndOutputPoints
(this.m_Matrix, out ptStart, out ptStartInside, out ptEnd, out ptEndInside)) return;
pathMin = new PointSpeedBuffer((this.m_Matrix.m_Size.Column - 2) *
(this.m_Matrix.m_Size.Row - 2));
if ((ptStart == ptEndInside) || (ptStartInside == ptEnd))
{
pathMin.m_Buffer[pathMin.m_Count++] = ptStart;
pathMin.m_Buffer[pathMin.m_Count++] = ptEnd;
return;
}
var matrix = new Matrix(this.m_Matrix);
matrix.ClearWay();
RemoveClosePoints_Pass1(matrix);
RemoveClosePoints_Pass2(matrix);
if (
(matrix.m_Buffer[ptStartInside.Row, ptStartInside.Column] == CellType.Fix) ||
(matrix.m_Buffer[ptEndInside.Row, ptEndInside.Column] == CellType.Fix)
)
{
pathMin = null;
return;
}
var pathCur = new PointSpeedBuffer((this.m_Matrix.m_Size.Column - 2) *
(this.m_Matrix.m_Size.Row - 2));
pathMin.m_Count = int.MaxValue;
pathCur.m_Buffer[pathCur.m_Count++] = ptStart;
pathCur.m_Buffer[pathCur.m_Count++] = ptStartInside;
--pathCur.m_Count;
int iFront = 1;
while (true)
{
if (this.m_Version != version) return;
if (this.m_DebugMode)
{
this.OnResult(new ResultEventArgs(pathCur, false));
System.Threading.Thread.Sleep(SLEEPINDEBUGMODE);
}
if (iFront > 0)
{
++pathCur.m_Count;
}
else
{
pathCur.m_Count += iFront;
if (pathCur.m_Count <= 1) break;
}
iFront = FindNextPoint
(matrix, pathCur, ptEndInside, pathCur.m_Count - 1, iFront > 0);
if (iFront > 0)
{
if (pathCur.m_Buffer[pathCur.m_Count] == ptEndInside) {
if (pathCur.m_Count < (pathCur.m_Capacity - 2))
{
++pathCur.m_Count;
pathCur.m_Buffer[pathCur.m_Count++] = ptEnd;
if (pathCur.m_Count < pathMin.m_Count)
{
pathMin.Fill(pathCur);
this.OnResult(new ResultEventArgs(pathMin, false));
}
if (pathCur.m_Count <= 5) break;
iFront = -3;
}
else
{
iFront = -1;
}
}
else
{
iFront = +1;
}
}
else if (pathCur.m_Count <= 2)
{
break;
}
}
The "AsyncStart
" method does general processing for the algorithm.
This method will process all paths.
Every time it finds a smaller way, it is copied in the "pathMin
" variant and, raises the "Result
" event for the UI update.
Finally, when all cases were processed
and, the algorithm go back to successive
and, when algorithm comes to start-point, the processing is finished
and, all cases are processed
and, shortest path has been found in "pathMin
" variant.
Using the Demo
To use the demo program example, note that the matrix should have only and only two blank points (hole) on its border.
(For more information, please review descriptions for the "FindInputAndOutputPoints
" method.)
Points of Interest
- Save/Load the maze and path in *.txt or *.map file
- Debug and Trace Mode
- Multi-thread processing
- Multi-language
History
- 18 July, 2010: Initial post
- 20 July, 2010:
- Added explanation
- Added 'Debug and Trace Mode'
- Added
RemoveClosePoints_Pass1
for speed and performance
- Added
IMatrixSerializer
- 21 July, 2010:
- Solved a bug in stopping in 'Debug and Trace Mode'
Thanks!