Shortest path for king on chessboard - algorithm

I have a 8x8 chessboard. This is info I get:
coordinates of the king
coordinates of the goal
number of blocked squares
coordinates of blocked squares
I cannot step on blocked squares. I want to find shortest path to goal, if no path is available (the goal is unreachable), I want to return -1.
I tried my hand at it, but I am not sure if the code makes any sense and I am kinda lost, any help is greatly appreciated.
Program ShortestPath;
TYPE
coords = array [0..1] of integer;
var goal,shortest : coords;
currentX, currentY,i : integer;
arrBlocked,result : array [0..64] of coords;
function findShortestPath (currentX, currentY, goal, arrBlocked,path,i) : array [0..64] of coords;
begin
{check if we are still on board}
if (currentX < 1 OR currentX > 8 OR currentY < 1 OR currentY > 8) then begin
exit;
end;
if (currentX = arrBlocked[currentX] AND currentY = arrBlocked[currentY]) then begin
exit;
end;
{save the new square into path}
path[i] = currentX;
path[i+1] = currentY;
{check if we reached the goal}
if (currentX = goal[0]) and (currentY = goal[1]) then begin
{check if the path was the shortest so far}
if (shortest > Length(path)) then begin
shortest := Length(path);
findShortestPath := path;
end else begin
exit;
end;
end else begin
{move on the board}
findShortestPath(currentX+1, currentY, goal, arrBlocked,path,i+2);
findShortestPath(currentX, currentY+1, goal, arrBlocked,path,i+2);
findShortestPath(currentX-1, currentY, goal, arrBlocked,path,i+2);
findShortestPath(currentX, currentY-1, goal, arrBlocked,path,i+2);
end;
end;
begin
{test values}
currentX = 2;
currentY = 5;
goal[0] = 8;
goal[1] = 7;
arrBlocked[0] = [4,3];
arrBlocked[1] = [2,2];
arrBlocked[2] = [8,5];
arrBlocked[3] = [7,6];
i := 0;
shortest := 9999;
path[i] = currentX;
path[i+1] = currentY;
i := i + 2;
result := findShortestPath(currentX,currentY,goal,arrBlocked,path,i);
end.

The task in the current case (small board with only 64 cells) can be solved without recursion in the following way.
Program ShortestPath;
type
TCoords = record
X, Y: byte;
end;
TBoardArray = array [0 .. 63] of TCoords;
var
Goal: TCoords;
Current: TCoords;
i, j: integer;
ArrBlocked, PathResult: TBoardArray;
BlockedCount: byte;
Board: array [1 .. 8, 1 .. 8] of integer;
procedure CountTurnsToCells;
var
Repetitions: byte;
BestPossible: byte;
begin
for Repetitions := 1 to 63 do
for j := 1 to 8 do
for i := 1 to 8 do
if Board[i, j] <> -2 then
begin
BestPossible := 255;
if (i < 8) and (Board[i + 1, j] >= 0) then
BestPossible := Board[i + 1, j] + 1;
if (j < 8) and (Board[i, j + 1] >= 0) and
(BestPossible > Board[i, j + 1] + 1) then
BestPossible := Board[i, j + 1] + 1;
if (i > 1) and (Board[i - 1, j] >= 0) and
(BestPossible > Board[i - 1, j] + 1) then
BestPossible := Board[i - 1, j] + 1;
if (j > 1) and (Board[i, j - 1] >= 0) and
(BestPossible > Board[i, j - 1] + 1) then
BestPossible := Board[i, j - 1] + 1;
{ diagonal }
if (j > 1) and (i > 1) and (Board[i - 1, j - 1] >= 0) and
(BestPossible > Board[i - 1, j - 1] + 1) then
BestPossible := Board[i - 1, j - 1] + 1;
if (j > 1) and (i < 8) and (Board[i + 1, j - 1] >= 0) and
(BestPossible > Board[i + 1, j - 1] + 1) then
BestPossible := Board[i + 1, j - 1] + 1;
if (j < 8) and (i < 8) and (Board[i + 1, j + 1] >= 0) and
(BestPossible > Board[i + 1, j + 1] + 1) then
BestPossible := Board[i + 1, j + 1] + 1;
if (j < 8) and (i > 1) and (Board[i - 1, j + 1] >= 0) and
(BestPossible > Board[i - 1, j + 1] + 1) then
BestPossible := Board[i - 1, j + 1] + 1;
if (BestPossible < 255) and
((Board[i, j] = -1) or (Board[i, j] > BestPossible)) then
Board[i, j] := BestPossible;
end;
end;
function GetPath: TBoardArray;
var
n, TurnsNeeded: byte;
NextCoord: TCoords;
function FindNext(CurrentCoord: TCoords): TCoords;
begin
result.X := 0;
result.Y := 0;
if (CurrentCoord.X > 1) and (Board[CurrentCoord.X - 1, CurrentCoord.Y] >= 0)
and (Board[CurrentCoord.X - 1, CurrentCoord.Y] < Board[CurrentCoord.X,
CurrentCoord.Y]) then
begin
result.X := CurrentCoord.X - 1;
result.Y := CurrentCoord.Y;
exit;
end;
if (CurrentCoord.Y > 1) and (Board[CurrentCoord.X, CurrentCoord.Y - 1] >= 0)
and (Board[CurrentCoord.X, CurrentCoord.Y - 1] < Board[CurrentCoord.X,
CurrentCoord.Y]) then
begin
result.X := CurrentCoord.X;
result.Y := CurrentCoord.Y - 1;
exit;
end;
if (CurrentCoord.X < 8) and (Board[CurrentCoord.X + 1, CurrentCoord.Y] >= 0)
and (Board[CurrentCoord.X + 1, CurrentCoord.Y] < Board[CurrentCoord.X,
CurrentCoord.Y]) then
begin
result.X := CurrentCoord.X + 1;
result.Y := CurrentCoord.Y;
exit;
end;
if (CurrentCoord.Y < 8) and (Board[CurrentCoord.X, CurrentCoord.Y + 1] >= 0)
and (Board[CurrentCoord.X, CurrentCoord.Y + 1] < Board[CurrentCoord.X,
CurrentCoord.Y]) then
begin
result.X := CurrentCoord.X;
result.Y := CurrentCoord.Y + 1;
exit;
end;
{ diagonal }
if (CurrentCoord.X > 1) and (CurrentCoord.Y > 1) and
(Board[CurrentCoord.X - 1, CurrentCoord.Y-1] >= 0) and
(Board[CurrentCoord.X - 1, CurrentCoord.Y-1] < Board[CurrentCoord.X,
CurrentCoord.Y]) then
begin
result.X := CurrentCoord.X - 1;
result.Y := CurrentCoord.Y - 1;
exit;
end;
if (CurrentCoord.X < 8) and (CurrentCoord.Y > 1) and
(Board[CurrentCoord.X + 1, CurrentCoord.Y-1] >= 0) and
(Board[CurrentCoord.X + 1, CurrentCoord.Y-1] < Board[CurrentCoord.X,
CurrentCoord.Y]) then
begin
result.X := CurrentCoord.X + 1;
result.Y := CurrentCoord.Y - 1;
exit;
end;
if (CurrentCoord.X < 8) and (CurrentCoord.Y < 8) and
(Board[CurrentCoord.X + 1, CurrentCoord.Y+1] >= 0) and
(Board[CurrentCoord.X + 1, CurrentCoord.Y+1] < Board[CurrentCoord.X,
CurrentCoord.Y]) then
begin
result.X := CurrentCoord.X + 1;
result.Y := CurrentCoord.Y + 1;
exit;
end;
if (CurrentCoord.X > 1) and (CurrentCoord.Y < 8) and
(Board[CurrentCoord.X - 1, CurrentCoord.Y+1] >= 0) and
(Board[CurrentCoord.X - 1, CurrentCoord.Y+1] < Board[CurrentCoord.X,
CurrentCoord.Y]) then
begin
result.X := CurrentCoord.X - 1;
result.Y := CurrentCoord.Y + 1;
exit;
end;
end;
begin
TurnsNeeded := Board[Goal.X, Goal.Y];
NextCoord := Goal;
for n := TurnsNeeded downto 1 do
begin
result[n] := NextCoord;
NextCoord := FindNext(NextCoord);
end;
result[0] := NextCoord; // starting position
end;
procedure BoardOutput;
begin
for j := 1 to 8 do
for i := 1 to 8 do
if i = 8 then
writeln(Board[i, j]:2)
else
write(Board[i, j]:2);
end;
procedure OutputTurns;
begin
writeln(' X Y');
for i := 0 to Board[Goal.X, Goal.Y] do
writeln(PathResult[i].X:2, PathResult[i].Y:2)
end;
begin
{ test values }
Current.X := 2;
Current.Y := 5;
Goal.X := 8;
Goal.Y := 7;
ArrBlocked[0].X := 4;
ArrBlocked[0].Y := 3;
ArrBlocked[1].X := 2;
ArrBlocked[1].Y := 2;
ArrBlocked[2].X := 8;
ArrBlocked[2].Y := 5;
ArrBlocked[3].X := 7;
ArrBlocked[3].Y := 6;
BlockedCount := 4;
{ preparing the board }
for j := 1 to 8 do
for i := 1 to 8 do
Board[i, j] := -1;
for i := 0 to BlockedCount - 1 do
Board[ArrBlocked[i].X, ArrBlocked[i].Y] := -2; // the blocked cells
Board[Current.X, Current.Y] := 0; // set the starting position
CountTurnsToCells;
BoardOutput;
if Board[Goal.X, Goal.Y] < 0 then
writeln('no path') { there is no path }
else
begin
PathResult := GetPath;
writeln;
OutputTurns
end;
readln;
end.
The ideea is the following. We use an array representing the board. Each cell can be set either to 0 - starting point, either to -1 - unknown/unreachable cell, either to -2 - blocked cell. All positive numbers represent the minimum turns to reach the current cell form the starting point.
Later on we check if the goal cell contains a number greater then 0. This means that the king can move to the destination cell. If so we find the cells with ordinal numbers following each other from goal to starting point and represent them in the decision array.
The two additional procedures: BoardOutput and OutputTurns print the Board structure and the decision to the console.

Because the dimensions of your problem is so small you are not bound to use the most efficient method. So you can use BFS to find the shortest path because first the cost of moving is consistent second you won't face memory limit due to small size of the problem.
1 Breadth-First-Search(Graph, root):
2
3 for each node n in Graph:
4 n.distance = INFINITY
5 n.parent = NIL
6
7 create empty queue Q
8
9 root.distance = 0
10 Q.enqueue(root)
11
12 while Q is not empty:
13
14 current = Q.dequeue()
15
16 for each node n that is adjacent to current:
17 if n.distance == INFINITY:
18 n.distance = current.distance + 1
19 n.parent = current
20 Q.enqueue(n)
https://en.wikipedia.org/wiki/Breadth-first_search
But when the problem gets larger you are bound to use more efficient methods. The ultimate solution is using IDA*. Because IDA* space complexity is linear and it will always return the optimal solution if you use consistent heurisitc.

A* Search is a good path-finding algorithm for graphs like your chess board, a bit of googling located an implementation in C that you can adapt to Pascal.
A* works by exploring the most promising paths first using an admissible heuristic to determine which paths are (probably) the best, i.e. the search first explores the most direct path to the goal and only explores more circuitous paths if the direct paths are blocked. In your case you can either use the cartesian distance as your heuristic, or else you can use the Chebyshev distance aka the chessboard distance.

You can transform this a graph theory problem and then apply one of the standard algorithms.
You consider all fields of the chess board nodes in a graph. All fields y that the king can move to from a given field x are connected to x. So c4 is connected to b3, b4, b5, c3, c5, d3, d4, d5. Remove all the nodes, and their connections that are blocked.
Now finding your shortest path can be solved using the Dijkstras Algorithm
This is essentially what #asd-tm implements in his/her solution, but I think implementing the Dijkstra Algorithm for the general case and using it for the special case might lead to cleaner, easier to understand code. Hence the separate answer.

Related

Spiral Matrix Procedure in Maple

I am wanting to write a procedure to take a square matrix and have it output a spiral matrix.
for example;
M:=Matrix(3,[[1,2,3],[4,5,6],[7,8,9]]);
would turn into
S:=Matrix(3,[[1,2,3],[8,9,4],[7,6,5]]);
Starting in the top left corner and each row follows around clockwise until you reach the middle.
My first thought was I need to be able to call each element (m_i,j) from a matrix and tell it where to go. I could write a different procedure for each square matrix assigning where the elements in the matrix each should move to. Since I could not get it to work for n.
Here is what i have for a 3x3 matrix
Spiral := proc(a1,a2,a3,b1,b2,b3,c1,c2,c3)
local M,S;
M:=Matrix(3,[[a1,a2,a3],[b1,b2,b3],[c1,c2,c3]]);
S:=Matrix(3,[[a1,a2,a3],[c2,c3,b1],[c1,b3,b2]]);
print(M);
print(S);
end:
Spiral(1,2,3,4,5,6,7,8,9);
It is very difficult for me to find information about Matrices in Maple. Any hint on using Maple would be appreciated. Thank you.
I don't think there is a particularly compact solution to this problem. Here is a solution I came up with. It takes in a square Matrix as input and returns another Matrix which is the spiral of the input Matrix.
spiral:=proc(M::Matrix)
local size, spiralCount, currentRow, currentCol, MIndex, k;
local spiralMatrix;
size := numelems(M[1]);
spiralCount := size;
currentRow := 1;
currentCol := 0;
MIndex := 0;
spiralMatrix := Matrix(size, size);
while spiralCount > 0 do
for k from 1 to spiralCount do
currentCol:=currentCol + 1;
spiralMatrix[currentRow,currentCol] := M[iquo(MIndex,size) + 1,(MIndex mod size) + 1];
MIndex := MIndex + 1;
end do:
for k from 1 to spiralCount-1 do
currentRow:=currentRow + 1;
spiralMatrix[currentRow,currentCol] := M[iquo(MIndex,size) + 1,(MIndex mod size) + 1];
MIndex := MIndex + 1;
end do:
for k from 1 to spiralCount-1 do
currentCol:=currentCol - 1;
spiralMatrix[currentRow,currentCol] := M[iquo(MIndex,size) + 1,(MIndex mod size) + 1];
MIndex := MIndex + 1;
end do:
for k from 1 to spiralCount-2 do
currentRow:=currentRow - 1;
spiralMatrix[currentRow,currentCol] := M[iquo(MIndex,size) + 1,(MIndex mod size) + 1];
MIndex := MIndex + 1;
end do:
spiralCount := spiralCount - 2;
end do:
return spiralMatrix;
end proc:
> m:=Matrix([[1,2,3],[4,5,6],[7,8,9]]);
[1 2 3]
[ ]
m := [4 5 6]
[ ]
[7 8 9]
> spiral(m);
[1 2 3]
[ ]
[8 9 4]
[ ]
[7 6 5]
Feel free to ask me any questions about this implementation and/or about how I to use the Matrix type.

2^n calculator in pascal for n={bigger numbers}

Before i must say this : Please, excuse me for my bad english...
I'm student.My teacher gave me problem in pascal for my course work...
I must write program that calculates 2^n for big values of n...I've wrote but there is a problem...My program returns 0 for values of n that bigger than 30...My code is below...Please help me:::Thanks beforehand...
function control(a: integer): boolean;
var
b: boolean;
begin
if (a >= 10) then b := true
else b := false;
control := b;
end;
const
n = 200000000;
var
a: array[1..n] of integer;
i, j, c, t, rsayi: longint; k: string;
begin
writeln('2^n');
write('n=');
read(k);
a[1] := 1;
rsayi := 1;
val(k, t, c);
for i := 1 to t do
for j := 1 to t div 2 do
begin
a[j] := a[j] * 2;
end;
for i := 1 to t div 2 do
begin
if control(a[j]) = true then
begin
a[j + 1] := a[j + 1] + (a[j] div 10);
a[j] := a[j] mod 10;
rsayi := rsayi + 1;
end;
end;
for j := rsayi downto 1 do write(a[j]);
end.
The first (nested) loop boils down to "t" multiplications by 2 on every single element of a.
30 multiplications by two is as far as you can go with a 32-bit integer (2^31-1 of positive values, so 2^31 is out of reach)
So the first loop doesn't work, and you probably have to rethink your strategy.
Here is a quick and dirty program to compute all 2^n up to some given, possibly large, n. The program repeatedly doubles the number in array a, which is stored in base 10; with lower digit in a[1]. Notice it's not particularly fast, so it would not be wise to use it for n = 200000000.
program powers;
const
n = 2000; { largest power to compute }
m = 700; { length of array, should be at least log(2)*n }
var
a: array[1 .. m] of integer;
carry, s, p, i, j: integer;
begin
p := 1;
a[1] := 1;
for i := 1 to n do
begin
carry := 0;
for j := 1 to p do
begin
s := 2*a[j] + carry;
if s >= 10 then
begin
carry := 1;
a[j] := s - 10
end
else
begin
carry := 0;
a[j] := s
end
end;
if carry > 0 then
begin
p := p + 1;
a[p] := 1
end;
write(i, ': ');
for j := p downto 1 do
write(a[j]);
writeln
end
end.

0-1 Knapsack on infinite integer array?

Given an infinite positive integer array or say a stream of positive integers, find out the first five numbers whose sum is twenty.
By reading the problem statement, it first seems to be 0-1 Knapsack problem, but I am confused that can 0-1 Knapsack algo be used on a stream of integers. Let suppose I write a recursive program for the above problem.
int knapsack(int sum, int count, int idx)
{
if (sum == 0 && count == 0)
return 1;
if ((sum == 0 && count != 0) || (sum != 0 && count == 0))
return 0;
if (arr[idx] > 20) //element cann't be included.
return knapsack(sum, count idx + 1);
return max(knapsack(sum, count, idx +1), knapsack(sum - arr[idx], count -1, idx + 1));
}
Now when the above function will call on an infinite array, the first call in max function i.e. knapsack(sum, count, idx +1) will never return as it will keep on ignoring the current element. Even if we change the order of the call in max function, there is still possibility that the first call will never return. Is there any way to apply knapsack algo in such scenarios?
This works if you are working with only positive integers.
Basically keep a list of ways you can reach any of the first 20 numbers and whenever you process a new number process this list accordingly.
def update(dictlist, num):
dk = dictlist.keys()
for i in dk:
if i+num <=20:
for j in dictlist[i]:
listlen = len(dictlist[i][j]) + 1
if listlen >5:
continue
if i+num not in dictlist or listlen not in dictlist[i+num]:
dictlist[i+num][listlen] = dictlist[i][j]+[num]
if num not in dictlist:
dictlist[num]= {}
dictlist[num][1] = [num]
return dictlist
dictlist = {}
for x in infinite_integer_stream:
dictlist = update(dictlist,x)
if 20 in dictlist and 5 in dictlist[20]:
print dictlist[20][5]
break
This code might have some minor bugs and I do not have time now to debug it. But basically dictlist[i][j] stores a j length list that sums to i.
Delphi code:
var
PossibleSums: array[1..4, 0..20] of Integer;
Value, i, j: Integer;
s: string;
begin
s := '';
for j := 1 to 4 do
for i := 0 to 20 do
PossibleSums[j, i] := -1;
while True do begin
Value := 1 + Random(20); // stream emulation
Memo1.Lines.Add(IntToStr(Value));
if PossibleSums[4, 20 - Value] <> -1 then begin
//we just have found 5th number to make the full sum
s := IntToStr(Value);
i := 20 - Value;
for j := 4 downto 1 do begin
//unwind storage chain
s := IntToStr(PossibleSums[j, i]) + ' ' + s;
i := i - PossibleSums[j, i];
end;
Memo1.Lines.Add(s);
Break;
end;
for j := 3 downto 1 do
for i := 0 to 20 - Value do
if (PossibleSums[j, i] <> -1) and (PossibleSums[j + 1, i + Value] = -1) then
PossibleSums[j + 1, i + Value] := Value;
if PossibleSums[1, Value] = -1 then
PossibleSums[1, Value] := Value;
end;
end;
output:
4
8
9
2
10
2
17
2
4 2 10 2 2

A* / Dijkstra's algorithm simple implementation (Pascal)

I'm trying to implement A* path finding algorithm (now it's Dijkstra's algorithm i.e without heuristic) using this article Link. But I can't figure out what's wrong in my code (it finds incorrect path).
instead of the empty begin ... end; it should be this step:
If it is on the open list already, check to see if this path to that
square is better, using G cost as the measure. A lower G cost means
that this is a better path. If so, change the parent of the square to
the current square, and recalculate the G and F scores of the square.
but I think it is not important because there is no diagonal movement.
uses
crt;
const
MAXX = 20;
MAXY = 25;
type
TArr = array [0..MAXY, 0..MAXX] of integer;
TCell = record
x: integer;
y: integer;
end;
TListCell = record
x: integer;
y: integer;
G: integer;
parent: TCell;
end;
TListArr = array [1..10000] of TListCell;
TList = record
arr: TListArr;
len: integer;
end;
var
i, j, minind, ind, c: integer;
start, finish: TCell;
current: TListCell;
field: TArr;
opened, closed: TList;
procedure ShowField;
var
i, j: integer;
begin
textcolor(15);
for i := 0 to MAXX do
begin
for j := 0 to MAXY do
begin
case field[j, i] of
99: textcolor(8); // not walkable
71: textcolor(14); // walkable
11: textcolor(10); // start
21: textcolor(12); // finish
15: textcolor(2); // path
14: textcolor(5);
16: textcolor(6);
end;
write(field[j, i], ' ');
end;
writeln;
end;
textcolor(15);
end;
procedure AddClosed(a: TListCell);
begin
closed.arr[closed.len + 1] := a;
inc(closed.len);
end;
procedure AddOpened(x, y, G: integer);
begin
opened.arr[opened.len + 1].x := x;
opened.arr[opened.len + 1].y := y;
opened.arr[opened.len + 1].G := G;
inc(opened.len);
end;
procedure DelOpened(n: integer);
var
i: integer;
begin
AddClosed(opened.arr[n]);
for i := n to opened.len - 1 do
opened.arr[i] := opened.arr[i + 1];
dec(opened.len);
end;
procedure SetParent(var a: TListCell; parx, pary: integer);
begin
a.parent.x := parx;
a.parent.y := pary;
end;
function GetMin(var a: TList): integer;
var
i, min, mini: integer;
begin
min := MaxInt;
mini := 0;
for i := 1 to a.len do
if a.arr[i].G < min then
begin
min := a.arr[i].G;
mini := i;
end;
GetMin := mini;
end;
function FindCell(a: TList; x, y: integer): integer;
var
i: integer;
begin
FindCell := 0;
for i := 1 to a.len do
if (a.arr[i].x = x) and (a.arr[i].y = y) then
begin
FindCell := i;
break;
end;
end;
procedure ProcessNeighbourCell(x, y: integer);
begin
if (field[current.x + x, current.y + y] <> 99) then // if walkable
if (FindCell(closed, current.x + x, current.y + y) <= 0) then // and not visited before
if (FindCell(opened, current.x + x, current.y + y) <= 0) then // and not added to list already
begin
AddOpened(current.x + x, current.y + y, current.G + 10);
SetParent(opened.arr[opened.len], current.x, current.y);
// field[opened.arr[opened.len].x, opened.arr[opened.len].y]:=16;
end
else
begin
end;
end;
begin
randomize;
for i := 0 to MAXX do
for j := 0 to MAXY do
field[j, i] := 99;
for i := 1 to MAXX - 1 do
for j := 1 to MAXY - 1 do
if random(5) mod 5 = 0 then
field[j, i] := 99
else field[j, i] := 71;
// start and finish positions coordinates
start.x := 5;
start.y := 3;
finish.x := 19;
finish.y := 16;
field[start.x, start.y] := 11;
field[finish.x, finish.y] := 21;
ShowField;
writeln;
opened.len := 0;
closed.len := 0;
AddOpened(start.x, start.y, 0);
SetParent(opened.arr[opened.len], -1, -1);
current.x := start.x;
current.y := start.y;
repeat
minind := GetMin(opened);
current.x := opened.arr[minind].x;
current.y := opened.arr[minind].y;
current.G := opened.arr[minind].G;
DelOpened(minind);
ProcessNeighbourCell(1, 0); // look at the cell to the right
ProcessNeighbourCell(-1, 0); // look at the cell to the left
ProcessNeighbourCell(0, 1); // look at the cell above
ProcessNeighbourCell(0, -1); // look at the cell below
if (FindCell(opened, finish.x, finish.y) > 0) then
break;
until opened.len = 0;
// count and mark path
c := 0;
while ((current.x <> start.x) or (current.y <> start.y)) do
begin
field[current.x, current.y] := 15;
ind := FindCell(closed, current.x, current.y);
current.x := closed.arr[ind].parent.x;
current.y := closed.arr[ind].parent.y;
inc(c);
end;
ShowField;
writeln(c);
readln;
end.
Edit Feb 1 '12: updated code, also fixed path marking (there should be or instead and), looks like it works now :)
You should rewrite the program to use a loop instead of cut-and-paste to visit each neighbor. If you do that you will avoid bugs like the following:
if (field[current.x, current.y - 1] <> 99) then
if (FindCell(closed, current.x, current.y - 1) <= 0) then
if (FindCell(opened, current.x + 1, current.y) <= 0) then
(See the inconsistent current.x + 1, current.y in the last line.)
With respect to the loop, I was thinking of something like this (pseudo-Python):
neighbor_offsets = [(0, 1), (0, -1), (1, 0), (-1, 0)]
for offset in neighbor_offsets:
neighbor = current + offset
if is_walkable(neighbor) and not is_visited(neighbor):
# Open 'neighbor' with 'current' as parent:
open(neighbor, current)
# Perhaps check if the goal is reached:
if neighbor == finish:
goal_reached = True
break
If you don't write a loop but just refactor to
ProcessCell(x+1, y);
ProcessCell(x-1, y);
ProcessCell(x, y-1);
ProcessCell(x, y-1);
then that's a great improvement too.
Youre posting quite a lot of code, have you tried narrow it down where it fails?
Have you compared your code with the pseudocode on wikipedia?
Also remember that dijkstra is just A* with a heuristic of 0.
Edit:
The article you linked (which I now realize is the very same I used to learn the A*, funny) contains illustrated steps. I would suggest that you recreate that map/grid and run your implementation on it. Then step through the images:
Are the eight initial neighbors added to the open list? Do they have the correct parent?
Is the correct open node picked as next to be scanned according to the heuristic?
Is the list of closed nodes correct?
And so on...

Matrix and algorithm "spiral"

i wanted ask if there some algorithm ready, that allowed me to do this: i have a matrix m (col) x n (row) with m x n elements. I want give position to this element starting from center and rotating as a spiral, for example, for a matrix 3x3 i have 9 elements so defined:
5 6 7
4 9 8
3 2 1
or for una matrix 4 x 3 i have 12 elements, do defined:
8 9 10 1
7 12 11 2
6 5 4 3
or again, a matrix 5x2 i have 10 elements so defined:
3 4
7 8
10 9
6 5
2 1
etc.
I have solved basically defining a array of integer of m x n elements and loading manually the value, but in generel to me like that matrix maked from algorithm automatically.
Thanks to who can help me to find something so, thanks very much.
UPDATE
This code, do exactely about i want have, but not is in delphi; just only i need that start from 1 and not from 0. Important for me is that it is valid for any matrics m x n. Who help me to translate it in delphi?
(defun spiral (rows columns)
(do ((N (* rows columns))
(spiral (make-array (list rows columns) :initial-element nil))
(dx 1) (dy 0) (x 0) (y 0)
(i 0 (1+ i)))
((= i N) spiral)
(setf (aref spiral y x) i)
(let ((nx (+ x dx)) (ny (+ y dy)))
(cond
((and (< -1 nx columns)
(< -1 ny rows)
(null (aref spiral ny nx)))
(setf x nx
y ny))
(t (psetf dx (- dy)
dy dx)
(setf x (+ x dx)
y (+ y dy)))))))
> (pprint (spiral 6 6))
#2A ((0 1 2 3 4 5)
(19 20 21 22 23 6)
(18 31 32 33 24 7)
(17 30 35 34 25 8)
(16 29 28 27 26 9)
(15 14 13 12 11 10))
> (pprint (spiral 5 3))
#2A ((0 1 2)
(11 12 3)
(10 13 4)
(9 14 5)
(8 7 6))
Thanks again very much.
Based on the classic spiral algorithm. supporting non-square matrix:
program SpiralMatrix;
{$APPTYPE CONSOLE}
uses
SysUtils;
type
TMatrix = array of array of Integer;
procedure PrintMatrix(const a: TMatrix);
var
i, j: Integer;
begin
for i := 0 to Length(a) - 1 do
begin
for j := 0 to Length(a[0]) - 1 do
Write(Format('%3d', [a[i, j]]));
Writeln;
end;
end;
var
spiral: TMatrix;
i, m, n: Integer;
row, col, dx, dy,
dirChanges, visits, temp: Integer;
begin
m := 3; // columns
n := 3; // rows
SetLength(spiral, n, m);
row := 0;
col := 0;
dx := 1;
dy := 0;
dirChanges := 0;
visits := m;
for i := 0 to n * m - 1 do
begin
spiral[row, col] := i + 1;
Dec(visits);
if visits = 0 then
begin
visits := m * (dirChanges mod 2) + n * ((dirChanges + 1) mod 2) - (dirChanges div 2) - 1;
temp := dx;
dx := -dy;
dy := temp;
Inc(dirChanges);
end;
Inc(col, dx);
Inc(row, dy);
end;
PrintMatrix(spiral);
Readln;
end.
3 x 3:
1 2 3
8 9 4
7 6 5
4 x 3:
1 2 3 4
10 11 12 5
9 8 7 6
2 x 5:
1 2
10 3
9 4
8 5
7 6
There you go!!! After 30some syntax errors...
On ideone.com, I ran it with some tests and it seems to work fine. I think you can see the output there still and run it yourself...
I put some comments in the code. Enough to understand most of it. The main navigation system is a little bit harder to explain. Briefly, doing a spiral is going in first direction 1 time, second 1 time, third 2 times, fourth 2 times, fifth 3 times, 3, 4, 4, 5, 5, and so on. I use what I called a seed and step to get this behavior.
program test;
var
w, h, m, n, v, d : integer; // Matrix size, then position, then value and direction.
spiral : array of array of integer; // Matrix/spiral itself.
seed, step : integer; // Used to travel the spiral.
begin
readln(h);
readln(w);
setlength(spiral, h, w);
v := w * h; // Value to put in spiral.
m := trunc((h - 1) / 2); // Finding center.
n := trunc((w - 1) / 2);
d := 0; // First direction is right.
seed := 2;
step := 1;
// Travel the spiral.
repeat
// If in the sub-spiral, store value.
if ((m >= 0) and (n >= 0) and (m < h) and (n < w)) then
begin
spiral[m, n] := v;
v := v - 1;
end;
// Move!
case d of
0: n := n + 1;
1: m := m - 1;
2: n := n - 1;
3: m := m + 1;
end;
// Plan trajectory.
step := step - 1;
if step = 0 then
begin
d := (d + 1) mod 4;
seed := seed + 1;
step := trunc(seed / 2);
end;
until v = 0;
// Print the spiral.
for m := 0 to (h - 1) do
begin
for n := 0 to (w - 1) do
begin
write(spiral[m, n], ' ');
end;
writeln();
end;
end.
If you really need that to print text spirals I'll let you align the numbers. Just pad them with spaces.
EDIT:
Was forgetting... In order to make it work on ideone, I put the parameters on 2 lines as input. m, then n.
For example:
5
2
yields
3 4
7 8
10 9
6 5
2 1
Here's the commented JavaScript implementation for what you're trying to accomplish.
// return an array representing a matrix of size MxN COLxROW
function spiralMatrix(M, N) {
var result = new Array(M * N);
var counter = M * N;
// start position
var curCol = Math.floor((M - 1) / 2);
var curRow = Math.floor(N / 2);
// set the center
result[(curRow * M) + curCol] = counter--;
// your possible moves RIGHT, UP, LEFT, DOWN * y axis is flipped
var allMoves = [[1,0], [0,-1], [-1,0], [0,1]];
var curMove = 0;
var moves = 1; // how many times to make current Move, 1,1,2,2,3,3,4,4 etc
// spiral
while(true) {
for(var i = 0; i < moves; i++) {
// move in a spiral outward counter clock-wise direction
curCol += allMoves[curMove][0];
curRow += allMoves[curMove][1];
// naively skips locations that are outside of the matrix bounds
if(curCol >= 0 && curCol < M && curRow >= 0 && curRow < N) {
// set the value and decrement the counter
result[(curRow * M) + curCol] = counter--;
// if we reached the end return the result
if(counter == 0) return result;
}
}
// increment the number of times to move if necessary UP->LEFT and DOWN->RIGHT
if(curMove == 1 || curMove == 3) moves++;
// go to the next move in a circular array fashion
curMove = (curMove + 1) % allMoves.length;
}
}
The code isn't the most efficient, because it walks the spiral naively without first checking if the location it's walking on is valid. It only checks the validity of the current location right before it tries to set the value on it.
Even though the question is already answered, this is an alternative solution (arguably simpler).
The solution is in python (using numpy for bidimendional arrays), but can be easily ported.
The basic idea is to use the fact that the number of steps is known (m*n) as end condition,
and to properly compute the next element of the loop at each iteration:
import numpy as np
def spiral(m, n):
"""Return a spiral numpy array of int with shape (m, n)."""
a = np.empty((m, n), int)
i, i0, i1 = 0, 0, m - 1
j, j0, j1 = 0, 0, n - 1
for k in range(m * n):
a[i, j] = k
if i == i0 and j0 <= j < j1: j += 1
elif j == j1 and i0 <= i < i1: i += 1
elif i == i1 and j0 < j <= j1: j -= 1
elif j == j0 and 1 + i0 < i <= i1: i -= 1
else:
i0 += 1
i1 -= 1
j0 += 1
j1 -= 1
i, j = i0, j0
return a
And here some outputs:
>>> spiral(3,3)
array([[0, 1, 2],
[7, 8, 3],
[6, 5, 4]])
>>> spiral(4,4)
array([[ 0, 1, 2, 3],
[11, 12, 13, 4],
[10, 15, 14, 5],
[ 9, 8, 7, 6]])
>>> spiral(5,4)
array([[ 0, 1, 2, 3],
[13, 14, 15, 4],
[12, 19, 16, 5],
[11, 18, 17, 6],
[10, 9, 8, 7]])
>>> spiral(2,5)
array([[0, 1, 2, 3, 4],
[9, 8, 7, 6, 5]])

Resources