Astar algorithm not working (possibly wrong heuristic value?) - c#

I tried to follow wiki's A* star pseudocode and I had problems creating heuristic value method, so i just used it as Shortest Reach, but the code keeps adding starting node to the closedSet.
public uint TotalNodes = 0;
uint[][] Neighbors = null;
uint[][] Weights = null;
public Graph(uint v)
{
TotalNodes = v + 1;
Neighbors = new uint[TotalNodes][];
Weights = new uint[TotalNodes][];
for (int i = 1; i < TotalNodes; i++)
{
for (int j = 1; j < TotalNodes; j++)
{
Neighbors[i] = new uint[TotalNodes];
Weights[i] = new uint[TotalNodes];
}
}
}
public void AddNeighbor(uint parent, uint neighbor, uint distance)
{
Neighbors[parent][neighbor] = neighbor;
Weights[parent][neighbor] = distance;
}
public void RunAstarAlgorithm(uint start, uint goal)
{
uint[] closedSet = new uint[TotalNodes];
uint[] openSet = new uint[TotalNodes];
uint[] g_score = new uint[TotalNodes];
uint[] f_score = new uint[TotalNodes];
uint openSetNodes = 0, closedSetNodes = 0;
uint currentNode = 0;
uint tentativePathCost = 0;
openSet[0] = start;
for (uint i = 0; i < TotalNodes; i++)
{
if (i == start) f_score[i] = 0;
else f_score[i] = uint.MaxValue;
}
f_score[start] = g_score[start] + (uint)HeuristicCostEstimate(start,goal);
while (openSet.Length != 0)
{
for (uint i = 1; i < TotalNodes; i++)
{
if (f_score[i] == f_score.Min()) currentNode = i;
}
if (currentNode == goal)
{
ReconstructPath(closedSet, currentNode);
return;
}
for (uint i = 0; i < TotalNodes; i++) if (openSet[i] == currentNode) openSet[i] = 0;
closedSet[closedSetNodes] = currentNode;
closedSetNodes++;
foreach (uint neighbor in Neighbors[currentNode])
{
for (uint i = 0; i < TotalNodes; i++)
{
if (closedSet.Contains(neighbor)) continue;
}
tentativePathCost += g_score[currentNode] + Weights[currentNode][neighbor];
if (!openSet.Contains(neighbor) || tentativePathCost < g_score[neighbor])
{
g_score[neighbor] = tentativePathCost;
f_score[neighbor] = g_score[neighbor] + (uint)HeuristicCostEstimate(neighbor,goal);
if (!openSet.Contains(neighbor))
{
openSet[openSetNodes] = neighbor;
openSetNodes++;
}
}
}
}
}
Find heuristic value:
public int HeuristicCostEstimate(uint start, uint goal)
{
Queue<uint> queue = new Queue<uint>();
queue.Enqueue(start);
bool[] visitedNodes = new bool[TotalNodes];
int[] distances = new int[TotalNodes];
for (uint i = 0; i < TotalNodes; i++) distances[i] = -1;
distances[start] = 0;
while(queue.Count > 0)
{
uint node = queue.Dequeue();
if (node == goal) break;
foreach(uint neighbor in Neighbors[node])
{
if (neighbor == 0) continue;
if(distances[neighbor] == -1)
{
distances[neighbor] = distances[node] + 1;
queue.Enqueue(neighbor);
}
}
}
return distances[goal];
}
So i have this graph:
7 node graph
and for ex. i want to go from 1 to 5, so the path should be 1-2-3-6-5 right?
My graph is represented in jagged arrays (ignore the first (0) spot, i start from 1 for printing out purposes)
Neighbors array (first line represents nodes, columns represent their neigbhors:
(0)(1)(2)(3)(4)(5)(6)(7)
(0) 0 0 0 0 0 0 0 0
(1) 0 0 1 0 0 0 0 1
(2) 0 2 0 2 2 0 0 0
(3) 0 0 3 0 3 0 3 0
(4) 0 0 4 4 0 4 0 0
(5) 0 0 0 0 5 0 5 0
(6) 0 0 0 6 0 6 0 6
(7) 0 7 0 0 0 0 7 0
And weight(distance) array (columns represent their neigbhors and what is the distance to them):
(0)(1)(2)(3)(4)(5)(6)(7)
(0) 0 0 0 0 0 0 0 0
(1) 0 0 2 0 0 0 0 5
(2) 0 2 0 3 7 0 0 0
(3) 0 0 3 0 4 0 3 0
(4) 0 0 7 4 0 6 0 0
(5) 0 0 0 0 5 0 1 0
(6) 0 0 0 2 0 1 0 10
(7) 0 5 0 0 0 0 10 0
So how could i achieve shortest path with A* algorithm?

Related

C# getting the biggest area of a matrix which has the same element

In C# I've come to a problem I can't solve myself. I have a 2d matrix which looks like this
1 1 1 0 0 0 0 1 1 1
1 0 1 0 0 0 0 1 1 1
1 0 1 1 1 1 1 0 1 1
1 0 1 1 1 1 1 0 1 1
1 0 1 1 1 1 1 1 1 1
1 1 1 0 0 1 1 1 1 1
1 1 1 0 0 1 1 1 1 1
I want to find out the biggest 4-connected (north, west, south, east) area of 0. For the example above the answer is 8 (the biggest area of all zeroes is on the top of the matrix):
0 0 0 0
0 0 0 0
I have no idea how to cope such a task, so anything could help.
Thanks!
I've tried iterating through the matrix with a for but its not working if the biggest area where 0 are on the same line (or column).
You can scan the map and on each 0 perform a search (in the code below it is Breadth First Search):
Code: (Fiddle)
private static int MaxIsland(int[,] ocean) {
int result = 0;
if (ocean == null)
return result;
var visited = new HashSet<(int y, int x)>();
for (int y = 0; y < ocean.GetLength(0); ++y)
for (int x = 0; x < ocean.GetLength(1); ++x) {
if (ocean[y, x] != 0)
continue;
if (!visited.Add((y, x)))
continue;
int current = 1;
var agenda = new Queue<(int y, int x)>();
agenda.Enqueue((y, x));
while (agenda.Count > 0) {
var (r, c) = agenda.Dequeue();
for (int d = 0; d < 4; ++d) {
int newR = r + (d - 1) % 2;
int newC = c + (d - 2) % 2;
if (newC < 0 || newR < 0 ||
newR >= ocean.GetLength(0) || newC >= ocean.GetLength(1))
continue;
if (ocean[newR, newC] != 0)
continue;
if (visited.Add((newR, newC))) {
agenda.Enqueue((newR, newC));
current += 1;
}
}
}
result = Math.Max(result, current);
}
return result;
}
Time complexity: O(n * m) where n and m are matrix dimensions
Space complexity: O(n * m) in the worst case

How can I save the coordinates of a Matrix Maze resolution?

Let's say I have a maze in a bidimensional array where I can only step on fields filled with 1, for example:
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
0 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0 0 0 0 0
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
0 1 0 1 1 1 1 1 1 0 0 1 0 0 1 0 0 1 1 1
0 1 0 1 1 1 1 1 1 0 0 1 0 0 1 0 0 1 1 1
0 1 0 1 0 0 0 0 1 0 0 1 0 0 1 0 0 1 1 1
0 1 0 1 0 1 0 0 1 0 0 1 0 0 1 0 0 1 1 1
0 0 0 1 0 1 0 0 1 0 0 1 0 0 1 0 0 1 1 1
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
Assuming that I'll always have an start point and an end point, I need to save which coordinates I'm stepping to reach the goal. For example, if my start point is (0, 0) and my end point is (2,9), the output should be a List with the coordinates
(0,0),(0,1),(0,2),(0,3),(0,4),(0,5),(0,6),(0,7),(0,8),(0,9),(0,10),(1,10),(2,10),(2,9)
I've tried implementing a kind of A* algorithm but for this kind of problem I think it's over complicated (or maybe I'm just not understanding it). Also I implemented a simple BFS to check the minimum path. I don't really know if it is useful for a backtracking.
public static class BFS
{
public static int ROW = 20;
public static int COL = 9;
public class Point
{
public int x;
public int y;
public Point(int x, int y)
{
this.x = x;
this.y = y;
}
};
public class queueNode
{
public Point pt;
public int dist;
public queueNode(Point pt, int dist)
{
this.pt = pt;
this.dist = dist;
}
};
static bool isValid(int row, int col)
{
return (row >= 0) && (row < ROW) &&
(col >= 0) && (col < COL);
}
static int[] rowNum = { -1, 0, 0, 1 };
static int[] colNum = { 0, -1, 1, 0 };
static int AplicarBFS(int[,] mat, Point src,
Point dest)
{
if (mat[src.x, src.y] != 1 ||
mat[dest.x, dest.y] != 1)
return -1;
bool[,] visited = new bool[ROW, COL];
visited[src.x, src.y] = true;
Queue<queueNode> q = new Queue<queueNode>();
queueNode s = new queueNode(src, 0);
q.Enqueue(s);
while (q.Count != 0)
{
queueNode curr = q.Peek();
Point pt = curr.pt;
if (pt.x == dest.x && pt.y == dest.y)
{
return curr.dist;
}
q.Dequeue();
for (int i = 0; i < 4; i++)
{
int row = pt.x + rowNum[i];
int col = pt.y + colNum[i];
if (isValid(row, col) &&
mat[row, col] == 1 &&
!visited[row, col])
{
visited[row, col] = true;
queueNode Adjcell = new queueNode
(new Point(row, col),
curr.dist + 1);
q.Enqueue(Adjcell);
}
}
}
return -1;
}
public static int EncontrarCaminoMinimo(int[,] Matriz, int xOrigen, int yOrigen, int xDestino, int yDestino)
{
Point source = new Point(xOrigen, yOrigen);
Point dest = new Point(xDestino, yDestino);
int dist = AplicarBFS(Matriz, source, dest);
if (dist != -1)
Console.WriteLine("Shortest Path is " + dist);
else
Console.WriteLine("Shortest Path doesn't exist");
return dist;
}
}
EDIT
I'm working with this:
public class matrixNode
{
public int fr = 0, to = 0, sum = 0;
public int x, y;
public matrixNode parent;
}
public static matrixNode EncontrarAStar(int[,] matrix, int fromX, int fromY, int toX, int toY)
{
Dictionary<string, matrixNode> greens = new Dictionary<string, matrixNode>(); //open
Dictionary<string, matrixNode> reds = new Dictionary<string, matrixNode>(); //closed
matrixNode startNode = new matrixNode { x = fromX, y = fromY };
string key = startNode.x.ToString() + startNode.x.ToString();
greens.Add(key, startNode);
Func<KeyValuePair<string, matrixNode>> smallestGreen = () =>
{
KeyValuePair<string, matrixNode> smallest = greens.ElementAt(0);
foreach (KeyValuePair<string, matrixNode> item in greens)
{
if (item.Value.sum < smallest.Value.sum)
smallest = item;
else if (item.Value.sum == smallest.Value.sum
&& item.Value.to < smallest.Value.to)
smallest = item;
}
return smallest;
};
List<KeyValuePair<int, int>> fourNeighbors = new List<KeyValuePair<int, int>>()
{ new KeyValuePair<int, int>(-1,0),
new KeyValuePair<int, int>(0,1),
new KeyValuePair<int, int>(1, 0),
new KeyValuePair<int, int>(0,-1) };
int maxX = matrix.GetLength(0);
if (maxX == 0)
return null;
int maxY = matrix.GetLength(1);
while (true)
{
if (greens.Count == 0)
return null;
KeyValuePair<string, matrixNode> current = smallestGreen();
if (current.Value.x == toX && current.Value.y == toY)
return current.Value;
greens.Remove(current.Key);
reds.Add(current.Key, current.Value);
foreach (KeyValuePair<int, int> plusXY in fourNeighbors)
{
int nbrX = current.Value.x + plusXY.Key;
int nbrY = current.Value.y + plusXY.Value;
string nbrKey = nbrX.ToString() + nbrY.ToString();
if (nbrX < 0 || nbrY < 0 || nbrX >= maxX || nbrY >= maxY
|| matrix[nbrX, nbrY] == 0 //Obstacles marcats amb 0
|| reds.ContainsKey(nbrKey))
continue;
if (greens.ContainsKey(nbrKey))
{
matrixNode curNbr = greens[nbrKey];
int from = Math.Abs(nbrX - fromX) + Math.Abs(nbrY - fromY);
if (from < curNbr.fr)
{
curNbr.fr = from;
curNbr.sum = curNbr.fr + curNbr.to;
curNbr.parent = current.Value;
}
}
else
{
matrixNode curNbr = new matrixNode { x = nbrX, y = nbrY };
curNbr.fr = Math.Abs(nbrX - fromX) + Math.Abs(nbrY - fromY);
curNbr.to = Math.Abs(nbrX - toX) + Math.Abs(nbrY - toY);
curNbr.sum = curNbr.fr + curNbr.to;
curNbr.parent = current.Value;
greens.Add(nbrKey, curNbr);
}
}
}
}
But, for example from (2, 0) to (5, 17) happens something similar to:
Image
Any ideas?
EDIT:
I've solved it, in a few days I'm posting the code and a fiddle!

How to get all combinations with a fixed number of flags in array

here I have an array with a specific length and also a specific number of flags which have to be set. The length and number of flags can change from case to case, so they should be generic.
Example:
var array = new bool[] { false, false, false, false, false, false };
var numberOfFlags = 2;
I'd now like to get all permutations / combinations which are possible, when always 2 flags have to be set. Example:
1 1 0 0 0 0
0 1 1 0 0 0
0 0 1 1 0 0
But also for example:
0 1 0 0 0 1
Or:
0 0 0 1 0 1
I simply need a way to get all possible combinations where the predefined number of flags are set. No pattern or anything, just all possible combinations. Preferrably in C#.
I'm really looking forward to an answer and thanks a lot!
I found this on rosettacode.org. (I changed it a little bit). It's non-recursive. It just uses a Stack. It returns the same (modified) array every time, but that can be easily changed if needed.
public static IEnumerable<int[]> Combinations(int n, int k)
{
var result = new int[k];
var stack = new Stack<int>();
stack.Push(0);
while (stack.Count > 0) {
int index = stack.Count - 1;
int value = stack.Pop();
while (value < n) {
result[index++] = value++;
stack.Push(value);
if (index == k) {
yield return result;
break;
}
}
}
}
Combinations(6, 2) will give:
[0,1], [0,2], [0,3], [0,4], [0,5], [1,2], [1,3], [1,4], [1,5], [2,3], [2,4], [2,5], [3,4], [3,5], [4,5]
Just for numberOfFlags = 2 this is a simple sollution:
static void Main(string[] args)
{
var array = new bool[] { false, false, false, false, false, false };
var length = array.Length;
for (int i = 0; i < length; i++)
{
for (int j = i + 1; j < length; j++)
{
var arr = (bool[])array.Clone();
arr[i] = arr[j] = true;
arr.ToList().ForEach(s => Console.Write((s ? 1 : 0) + " "));
Console.WriteLine();
}
}
Console.Read();
}
Output:
1 1 0 0 0 0
1 0 1 0 0 0
1 0 0 1 0 0
1 0 0 0 1 0
1 0 0 0 0 1
0 1 1 0 0 0
0 1 0 1 0 0
0 1 0 0 1 0
0 1 0 0 0 1
0 0 1 1 0 0
0 0 1 0 1 0
0 0 1 0 0 1
0 0 0 1 1 0
0 0 0 1 0 1
0 0 0 0 1 1
The number of combinations can be calculed with:
P=(n!)/(a!·b!)
Where n is the lenght of the array, a is numberOfFlags and b is n - a.
This is a method of achieving what you want:
bool[] array = new bool[] { false, false, false,false};
int numberOfFlags = 1;
int n, a, b,_n,_a,_b;
n = array.Length;
_n = n;
a = numberOfFlags;
_a = a;
b = n - a;
_b = b;
//Calculate n!
for (int i = _n - 1; i >= 1; i--)
n = n * i;
//Calculate a!
for (int i = _a - 1; i >= 1; i--)
a = a * i;
//Calculate a!
for (int i = _b - 1; i >= 1; i--)
b = b * i;
int NumberOfPermutations = n / (a * b);
------EDIT------
This code works only for an array with only 2 possible values. Imagine that we have 3 possible values, then:
n = lenght of the array
a = repetitions of the first value
b = repetitions of the second value
c = n - (a+b) = repetitions of the third value
The number of permutations could be calculed with
P=(n!)/(a!·b!·c! ...)
In the code you should add only some variables, some loops...et voilà

Find string with most frequency of a character in List of strings using LINQ C#

I am dealing with binary vectors so each string in a List<string> looks like
vectors[0] = "1 0 0 0 1 1 1 0 0 0 1 0 1 1 0 0 0 0 1 0 0 1";
vectors[1] = "1 0 0 0 0 1 1 0 0 0 1 0 0 1 0 0 0 0 0 0 0 1";
I want to get the biggest string from the List<string>, with most number of 1's.
I feel like the provided solutions here are way too complicated.
So here is mine:
vectors.OrderByDescending(v => v.Count(c => c == '1')).First();
Note that Count is only evaluated once per "Vector". EnumerableSorter does this for you.
If you want a more performant solution then go with #octavioccls answer
I would do this:
var biggest= vectors.Select(v=> new {Vector = v, Count = v.Count(c => c=='1')})
.Aggregate((seed, current) => seed.Count < current.Count ? current:seed)
.Vector;
You can also use OrderBy extension method but Aggregate is O(n) while OrderBy is O(n* log n).
I first call Select extension to avoid calculate multiple times the amount of 1's of seed:
var biggest= vectors.Aggregate((seed, current)=>seed.Count(c=>c=='1')<current.Count(c=>c=='1')?current:seed);
You can use Count() method:
int index = 0;
int max = vectors[0].Count(x => x == '1');
for (int a = 1; a < vectors.Length; a++)
{
var count = vectors[a].Count(x => x == '$');
if (count > max)
{
max = count;
index = a;
}
}
after loop you will have index of string which has maximum '1'
Using LINQ
var result = vectors.Select(d => new { Item = d,
OneCount = d.Split(' ').Count(y => y == "1")})
.OrderByDescending(t => t.OneCount)
.Select(k=>k.Item).FirstOrDefault();
vectors.OrderByDescending(b => b.Split('1').Length).First()
You can do a loop on the list and for each string calculate the number of occurences of '1' in this way:
int count = myString.Split('1').Length - 1;
Then store a temporary variable with the local maximum and the number of string with that maximum.
Like this:
int max = list[0].Split('1').Length - 1;
int stringMax = 0;
for (int i = 1; i < list.Length; i++)
{
var count = list[i].Split('1').Length - 1;
if (count > max)
{
max = count;
stringMax = i;
}
}
At the end stringMax is the string with max value and max is that value
var vectors = new[] {
"1 0 0 0 1 1 1 0 0 0 1 0 1 1 0 0 0 0 1 0 0 1",
"1 0 0 0 0 1 1 0 0 0 1 0 0 1 0 0 0 0 0 0 0 1",
};
var q = from v in vectors
select new
{
v,
c = v.Split(' ').Count(b => b == "1"),
};
var m = q.First(c => c.c == q.Max(b => b.c)).v;
using System;
using System.Collections.Generic;
public class Program
{
public static void Main()
{
Console.WriteLine("Hello World");
var vectors = new List<string>();
vectors.Add("1 0 0 0 1 1 1 0 0 0 1 0 1 1 0 0 0 0 1 0 0 1");
vectors.Add("1 0 0 0 0 1 1 0 0 0 1 0 0 1 0 0 0 0 0 0 0 1");
foreach(var v in vectors)
{
var result = new List<string>();
int max = 0;
foreach(var c in v)
{
if(string.IsNullOrEmpty(c.ToString().Trim())) continue;
//Console.Write(c);
//Console.Write(max);
if(c != '1')
{
max = 0;
}
else
{
max++;
if(max > result.Count)
{
result.Add(c.ToString());
}
}
}
Console.WriteLine("Final Result: " + string.Join(" ", result));
}
}
}

Algorithm to "spread" decreasing value on 3D array

I need to find an efficient algorithm that does this:
byte[,] initialArray
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 5 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 0
0 0 5 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
To this:
byte[,] resultArray
0 0 0 1 2 1 0 0 0 0
0 0 1 2 3 2 1 0 0 0
0 1 2 3 4 3 2 1 0 0
1 2 3 4 5 4 3 2 1 0
0 1 2 3 4 3 2 1 0 0
0 1 2 2 3 2 1 0 0 0
1 2 3 2 2 1 0 0 0 0
2 3 4 3 2 1 0 0 0 0
3 4 5 4 3 2 1 0 0 0
2 3 4 3 2 1 0 0 0 0
What's happening
The initial array has two cells that are set to an initial value, the other cells are set to 0. The algorithm needs to "spread" that value to neighbor cells (no diagonals, just up/down/left/right). Each time the value spreads to a new cell, the value is decreased by 1 and spreads again, recursively. When the value reaches 0 it stops.
If the value is spreading to a cell with value > 0, the largest of the two values should be kept, instead of simply overwriting.
The example shows a 2D array but I'm actually working with a 3D array.
My attempt
I've managed to make a simple recursive algorithm in C#. It works but it must be terribly inefficient. It's way to slow on a large 3D array with 4 initial cells with value >= 10. There must be a much better way of doing this (for those who are familiar, this is the method the Minecraft game uses to determine light intensity of each cell in the game. Minecraft level arrays are massive and can contain many light sources)
I'm looking for the most efficient way of doing this. Here is my C# implementation for 3D arrays:
main ... {
List<int[]> toCheck = new List<int[]>();
// This list will keep a record of the initial cells that have a value > 0
// For loop over each cell to find those with initial value > 0
for (int x=0; x<worldX; x++){
for (int y=0; y<worldY; y++){
for (int z=0; z<worldZ; z++){
if (data[x,y,z].light > 0)
toCheck.Add (new int[] {x,y,z});
}
}
}
// For each cell w/ initial value > 0, spread the light
foreach (int[] i in toCheck)
SpreadLight(i[0],i[1],i[2],(byte)(data[i[0],i[1],i[2]].light - 1));
}
void SpreadLight(int x, int y, int z, byte light) {
try {
// Make sure this cells current value is smaller than the value we want to assign to it
if (data[x,y,z].light < light) {
data[x,y,z].light = (byte)light;
}
// If the value at this cell is > 0, get adjacent cells and spread the light to each of them
if (light > 0) {
int[][] adjBlocks = GetAdjacentBlocks(x,y,z);
for (int i = 0; i < 6; i++) {
SpreadLight(adjBlocks[i][0], adjBlocks[i][1], adjBlocks[i][2],(byte)(light-1));
}
}
}
catch { return; } // I'm not proud if this, it's the easiest way I found to avoid out of array bounds error
}
// This method simply returns an array with the 3D coordinates of each adjacent cell
int[][] GetAdjacentBlocks(int x, int y, int z) {
int[][] result = new int[6][];
// Top
result[0] = new int[] {x, y+1, z};
// North
result[1] = new int[] {x, y, z+1};
// East
result[2] = new int[] {x+1, y, z};
// South
result[3] = new int[] {x, y, z-1};
// West
result[4] = new int[] {x-1, y, z};
// Bottom
result[5] = new int[] {x, y-1, z};
return result;
}
Try this. In my experience 1D arrays work much faster than 2D arrays. Also implemented are several shortcuts for the calculations.
2D version
class Program
{
static void Main(string[] args)
{
Area A=new Area(10, 10);
A[3, 4]=5;
A[8, 2]=5;
Console.WriteLine(A);
//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 5 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 0
//0 0 5 0 0 0 0 0 0 0
//0 0 0 0 0 0 0 0 0 0
bool spread1=A.CheckSpread();
Console.WriteLine();
Console.WriteLine("Spreading...");
A.Spread();
bool spread2=A.CheckSpread();
Console.WriteLine(A);
//0 0 0 1 2 1 0 0 0 0
//0 0 1 2 3 2 1 0 0 0
//0 1 2 3 4 3 2 1 0 0
//1 2 3 4 5 4 3 2 1 0
//0 1 2 3 4 3 2 1 0 0
//0 1 2 2 3 2 1 0 0 0
//1 2 3 2 2 1 0 0 0 0
//2 3 4 3 2 1 0 0 0 0
//3 4 5 4 3 2 1 0 0 0
//2 3 4 3 2 1 0 0 0 0
}
}
public struct Area
{
byte[] map;
int rows, columns;
public Area(int rows, int columns)
{
this.map=new byte[rows*columns];
this.columns=columns;
this.rows=rows;
}
public Area(Area other)
: this(other.rows, other.columns)
{
Array.Copy(other.map, this.map, other.map.Length);
}
public Area(byte[,] array)
{
this.rows=array.GetLength(0);
this.columns=array.GetLength(1);
this.map=new byte[rows*columns];
for (int i=0; i<rows; i++)
{
for (int j=0; j<columns; j++)
{
this.map[i*columns+j]=array[i, j];
}
}
}
public int Rows { get { return rows; } }
public int Columns { get { return columns; } }
public byte[] Map { get { return map; } }
public byte this[int index]
{
get { return map[index]; }
set { map[index]=value; }
}
public byte this[int row, int column]
{
get { return map[row*columns+column]; }
set { map[row*columns+column]=value; }
}
public byte[,] ToArray2()
{
byte[,] array=new byte[rows, columns];
for (int i=0; i<rows; i++)
{
for (int j=0; j<columns; j++)
{
array[i, j]=map[i*columns+j];
}
}
return array;
}
public void Spread()
{
bool changed;
do // CAUTION: This is not guaranteed to exit. Or is it?
{
changed=false;
for (int k=0; k<map.Length; k++)
{
byte x=map[k];
if (x<=1) continue; // cannot affect neighbors
int i=k/columns;
int j=k%columns;
int k_N=i>0?(i-1)*columns+j:-1;
int k_S=i<rows-1?(i+1)*columns+j:-1;
int k_E=j<columns-1?i*columns+j+1:-1;
int k_W=j>0?i*columns+j-1:-1;
if (k_N>=0&&map[k_N]+1<x) { map[k_N]=(byte)(x-1); changed=true; }
if (k_S>=0&&map[k_S]+1<x) { map[k_S]=(byte)(x-1); changed=true; }
if (k_E>=0&&map[k_E]+1<x) { map[k_E]=(byte)(x-1); changed=true; }
if (k_W>=0&&map[k_W]+1<x) { map[k_W]=(byte)(x-1); changed=true; }
}
} while (changed);
}
public bool CheckSpread()
{
for (int k=0; k<map.Length; k++)
{
byte x=map[k];
if (x<=1) continue; // cannot affect neighbors
int i=k/columns;
int j=k%columns;
int k_N=i>0?(i-1)*columns+j:-1;
int k_S=i<rows-1?(i+1)*columns+j:-1;
int k_E=j<columns-1?i*columns+j+1:-1;
int k_W=j>0?i*columns+j-1:-1;
if (k_N>=0&&map[k_N]+1<x) return false;
if (k_S>=0&&map[k_S]+1<x) return false;
if (k_E>=0&&map[k_E]+1<x) return false;
if (k_W>=0&&map[k_W]+1<x) return false;
}
return true;
}
public override string ToString()
{
string[] table=new string[rows];
for (int i=0; i<rows; i++)
{
string[] row=new string[columns];
for (int j=0; j<columns; j++)
{
row[j]= string.Format("{0,-3}", map[i*columns+j]);
}
table[i]= string.Join(" ", row);
}
return string.Join(Environment.NewLine, table);
}
}
3D version
public struct Area3
{
byte[] map;
int rows, columns, pages;
public Area3(int rows, int columns, int pages)
{
this.map=new byte[rows*columns*pages];
this.columns=columns;
this.rows=rows;
this.pages=pages;
}
public Area3(Area3 other)
: this(other.rows, other.columns, other.pages)
{
Array.Copy(other.map, this.map, other.map.Length);
}
public Area3(byte[, ,] array)
{
this.rows=array.GetLength(0);
this.columns=array.GetLength(1);
this.pages=array.GetLength(2);
this.map=new byte[rows*columns*pages];
for (int i=0; i<rows; i++)
{
for (int j=0; j<columns; j++)
{
for (int l=0; l<pages; l++)
{
this.map[(l*rows+i)*columns+j]=array[i, j, l];
}
}
}
}
public int Rows { get { return rows; } }
public int Columns { get { return columns; } }
public int Pages { get { return pages; } }
public byte[] Map { get { return map; } }
public byte this[int index]
{
get { return map[index]; }
set { map[index]=value; }
}
public byte this[int row, int column, int page]
{
get { return map[(page*rows+row)*columns+column]; }
set { map[(page*rows+row)*columns+column]=value; }
}
public byte[, ,] ToArray3()
{
byte[, ,] array=new byte[rows, columns, pages];
for (int i=0; i<rows; i++)
{
for (int j=0; j<columns; j++)
{
for (int l=0; l<pages; l++)
{
array[i, j, l]=map[(l*rows+i)*columns+j];
}
}
}
return array;
}
public void Spread()
{
bool changed;
do
{
changed=false;
for (int k=0; k<map.Length; k++)
{
byte x=map[k];
if (x<=1) continue; // cannot affect neighbors
int l=k/(rows*columns);
int i=(k%(rows*columns))/columns;
int j=(k%(rows*columns))%columns;
int k_N=i>0?(l*rows+i-1)*columns+j:-1;
int k_S=i<rows-1?(l*rows+i+1)*columns+j:-1;
int k_E=j<columns-1?(l*rows+i)*columns+j+1:-1;
int k_W=j>0?(l*rows+i)*columns+j-1:-1;
int k_U=l<pages-1?((l+1)*rows+i)*columns+j:-1;
int k_D=l>0?((l-1)*rows+i)*columns+j:-1;
if (k_N>=0&&map[k_N]+1<x) { map[k_N]=(byte)(x-1); changed=true; }
if (k_S>=0&&map[k_S]+1<x) { map[k_S]=(byte)(x-1); changed=true; }
if (k_E>=0&&map[k_E]+1<x) { map[k_E]=(byte)(x-1); changed=true; }
if (k_W>=0&&map[k_W]+1<x) { map[k_W]=(byte)(x-1); changed=true; }
if (k_U>=0&&map[k_U]+1<x) { map[k_U]=(byte)(x-1); changed=true; }
if (k_D>=0&&map[k_D]+1<x) { map[k_D]=(byte)(x-1); changed=true; }
}
} while (changed);
}
}
No visualization coded for 3D.
The following implementation (2D) should be faster - it keeps object creation and unnecessary function calls to the minimum. Extending to 3D would be quite straightforward:
class Program
{
class ArrayPoint { public int x; public int y;}
private static byte[,] startArray =
{
{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,5,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,0},{0,0,5,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0}
};
private static int rows = startArray.GetLength(0);
private static int cols = startArray.GetLength(1);
static void Main(string[] args)
{
Spread(startArray);
}
static void Spread(byte[,] array)
{
var points = GetStartPoints(array);
foreach (var point in points.ToList())
SpreadPoint(array, point.x, point.y);
Display(array);
}
static void SpreadPoint(byte[,] array, int x, int y)
{
for (var i = x-1; i < x+2; i++)
for (var j = y-1; j < y+2; j++)
if ( (i==x || j==y) && !(i==x && j==y) && (i >= 0 && i < rows && j >= 0 && j < cols)
&& array[i, j] + 1 < array[x, y])
{
array[i, j] = (byte)(array[x, y] - 1);
SpreadPoint(array, i, j);
}
}
static void Display(byte[,] array)
{
for (int i = 0; i < rows; i++)
{
for (int j = 0; j < cols; j++)
Console.Write("{0} ",array[i,j]);
Console.WriteLine();
}
Console.WriteLine();
}
static IEnumerable<ArrayPoint> GetStartPoints(byte[,] array)
{
for (var i = 0; i < rows; i++)
for (var j = 0; j < cols; j++)
if (array[i, j] != 0)
yield return new ArrayPoint {x = i, y = j};
}
}
Output is:
0 0 0 1 2 1 0 0 0 0
0 0 1 2 3 2 1 0 0 0
0 1 2 3 4 3 2 1 0 0
1 2 3 4 5 4 3 2 1 0
0 1 2 3 4 3 2 1 0 0
0 1 2 2 3 2 1 0 0 0
1 2 3 2 2 1 0 0 0 0
2 3 4 3 2 1 0 0 0 0
3 4 5 4 3 2 1 0 0 0
2 3 4 3 2 1 0 0 0 0
My two cents: consider expanding the points at the same time along wavefronts. Individual wavefronts could thus be terminated when done earlier than others.
A simplified JavaScript example:
var s = new Array(81)
for (var i=0; i<81; i++){
s[i] = i == 40 ? 5 : 0
}
var max = 5, m = 9, n = 9
function expand(wavefront){
while (wavefront.val > 0) {
//Southwest
var tmpLoc = wavefront.loc--
for (var i=0; i<max - 1 - wavefront.val; i++){
s[tmpLoc] = wavefront.val
tmpLoc += n + 1
}
//Southeast...
//...
wavefront.val--
}
}
expand({loc: 49, val: 4})
s now looks like this:
[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,5,0,0,0,0
,0,1,2,3,0,0,0,0,0
,0,0,1,2,0,0,0,0,0
,0,0,0,1,0,0,0,0,0
,0,0,0,0,0,0,0,0,0]

Categories