I'm trying to develop custom path-finding for the game I'm making. It's using the new 2D colliders so I can't use unity's built in navmesh. So instead of using an asset someone else created I figured I'd have a go at making my own implementation.
I've looked up a bunch of stuff about path-finding and A*, which I have no problem at all understanding. Where I'm having troubles though is the MinHeap. I've never looked into bit shifting and the like so I modified some code I found in a tutorial to suit my needs.
The problem that I'm having is that, from what I can tell in my debugging, it isn't properly sorting the nodes by cost value. From what I can see, and I'll post a picture below so you know what I'm talking about, every node that's being added to the open list is staying where it's added, so it's more like a Dijkstra's search then an A* one.
Could someone look at the code and tell me if I'm doing something very wrong.. because this is driving me mad.
This is the code:
using System;
public class NodeList<T> where T : IComparable<T>
{
private int count;
private int capacity;
private T temp;
private T mheap;
private T[] array;
private T[] tempArray;
public int Count
{
get { return this.count; }
}
public NodeList() : this(16) { }
public NodeList(int capacity)
{
this.count = 0;
this.capacity = capacity;
array = new T[capacity];
}
public void BuildHead()
{
int position;
for (position = (this.count - 1) >> 1; position >= 0; position--)
{
this.MinHeapify(position);
}
}
public void Add(T item)
{
this.count++;
if (this.count > this.capacity)
{
DoubleArray();
}
this.array[this.count - 1] = item;
int position = this.count - 1;
int parentPosition = ((position - 1) >> 1);
while (position > 0 && array[parentPosition].CompareTo(array[position]) > 0)
{
temp = this.array[position];
this.array[position] = this.array[parentPosition];
this.array[parentPosition] = temp;
position = parentPosition;
parentPosition = ((position - 1) >> 1);
}
}
private void DoubleArray()
{
this.capacity <<= 1;
tempArray = new T[this.capacity];
CopyArray(this.array, tempArray);
this.array = tempArray;
}
private static void CopyArray(T[] source, T[] destination)
{
int index;
for (index = 0; index < source.Length; index++)
{
destination[index] = source[index];
}
}
public T Peek()
{
if (this.count == 0)
{
throw new InvalidOperationException("Heap is empty");
}
return this.array[0];
}
public T ExtractFirst()
{
if (this.count == 0)
{
throw new InvalidOperationException("Heap is empty");
}
temp = this.array[0];
this.array[0] = this.array[this.count - 1];
this.count--;
this.MinHeapify(0);
return temp;
}
private void MinHeapify(int position)
{
do
{
int left = ((position << 1) + 1);
int right = left + 1;
int minPosition;
if (left < count && array[left].CompareTo(array[position]) < 0)
{
minPosition = left;
}
else
{
minPosition = position;
}
if (right < count && array[right].CompareTo(array[minPosition]) < 0)
{
minPosition = right;
}
if (minPosition != position)
{
mheap = this.array[position];
this.array[position] = this.array[minPosition];
this.array[minPosition] = mheap;
position = minPosition;
}
else
{
return;
}
} while (true);
}
}
My path searches from the end point to the start (From where the player clicks, to the player), so the "Starting point" is the grey node in the middle of the screen:
Pathfinding issue
This is why it's a problem:
Issue 2
Related
Im trying to write a quicksort algorithm in C#, and I've been getting System.StackOverflowExceptions for the last while and can't figure out why.
Here is my Class:
class quicksort : sortalgorithm
{
int pivotIndex = -1;
int pivotSwapped = 0;
Random rand = new Random();
public quicksort(int[] arr)
{
if (arr.Length < 5)
{
throw new Exception("Array has too few Entries");
}
toSort = arr;
}
protected override int sort()
{
if (pivotIndex == -1) getPivot();
int indexL = getIndexLeft();
int indexR = getIndexRight();
if (indexR != -1 && indexL != -1 && indexL != toSort.Length-1)
{
swap(indexL, indexR);
}
if (indexL > indexR)
{
Console.WriteLine("Index thingy");
swap(toSort.Length - 1, indexL);
pivotSwapped++;
if (pivotSwapped == toSort.Length - 1)
{
return 1;
}
else
{
Console.WriteLine("new piv");
pivotSwapped++;
getPivot();
sort();
return -1;
}
}
else
{
sort();
return -1;
}
}
private void swap(int i, int j)
{
int temp = toSort[i];
toSort[i] = toSort[j];
toSort[j] = temp;
}
private void getPivot()
{
pivotIndex = rand.Next(0, toSort.Length - 1);
swap(toSort.Length - 1, pivotIndex);
}
private int getIndexLeft() // Larger then Pivot Starting: Left
{ //Error Here
int itemFromLeft = -1;
for (int i = 0; i < toSort.Length - 1; i++)
{
if (toSort[i] >= toSort[toSort.Length - 1])
{
itemFromLeft = i;
i = toSort.Length + 1;
}
}
//Console.WriteLine(itemFromLeft);
return itemFromLeft;
}
private int getIndexRight()// Smaller then Pivot Starting: Right
{
int itemFromRight = -1;
for (int i = toSort.Length - 1; i >= 0; i--)
{
if (toSort[i] < toSort[toSort.Length - 1])
{
itemFromRight = i;
i = -1;
}
}
//Console.WriteLine(itemFromRight);
return itemFromRight;
}
}
I hope that someone can help me.
P.S. the class sortalgorithm in this case only has the the array toSort.
My Console output always looks a bit like this:
[4, 28, 62, 33, 11] // The unsorted array
Index thingy
new piv
Index thingy
new piv
Index thingy
new piv
Process is terminated due to `StackOverflowException`.
a stack overflow error usually happens when you have a error in your recursion , ie a function keeps calling itself until there is no room left in the stack to hold all the references,
the only obvious candidate is
**sort();**
return -1;
}
}
else
{
**sort();**
return -1;
}
so either one or the other recursive calls is probably incorrect and needs removing and i suspect the issue will be resolved
EDIT:
as you are unable to debug your code
this is what a quick sort should look like
public int sort()
{
qsort(0, toSort.Length - 1);
return 0;
}
private void qsort( int left, int right)
{
if (left < right)
{
int pivot = Partition( left, right);
if (pivot > 1)
{
qsort( left, pivot - 1);
}
if (pivot + 1 < right)
{
qsort( pivot + 1, right);
}
}
}
private int Partition( int left, int right)
{
int pivot = toSort[left];
while (true)
{
while (toSort[left] < pivot)
{
left++;
}
while (toSort[right] > pivot)
{
right--;
}
if (left < right)
{
if (toSort[left] == toSort[right]) return right;
int temp = toSort[left];
toSort[left] = toSort[right];
toSort[right] = temp;
}
else
{
return right;
}
}
}
Note that if left >= right do nothing
This simple implementation of Hoare partition scheme demonstrates how to avoid stack overflow by recursing on the smaller partition, and looping back for the larger partition, an idea used in the original quicksort. Stack space complexity is limited to O(log2(n)), but worst case time complexity is still O(n^2).
static public void Quicksort(int [] a, int lo, int hi)
{
while(lo < hi){ // while partition size > 1
int p = a[(lo + hi) / 2];
int i = lo, j = hi;
i--; // Hoare partition
j++;
while (true)
{
while (a[++i] < p) ;
while (a[--j] > p) ;
if (i >= j)
break;
int t = a[i];
a[i] = a[j];
a[j] = t;
}
i = j++; // i = last left, j = first right
if((i - lo) <= (hi - j)){ // if size(left) <= size(right)
Quicksort(a, lo, i); // recurse on smaller partition
lo = j; // and loop on larger partition
} else {
Quicksort(a, j, hi); // recurse on smaller partition
hi = i; // and loop on larger partition
}
}
}
I am currently trying my hand at making a minimax AI for tictactoe. My goal was that it should suffice for larger boards as well. However, I am having quite a hard time wrapping my head around how to implement the algorithm. I have read countless different descriptions of the algorithm but I still don't seem to be able to make it work. My result as of yet is an incredibly stupid AI. Here is my code for it.
Edit: The main point I am wondering about is how I make the AI value me not winning over forwarding itself towards the win. As of now it doesn't really care that I will win the next turn.
namespace TicTacToe_AI
{
public class Move //A class for moves
{
public int x, y, value, MoveNumber;
void SetMove(int a, int b)
{
x = a;
y = b;
}
public Move(int a, int b)
{
SetMove(a, b);
}
public Move()
{ }
}
class AI //AIClass
{
//The minimax algorithm
public Move CalculateMoves(int[,] Board, int BoardSize, int Depth, Move BestMoveAI, Move BestMovePlayer, int OriginalDepth, int CurrentTurn)
{
Depth--; //Decrease the depth for each iteration
bool Alpha = false; //Alpha-beta pruning - needs improvement
bool Beta = false;
bool WinningMove = false;
if (CurrentTurn == 1) CurrentTurn = 2;
if (CurrentTurn == 2) CurrentTurn = 1;
List<Move> DifferentMoves = new List<Move>();
List<Move> PossibleMoves = new List<Move>();
for (int i = 0; i < BoardSize; i++) //Add all possible moves to a list
{
for (int j = 0; j < BoardSize; j++)
{
if (Board[i, j] == 0)
{
Move Possible = new Move(i, j);
PossibleMoves.Add(Possible);
}
}
}
if (CurrentTurn == 2 && Depth >= 0 && Depth < BestMoveAI.MoveNumber) Alpha = true; //Alpha-beta pruning
if (CurrentTurn == 1 && Depth >= 0 && Depth < BestMovePlayer.MoveNumber) Beta = true;
if(Alpha || Beta)
{
foreach (Move TryMove in PossibleMoves) //Try every possible move to see if they are a winning move
{
int[,] Trying = new int[BoardSize, BoardSize];
Trying = (int[,])Board.Clone();
Trying[TryMove.x, TryMove.y] = CurrentTurn;
TryMove.MoveNumber = OriginalDepth - Depth;
if (Form1.Win(Trying) == 2)
{
TryMove.value = -1;
DifferentMoves.Add(TryMove);
if (Depth + 1 == OriginalDepth)
{
if (TryMove.MoveNumber < BestMoveAI.MoveNumber) BestMoveAI = TryMove;
WinningMove = true;
break;
}
else
{
WinningMove = true;
if (TryMove.MoveNumber < BestMoveAI.MoveNumber) BestMoveAI = TryMove;
return TryMove;
}
}
else if (Form1.Win(Trying) == 1)
{
WinningMove = true;
TryMove.value = 1;
BestMovePlayer = TryMove;
DifferentMoves.Add(TryMove);
return TryMove;
}
}
if (!WinningMove) // If no winning move was found, try recursively searching for a winning move
{
if (Alpha || Beta)
{
foreach (Move TryMove2 in PossibleMoves)
{
int[,] TestMove = new int[BoardSize, BoardSize];
TestMove = (int[,])Board.Clone();
TestMove[TryMove2.x, TryMove2.y] = CurrentTurn;
TryMove2.value = CalculateMoves(TestMove, BoardSize, Depth, BestMoveAI, BestMovePlayer, OriginalDepth, CurrentTurn).value;
DifferentMoves.Add(TryMove2);
}
}
}
}
//Find the best possible move and return it
BestMoveAI.value = 0;
BestMoveAI.MoveNumber = OriginalDepth;
BestMovePlayer.value = 0;
BestMovePlayer.MoveNumber = OriginalDepth;
if (CurrentTurn == 2)
{
foreach (Move AllMoves in DifferentMoves)
{
if (AllMoves.value <= BestMoveAI.value && AllMoves.MoveNumber <= BestMoveAI.MoveNumber)
{
BestMoveAI = AllMoves;
}
}
return BestMoveAI;
}
else if(CurrentTurn == 1)
{
foreach (Move AllMoves in DifferentMoves)
{
if (AllMoves.value >= BestMovePlayer.value && AllMoves.MoveNumber <= BestMovePlayer.MoveNumber)
{
BestMovePlayer = AllMoves;
}
}
return BestMovePlayer;
}
Move BadMove = new Move();
BadMove.value = 0;
BadMove.MoveNumber = Depth;
return BadMove;
}
}
}
I was trying to implement an Enumerable that replicates what LINQ OrderBy does. To do so, I used two approaches:
I used Mono's Linq/Enumerable.cs as base and replicated the OrderBy code in my Enumerable.
I used a decompiler to get an impression of .NET's version and replicated that code.
When benchmarking the LINQ version versus both options (I used .Take(10) to print less output), the linq version is significantly faster (1900ms vs 3700ms). The source data was List<MyObject>, sorted by char member. Optimized build flag was on.
Could someone please explain me where this difference could come from?
Edit:
I will outline the code for 2. below:
Here, Buffer<TElement> is a copy of System.Linq.Buffer<TElement> (copied from ILSpy as it is internal) and Sort is (mostly) copied in the same way from System.Linq.EnumerableSorter<TElement>.
public class Query2F
{
private Func<Lineitem, char> keySelector;
private char[] keys;
private IComparer<char> comparer;
private bool descending;
public Query2F(Func<Lineitem, char> keySelector, bool descending)
{
this.keySelector = keySelector;
this.descending = descending;
this.comparer = Comparer<char>.Default;
}
public static IEnumerable<Lineitem> Execute(List<Lineitem> lineitem)
{
Buffer<Lineitem> buffer = new Buffer<Lineitem>(lineitem);
if (buffer.count > 0)
{
Query2F q2f = new Query2F((s => s.returnflag), false);
int[] array = q2f.Sort(buffer.items, buffer.count);
q2f = null;
for (int i = 0; i < buffer.count; i++)
{
yield return buffer.items[array[i]];
}
}
yield break;
}
private void ComputeKeys(Lineitem[] elements, int count)
{
this.keys = new char[count];
for (int i = 0; i < count; i++)
{
//this.keys[i] = this.keySelector(elements[i]);
this.keys[i] = elements[i].returnflag;
}
}
private int CompareKeys(int index1, int index2)
{
int num = this.comparer.Compare(this.keys[index1], this.keys[index2]);
if (num == 0)
{
return index1 - index2;
}
else
{
if (!this.descending)
{
return num;
}
return -num;
}
}
internal int[] Sort(Lineitem[] elements, int count)
{
this.ComputeKeys(elements, count);
int[] array = new int[count];
for (int i = 0; i < count; i++)
{
array[i] = i;
}
this.QuickSort(array, 0, count - 1);
return array;
}
private void QuickSort(int[] map, int left, int right)
{
do
{
int num = left;
int num2 = right;
int index = map[num + (num2 - num >> 1)];
do
{
if (num < map.Length)
{
if (this.CompareKeys(index, map[num]) > 0)
{
num++;
continue;
}
}
while (num2 >= 0 && this.CompareKeys(index, map[num2]) < 0)
{
num2--;
}
if (num > num2)
{
break;
}
if (num < num2)
{
int num3 = map[num];
map[num] = map[num2];
map[num2] = num3;
}
num++;
num2--;
}
while (num <= num2);
if (num2 - left <= right - num)
{
if (left < num2)
{
this.QuickSort(map, left, num2);
}
left = num;
}
else
{
if (num < right)
{
this.QuickSort(map, num, right);
}
right = num2;
}
}
while (left < right);
}
}
internal struct Buffer<TElement>
{
internal TElement[] items;
internal int count;
internal Buffer(IEnumerable<TElement> source)
{
TElement[] array = null;
int num = 0;
ICollection<TElement> collection = source as ICollection<TElement>;
if (collection != null)
{
num = collection.Count;
if (num > 0)
{
array = new TElement[num];
collection.CopyTo(array, 0);
}
}
else
{
foreach (TElement current in source)
{
if (array == null)
{
array = new TElement[4];
}
else
{
if (array.Length == num)
{
TElement[] array2 = new TElement[checked(num * 2)];
Array.Copy(array, 0, array2, 0, num);
array = array2;
}
}
array[num] = current;
num++;
}
}
this.items = array;
this.count = num;
}
internal TElement[] ToArray()
{
if (this.count == 0)
{
return new TElement[0];
}
if (this.items.Length == this.count)
{
return this.items;
}
TElement[] array = new TElement[this.count];
Array.Copy(this.items, 0, array, 0, this.count);
return array;
}
}
Problem solved.Thanks to Mike for pointing me to the solution.
The reason for the discrepancies in the execution time were merely that I executed the wrong build (which was not a Release build).
I need to sort out the values in such a way that the toReturn list should contain the following:
Test(1100, 1130)
Test(1134, 1200)
Test(1210, 1310)
Test(1100, 1140)
Test(1145, 1210)
Test(1220, 1320)
I get:
Test(1100, 1130)
Test(1134, 1200)
Test(1210, 1310)
Test(1100, 1140)
Test(1145, 1210)
Test(1134, 1200)
Test(1220, 1320)
Basically, looping through the three lists and checking if the Start of second is > End of first.
I have tried various ways to solve this and this the closest I could get, but still don't get the results as I expect.
Can someone please advise what I might be doing wrong?
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication22
{
class Program
{
static List<Test> toReturn = new List<Test>();
static void Main(string[] args)
{
List<Test> l1 = new List<Test>();
l1.Add(new Test(1100, 1130));
l1.Add(new Test(1100, 1140));
l1.Add(new Test(1110, 1150));
List<Test> l2 = new List<Test>();
l2.Add(new Test(1134, 1200));
l2.Add(new Test(1145, 1210));
List<Test> l3 = new List<Test>();
l3.Add(new Test(1210, 1310));
l3.Add(new Test(1220, 1320));
List<List<Test>> container = new List<List<Test>>();
container.Add(l1);
container.Add(l2);
container.Add(l3);
sort(container, 0, 0);
}
private static void sort(List<List<Test>> temp, int position, int time)
{
if (position + 1 == temp.Count)
{
return;
}
List<Test> tt = temp[position];
if (position + 1 < temp.Count)
{
List<Test> tt1 = temp[position + 1];
for (int i = 0; i < tt.Count; i++)
{
int t1 = tt[i].End;
for (int j = 0; j < tt1.Count; j++)
{
int t2 = tt1[j].Start;
if (t2 > t1 && t2 > time)
{
//if (toReturn.Count == 0)
//{
if (toReturn.Count == 0)
{
toReturn.Add(tt[i]);
}
else
{
if (toReturn[toReturn.Count - 1].End != tt[i].End && toReturn[toReturn.Count - 1].Start != tt[i].Start)
{
toReturn.Add(tt[i]);
}
}
if (toReturn[toReturn.Count - 1].End != tt1[j].End && toReturn[toReturn.Count - 1].Start != tt1[j].Start)
{
toReturn.Add(tt1[j]);
}
sort(temp, position + 1, tt1[j].End);
break;
}
}
if (position > 0)
{
return;
}
}
}
String val = "";
//return toReturn;
}
}
class Test
{
int start;
public Test(int val1, int val2)
{
Start = val1;
End = val2;
}
public int Start
{
get { return start; }
set { start = value; }
}
int end;
public int End
{
get { return end; }
set { end = value; }
}
}
}
Use a comparator, as suggested: An element is less than another if its End is less than the other's Start. An element is greater than another if its Start is greater than the other's End.
class TestComparer : IComparer<Test>
{
int Compare(Test a, Test b)
{
if (a.End < b.Start) return -1;
else if (a.Start > b.End) return 1;
else return 0;
}
}
...
List.Sort(list, new TestComparer());
Edit After re-reading your question, I would follow an approach like the one below:
While there is an element left in the list
Find element with smallest Start
Add it to the resulting list, and remove it from the list
While an element with a larger Start then the last item's End in the resulting list exists
Add it to the resulting list, and remove it from the list
Something like:
while (list.Count > 0)
{
int i = GetSmallestTestIdx(list);
do
{
result.Add(list[i]);
list.RemoveAt(i);
}
while ((i = GetNextTextIdx(list, result)) != -1);
}
Whats the best way to swap two ListView items in C#? I see that the standard ListView doesn't implement such functionality.
--
Best Regards,
Murat
Building upon the KnowDotNet article ref'd by Murat, here's my extension method that is a bit more flexible (it operates on any item, not just the cursel), and bugfixed (BeginUpdate/Endupdate for less flicker, EnsureVisible, and bounds checking).
Doesn't need to be an extension method, but I like them :)
namespace YourApp
{
public static class MyExtensions
{
// Based upon http://www.knowdotnet.com/articles/listviewmoveitem.html
public static void MoveSelectedItem(this System.Windows.Forms.ListView lv, int idx, bool moveUp)
{
// Gotta have >1 item in order to move
if(lv.Items.Count > 1)
{
int offset = 0;
if (idx >= 0)
{
if (moveUp)
{
// ignore moveup of row(0)
offset = -1;
}
else
{
// ignore movedown of last item
if (idx < (lv.Items.Count - 1))
offset = 1;
}
}
if (offset != 0)
{
lv.BeginUpdate();
int selitem = idx + offset;
for (int i = 0; i < lv.Items[idx].SubItems.Count; i++)
{
string cache = lv.Items[selitem].SubItems[i].Text;
lv.Items[selitem].SubItems[i].Text = lv.Items[idx].SubItems[i].Text;
lv.Items[idx].SubItems[i].Text = cache;
}
lv.Focus();
lv.Items[selitem].Selected = true;
lv.EnsureVisible(selitem);
lv.EndUpdate();
}
}
}
}
}
If you use custom ListViewItem, or object you cannot clone object, or stock in string:
enum Direction { UP = -1, DOWN = +1};
void ListViewMove(ListView lv, Direction direction)
{
if (lv.SelectedItems.Count > 0)
{
int selIdx = lv.SelectedItems[0].Index;
ListViewItem tmp = lv.Items[selIdx] ;
if ( ( (selIdx != 0) && direction == Direction.UP ) ||
((selIdx!=lv.Items.Count-1) && (direction == Direction.DOWN)) )
{
lv.Items.RemoveAt(selIdx);
tmp = lv.Items.Insert(selIdx + (int)direction, tmp);
tmp.Selected = true;
}
}
lv.Focus();
}
Both ASP.NET and Winforms ListView have Items property which allows to add or removed items.
I´ve wrote a little sample that should work.
ListViewItem[] copyOfItemsInListView1 = new ListViewItem[listView1.Items.Count];
ListViewItem[] copyOfItemsInListView2 = new ListViewItem[listView2.Items.Count];
listView1.Items.CopyTo(copyOfItemsInListView1, 0);
listView2.Items.CopyTo(copyOfItemsInListView2, 0);
listView1.Items.Clear();
listView2.Items.Clear();
for (int i = 0; i < copyOfItemsInListView2.Length; i++)
{
listView1.Items.Add(copyOfItemsInListView2[i]);
}
for (int i = 0; i < copyOfItemsInListView1.Length; i++)
{
listView2.Items.Add(copyOfItemsInListView1[i]);
}
Clone them:
// move selected item up
int selectedIndex = mListView.SelectedIndices[0];
if (selectedIndex > 0)
{
ListViewItem item1 = (ListViewItem)mListView.Items[selectedIndex - 1].Clone();
ListViewItem item2 = (ListViewItem)mListView.Items[selectedIndex].Clone();
mListView.Items[selectedIndex - 1] = item2;
mListView.Items[selectedIndex] = item1;
mListView.SelectedIndices.Remove(selectedIndex);
mListView.SelectedIndices.Add(selectedIndex - 1);
}