I am trying to implement the algorithm described in the following http://repositorium.sdum.uminho.pt/bitstream/1822/6429/1/ConcaveHull_ACM_MYS.pdf
I am using the following class libraries. Loyc libs come from http://core.loyc.net/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Device.Location;
using Loyc.Collections;
using Loyc.Geometry;
using Loyc;
Here is the basic class
public class Hulls
{
private static List<Point<double>> KNearestNeighbors(List<Point<double>> points, Point<double> currentPoint, int k, out int kk)
{
kk = Math.Min(k, points.Count - 1);
var ret = points
.OrderBy(x => PointMath.Length(new Vector<double>(currentPoint.X - x.X, currentPoint.Y - x.Y)))
.Take(k)
.ToList();
return ret;
}
private static double Angle(Point<double> a, Point<double> b)
{
var ret = -Math.Atan2(b.Y - a.Y, b.X - a.X);
return NormaliseAngle(ret);
}
private static double NormaliseAngle(double a)
{
//while (a < b - Math.PI) a += Math.PI * 2;
//while (b < a - Math.PI) b += Math.PI * 2;
if (a < 0.0) { return a + Math.PI + Math.PI; }
return a;
}
private static List<Point<double>> SortByAngle(List<Point<double>> kNearest, Point<double> currentPoint, double angle)
{
//kNearest
// .Sort((v1, v2) => AngleDifference(angle, Angle(currentPoint, v1)).CompareTo(AngleDifference(angle, Angle(currentPoint, v2))));
//return kNearest.ToList();
kNearest = kNearest.OrderByDescending(x => NormaliseAngle(Angle(currentPoint, x) - angle)).ToList();
return kNearest;
}
private static bool CCW(Point<double> p1, Point<double> p2, Point<double> p3)
{
var cw = ((p3.Y - p1.Y) * (p2.X - p1.X)) - ((p2.Y - p1.Y) * (p3.X - p1.X));
return cw > 0 ? true : cw < 0 ? false : true; // colinear
}
private static bool _Intersect(LineSegment<double> seg1, LineSegment<double> seg2)
{
return CCW(seg1.A, seg2.A, seg2.B) != CCW(seg1.B, seg2.A, seg2.B) && CCW(seg1.A, seg1.B, seg2.A) != CCW(seg1.A, seg1.B, seg2.B);
}
private static bool Intersect(LineSegment<double> seg1, LineSegment<double> seg2)
{
if ((seg1.A.X == seg2.A.X && seg1.A.Y == seg2.A.Y)
|| (seg1.B.X == seg2.B.X && seg1.B.Y == seg2.B.Y))
{
return false;
}
if (_Intersect(seg1, seg2))
{
return true;
}
return false;
}
public IListSource<Point<double>> KNearestConcaveHull(List<Point<double>> points, int k)
{
points.Sort((a, b) => a.Y == b.Y ? (a.X > b.X ? 1 : -1) : (a.Y >= b.Y ? 1 : -1));
Console.WriteLine("Starting with size {0}", k.ToString());
DList<Point<double>> hull = new DList<Point<double>>();
var len = points.Count;
if (len < 3) { return null; }
if (len == 3) { return hull; }
var kk = Math.Min(Math.Max(k, 3), len);
var dataset = new List<Point<double>>();
dataset.AddRange(points.Distinct());
var firstPoint = dataset[0];
hull.PushFirst(firstPoint);
var currentPoint = firstPoint;
dataset.RemoveAt(0);
double previousAngle = 0;
int step = 2;
int i;
while ((currentPoint != firstPoint || step == 2) && dataset.Count > 0)
{
if (step == 5) { dataset.Add(firstPoint); }
var kNearest = KNearestNeighbors(dataset, currentPoint, k, out kk);
var cPoints = SortByAngle(kNearest, currentPoint, previousAngle);
var its = true;
i = 0;
while (its == true && i < cPoints.Count)
{
i++;
int lastPoint = 0;
if (cPoints[i - 1] == firstPoint)
{
lastPoint = 1;
}
int j = 2;
its = false;
while (its == false && j < hull.Count - lastPoint)
{
LineSegment<double> line1 = new LineSegment<double>(hull[step - 2], cPoints[i - 1]);
LineSegment<double> line2 = new LineSegment<double>(hull[step - 2 - j], hull[step - 1 - j]);
//its = LineMath.ComputeIntersection(line1, line2, out pfrac, LineType.Segment);
its = Intersect(line1, line2);
j++;
}
}
if (its == true)
{
return KNearestConcaveHull(points, kk + 1);
}
currentPoint = cPoints[i - 1];
hull.PushLast(currentPoint);
previousAngle = Angle(hull[step - 1], hull[step - 2]);
dataset.Remove(currentPoint);
step++;
}
bool allInside = true;
i = dataset.Count;
while (allInside == true && i > 0)
{
allInside = PolygonMath.IsPointInPolygon(hull, dataset[i - 1]);
i--;
}
if (allInside == false) { return KNearestConcaveHull(points, kk + 1); }
return hull;
}
}
The above is supposed to pick a new edge for the boundary based on the furthest right-hand turn from the previous edge going around the point set counterclockwise. The code seems to pick the correct first edge from the initial vertex which has the lowest y-value, but then does not pick the next edge correctly when the offset angle is nonzero. I think the issue is the SortByAngle or Angle. -atan2 would return the clockwise turn, correct? Possibly I should be adding the offset angle?
EDIT (SOLUTION): Found the issue after following Eric's helpful advice provided in the first comment to the question. It was SortByAngle and Angle:
private static double Angle(Point<double> a, Point<double> b)
{
var ret = Math.Atan2(b.Y - a.Y, b.X - a.X);
return NormaliseAngle(ret);
}
private static double NormaliseAngle(double a)
{
if (a < 0.0) { return a + Math.PI + Math.PI; }
return a;
}
private static List<Point<double>> SortByAngle(List<Point<double>> kNearest, Point<double> currentPoint, double angle)
{
//kNearest = kNearest.OrderByDescending(x => NormaliseAngle(Angle(currentPoint, x) - angle)).ToList();
kNearest.Sort((a, b) => NormaliseAngle(Angle(currentPoint, a) - angle) > NormaliseAngle(Angle(currentPoint, b) - angle) ? 1 : -1);
return kNearest;
}
You have some bug:
var kNearest = KNearestNeighbors(dataset, currentPoint, k, out kk);
Change the kk to just some var. You override the incrementation of that "kk" value, and then you're getting StackOverflow exceptions.
Change your code to the following:
int someVal;
var kNearest = KNearestNeighbors(dataset, currentPoint, k, out someVal);
Related
I'm trying to get the job done MOHIBPIZ - PIZZA (https://www.spoj.com/problems/MOHIBPIZ/).
I'm already sitting on it the second day, I've tried everything I can and found on the internet. The last chance before giving up is to ask you guys
For recudces time I'm using InputOutput class created by davidsekar (https://github.com/davidsekar/C-sharp-Programming-IO/blob/master/ConsoleInOut/InputOutput.cs)
but still I have time "time limit exceeded". :(
I tried with two loops, but the method with the function seems more optimal to me. Thanks in advance for all the hints, suggestions and answers.
This is code (link on ideone: https://ideone.com/):
using System;
using System.IO;
public class Test
{
public static void Main()
{
InputOutput reader = new InputOutput();
StreamWriter _output = new StreamWriter(Console.OpenStandardOutput());
int T = reader.ReadInt();
for (int i = 0; i < T; i++)
{
_output.WriteLine(Recursion(reader.ReadInt()));
}
_output.Flush();
}
private static int Recursion(int x)
{
if(x <= 1)
{
return 2;
}
else
{
return Recursion(x - 1) + x;
}
}
#region Input Output Helper
public class InputOutput : System.IDisposable
{
private System.IO.Stream _readStream, _writeStream;
private int _readIdx, _bytesRead, _writeIdx, _inBuffSize, _outBuffSize;
private readonly byte[] _inBuff, _outBuff;
private readonly bool _bThrowErrorOnEof;
public void SetBuffSize(int n)
{
_inBuffSize = _outBuffSize = n;
}
public InputOutput(bool throwEndOfInputsError = false)
{
_readStream = System.Console.OpenStandardInput();
_writeStream = System.Console.OpenStandardOutput();
_readIdx = _bytesRead = _writeIdx = 0;
_inBuffSize = _outBuffSize = 1 << 22;
_inBuff = new byte[_inBuffSize];
_outBuff = new byte[_outBuffSize];
_bThrowErrorOnEof = throwEndOfInputsError;
}
public void SetFilePath(string strPath)
{
strPath = System.IO.Path.GetFullPath(strPath);
_readStream = System.IO.File.Open(strPath, System.IO.FileMode.Open);
}
public T ReadNumber<T>()
{
byte rb;
while ((rb = GetByte()) < '-')
;
var neg = false;
if (rb == '-')
{
neg = true;
rb = GetByte();
}
dynamic m = (T)Convert.ChangeType(rb - '0', typeof(T));
while (true)
{
rb = GetByte();
if (rb < '0')
break;
m = m * 10 + (rb - '0');
}
return neg ? -m : m;
}
public int ReadInt()
{
byte readByte;
while ((readByte = GetByte()) < '-')
;
var neg = false;
if (readByte == '-')
{
neg = true;
readByte = GetByte();
}
var m = readByte - '0';
while (true)
{
readByte = GetByte();
if (readByte < '0')
break;
m = m * 10 + (readByte - '0');
}
return neg ? -m : m;
}
public string ReadString()
{
return ReadString(' ');
}
public string ReadString(string delimiter)
{
return ReadString(delimiter[0]);
}
public string ReadString(char delimiter)
{
byte readByte;
while ((readByte = GetByte()) <= delimiter)
;
System.Text.StringBuilder sb = new System.Text.StringBuilder();
do
{
sb.Append((char)readByte);
} while ((readByte = GetByte()) > delimiter);
return sb.ToString();
}
[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
private byte GetByte()
{
if (_readIdx >= _bytesRead)
{
_readIdx = 0;
_bytesRead = _readStream.Read(_inBuff, 0, _inBuffSize);
if (_bytesRead >= 1)
return _inBuff[_readIdx++];
if (_bThrowErrorOnEof)
throw new System.Exception("End Of Input");
_inBuff[_bytesRead++] = 0;
}
return _inBuff[_readIdx++];
}
public void WriteToBuffer(string s)
{
foreach (var b in System.Text.Encoding.ASCII.GetBytes(s))
{
if (_writeIdx == _outBuffSize)
Flush();
_outBuff[_writeIdx++] = b;
}
}
public void WriteLineToBuffer(string s)
{
WriteToBuffer(s);
if (_writeIdx == _outBuffSize)
Flush();
_outBuff[_writeIdx++] = 10;
}
public void WriteToBuffer(int c)
{
byte[] temp = new byte[10];
int tempidx = 0;
if (c < 0)
{
if (_writeIdx == _outBuffSize)
Flush();
_outBuff[_writeIdx++] = (byte)'-';
c = -c;
}
do
{
temp[tempidx++] = (byte)((c % 10) + '0');
c /= 10;
} while (c > 0);
for (int i = tempidx - 1; i >= 0; i--)
{
if (_writeIdx == _outBuffSize)
Flush();
_outBuff[_writeIdx++] = temp[i];
}
}
public void WriteLineToBuffer(int c)
{
WriteToBuffer(c);
if (_writeIdx == _outBuffSize)
Flush();
_outBuff[_writeIdx++] = 10;
}
private void Flush()
{
_writeStream.Write(_outBuff, 0, _writeIdx);
_writeStream.Flush();
_writeIdx = 0;
}
public void Dispose()
{
Flush();
_writeStream.Close();
_readStream.Close();
}
}
#endregion Input Output Helper
}
As far as I can see, you have a well known Circle Division problem; see also A000124 sequence:
number of pieces after n cuts are (n * n + n + 2) / 2
That's why we can put O(1) time and space complexity
Code:
private static int Solution(int n) => (int)(((long)n * n + n + 2) / 2);
Here I've put (long) n in case n * n exceeds int.MaxValue, when (n * n + n + 2) / 2 doesn't.
Edit: I've implemented int Solution(int n) method which is based on current code int Recursion(int x) signature; but if there're tests for large n we are going to have integer overflow.
In this case
private static long Solution(long n) =>
1 + (n % 2 == 0 ? n / 2 * (n + 1) : (n + 1) / 2 * n);
In case of arbitrary n we have to use BigInteger:
using System.Numerics;
...
private static BigInteger Solution(BigInteger n) =>
1 + (n * n + n) / 2;
Is there a way to set custom cost for different operations? Like:
Replace = 1
Insert = 1.5
Delete = 1.2
The closest I found is https://github.com/glienard/StringSimilarity.NET with Weighted Levenshtein, but I could not find out how to actually use it.
My (simplified) version is something like this.
Help classes:
[Flags]
public enum EditOperationKind {
None = 0,
Insert = 1,
Delete = 2,
Edit = Insert | Delete
}
public sealed class EditOperation<T>
: IEquatable<EditOperation<T>> {
internal string ToReport() {
switch (Kind) {
case EditOperationKind.None: return $"Keep '{Before}', ({Cost})";
case EditOperationKind.Insert: return $"Insert '{After}', ({Cost})";
case EditOperationKind.Delete: return $"Delete '{Before}', ({Cost})";
case EditOperationKind.Edit: return $"Edit '{Before}' into '{After}', ({Cost})";
default: return $"??? '{Before}' into '{After}', ({Cost})";
};
}
internal EditOperation(EditOperationKind kind, T before, T after, double cost)
: base() {
Kind = kind;
Before = before;
After = after;
Cost = cost;
}
public EditOperationKind Kind { get; }
public T Before { get; }
public T After { get; }
public double Cost { get; private set; }
public override string ToString() {
switch (Kind)
{
case EditOperationKind.None: return $"Keep '{Before}'";
case EditOperationKind.Insert: return $"Insert '{After}'";
case EditOperationKind.Delete: return $"Delete '{Before}'";
case EditOperationKind.Edit: return $"Edit '{Before}' into '{After}'";
default: return $"Unknown '{Before}' into '{After}'";
};
}
public static bool operator ==(EditOperation<T> left, EditOperation<T> right) {
if (ReferenceEquals(left, right))
return true;
else if (null == left || null == right)
return false;
return left.Equals(right);
}
public static bool operator !=(EditOperation<T> left, EditOperation<T> right) {
if (ReferenceEquals(left, right))
return false;
else if (null == left || null == right)
return true;
return !left.Equals(right);
}
public bool Equals(EditOperation<T> other) {
if (ReferenceEquals(this, other))
return true;
else if (null == other)
return false;
return object.Equals(this.Before, other.Before) &&
object.Equals(this.After, other.After) &&
this.Kind == other.Kind;
}
public override bool Equals(object obj) => Equals(obj as EditOperation<T>);
public override int GetHashCode() {
return (Before == null ? 0 : Before.GetHashCode()) ^
(After == null ? 0 : After.GetHashCode()) ^
(int)Kind;
}
}
Main class:
public sealed class EditProcedure<T> : IReadOnlyList<EditOperation<T>> {
private List<EditOperation<T>> m_Sequence;
private void CorePerform(T[] source,
T[] target,
Func<T, double> insertCost,
Func<T, double> deleteCost,
Func<T, T, double> editCost) {
// Best operation (among insert, update, delete) to perform
EditOperationKind[][] M = Enumerable
.Range(0, source.Length + 1)
.Select(line => new EditOperationKind[target.Length + 1])
.ToArray();
// Minimum cost so far
double[][] D = Enumerable
.Range(0, source.Length + 1)
.Select(line => new double[target.Length + 1])
.ToArray();
// Edge: all removes
double sum = 0.0;
for (int i = 1; i <= source.Length; ++i) {
M[i][0] = EditOperationKind.Delete;
D[i][0] = (sum += deleteCost(source[i - 1]));
}
// Edge: all inserts
sum = 0.0;
for (int i = 1; i <= target.Length; ++i) {
M[0][i] = EditOperationKind.Insert;
D[0][i] = (sum += insertCost(target[i - 1]));
}
// Having fit N - 1, K - 1 characters let's fit N, K
for (int i = 1; i <= source.Length; ++i)
for (int j = 1; j <= target.Length; ++j) {
// here we choose the operation with the least cost
double insert = D[i][j - 1] + insertCost(target[j - 1]);
double delete = D[i - 1][j] + deleteCost(source[i - 1]);
double edit = D[i - 1][j - 1] + editCost(source[i - 1], target[j - 1]);
double min = Math.Min(Math.Min(insert, delete), edit);
if (min == insert)
M[i][j] = EditOperationKind.Insert;
else if (min == delete)
M[i][j] = EditOperationKind.Delete;
else if (min == edit)
M[i][j] = object.Equals(source[i - 1], target[j - 1])
? EditOperationKind.None
: EditOperationKind.Edit;
D[i][j] = min;
}
EditDistance = D[source.Length][target.Length];
// Backward: knowing scores (D) and actions (M) let's building edit sequence
m_Sequence =
new List<EditOperation<T>>(source.Length + target.Length);
for (int x = target.Length, y = source.Length; (x > 0) || (y > 0);) {
EditOperationKind op = M[y][x];
if (op == EditOperationKind.Insert) {
x -= 1;
m_Sequence.Add(new EditOperation<T>(op, default, target[x], D[y][x + 1] - D[y][x]));
}
else if (op == EditOperationKind.Delete) {
y -= 1;
m_Sequence.Add(new EditOperation<T>(op, source[y], default, D[y + 1][x] - D[y][x]));
}
else if (op == EditOperationKind.Edit || op == EditOperationKind.None) {
x -= 1;
y -= 1;
m_Sequence.Add(new EditOperation<T>(op, source[y], target[x], D[y + 1][x + 1] - D[y][x]));
}
else // Start of the matching (EditOperationKind.None)
break;
}
m_Sequence.Reverse();
}
public EditProcedure(IEnumerable<T> source,
IEnumerable<T> target,
Func<T, double> insertCost,
Func<T, double> deleteCost,
Func<T, T, double> editCost) {
if (null == source)
throw new ArgumentNullException(nameof(source));
else if (null == target)
throw new ArgumentNullException(nameof(target));
else if (null == insertCost)
throw new ArgumentNullException(nameof(insertCost));
else if (null == deleteCost)
throw new ArgumentNullException(nameof(deleteCost));
else if (null == editCost)
throw new ArgumentNullException(nameof(editCost));
CorePerform(source.ToArray(),
target.ToArray(),
insertCost,
deleteCost,
editCost);
}
public double EditDistance { get; private set; }
public IReadOnlyList<EditOperation<T>> EditSequence => m_Sequence;
public override string ToString() {
return string.Join(Environment.NewLine,
$"Distance: {EditDistance}",
$"Sequence ({m_Sequence.Count} steps):",
string.Join(Environment.NewLine, m_Sequence
.Select(item => $" {item.ToReport()}")));
}
public int Count => m_Sequence.Count;
public EditOperation<T> this[int index] => m_Sequence[index];
public IEnumerator<EditOperation<T>> GetEnumerator() => m_Sequence.GetEnumerator();
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => m_Sequence.GetEnumerator();
}
Demo:
string source = "abracadabra";
string target = "alakazam";
var edit = new EditProcedure<char>(
source,
target,
insertChar => 1.5,
deleteChar => 1.2,
(fromChar, toChar) => fromChar == toChar ? 0 : 1.0);
Console.WriteLine(edit.EditDistance);
Console.WriteLine();
Console.WriteLine(edit.ToString());
Outcome:
7.6
Distance: 7.6
Sequence (11 steps):
Keep 'a', (0)
Edit 'b' into 'l', (1)
Delete 'r', (1.2)
Keep 'a', (0)
Edit 'c' into 'k', (1)
Keep 'a', (0)
Edit 'd' into 'z', (1)
Keep 'a', (0)
Edit 'b' into 'm', (1)
Delete 'r', (1.2)
Delete 'a', (1.2)
Somebody had posted an answer just to remove it 1 hour later. I modified it a bit and here is the result:
public class Levenshtein
{
public double ReplaceCost { get; set; } = 1;
public double DeleteCost { get; set; } = 1;
public double InsertCost { get; set; } = 1;
public double Distance(string source, string target, bool caseSensitive = true)
{
if (!caseSensitive)
{
source = source.ToUpperInvariant();
target = target.ToUpperInvariant();
}
int xLength = source.Length;
int yLength = target.Length;
//short circuit
if (xLength == 0) return yLength;
if (yLength == 0) return xLength;
//create path matrix
var d = new double[xLength + 1, yLength + 1];
for (int i = 0; i <= xLength; i++)
{
d[i, 0] = i;
}
for (int j = 0; j <= yLength; j++)
{
d[0, j] = j;
}
//navigate best route
for (int i = 1; i <= xLength; i++)
{
for (int j = 1; j <= yLength; j++)
{
double diagCost = (target[j - 1] == source[i - 1]) ? 0 : ReplaceCost;
double diagMovement = d[i - 1, j - 1] + diagCost; //replace
double horzMovement = d[i - 1, j] + DeleteCost; //delete
double vertMovement = d[i, j - 1] + InsertCost; //insert (does not appear in source)
d[i, j] = Math.Min(Math.Min(horzMovement, vertMovement), diagMovement);
}
}
return d[xLength, yLength];
}
}
I want to know when four Vector 3 positions intersect, as in the image, from point to point a1 to a2 and b1 to b2 if they intersect each other from each of their positions. Does anyone have any idea how to do this?
The Unity community wiki has a page of math functions that includes a very helpful procedure for this. Copied (and edited slightly) below:
public static bool LineLineIntersection(out Vector3 intersection, Vector3 linePoint1,
Vector3 lineVec1, Vector3 linePoint2, Vector3 lineVec2){
Vector3 lineVec3 = linePoint2 - linePoint1;
Vector3 crossVec1and2 = Vector3.Cross(lineVec1, lineVec2);
Vector3 crossVec3and2 = Vector3.Cross(lineVec3, lineVec2);
float planarFactor = Vector3.Dot(lineVec3, crossVec1and2);
//is coplanar, and not parallel
if( Mathf.Abs(planarFactor) < 0.0001f
&& crossVec1and2.sqrMagnitude > 0.0001f)
{
float s = Vector3.Dot(crossVec3and2, crossVec1and2)
/ crossVec1and2.sqrMagnitude;
intersection = linePoint1 + (lineVec1 * s);
return true;
}
else
{
intersection = Vector3.zero;
return false;
}
}
So, in your situation, you could use that, then check if the intersection point is between a1 and a2 and b1 and b2:
Vector3 intersection;
Vector3 aDiff = a2-a1;
Vector3 bDiff = b2-b1;
if (LineLineIntersection(out intersection, a1, aDiff, b1, bDiff))
{
float aSqrMagnitude = aDiff.sqrMagnitude;
float bSqrMagnitude = bDiff.sqrMagnitude;
if ( (intersection - a1).sqrMagnitude <= aSqrMagnitude
&& (intersection - a2).sqrMagnitude <= aSqrMagnitude
&& (intersection - b1).sqrMagnitude <= bSqrMagnitude
&& (intersection - b2).sqrMagnitude <= bSqrMagnitude)
{
// there is an intersection between the two segments and
// it is at intersection
}
}
You could also use this option if you want to dispense with the UnityEngine library:
We define a 2d vector as:
public class V2
{
public static V2 zero = new V2(0, 0);
public readonly float x;
public readonly float y;
[JsonConstructor]
public V2(float x, float y)
{
if (
physicsLogic.debugOn &&
(float.IsNaN(x) || float.IsNaN(y) || float.IsInfinity(y) || float.IsInfinity(x)))
{
throw new Exception($"Dodgy V2 ({x},{y})");
}
this.x = x; this.y = y;
}
public FieldPoint asFlooredFieldPoint()
{
return new FieldPoint(x,0,y);
}
public static V2 operator +(V2 b, V2 a) => new V2(b.x + a.x, b.y + a.y);
public static V2 operator -(V2 b, V2 a) => new V2(b.x - a.x, b.y - a.y);
public static V2 operator /(V2 a, double b) => new V2((float)(a.x / b), (float)(a.y / b));
public static V2 operator *(V2 a, double b) => new V2((float)(a.x * b), (float)(a.y * b));
public static V2 operator *(double b, V2 a) => new V2((float)(a.x * b), (float)(a.y * b));
[JsonIgnore]
float mag = -1;
[JsonIgnore]
public float magnitude
{
get
{
if (mag < 0)
{
mag = (float)Math.Sqrt(sqrMagnitude);
}
return mag;
}
}
[JsonIgnore]
public V2 normalized
{
get
{
var mag = magnitude;
if (mag == 0)
{
return V2.zero;
}
return new V2(x / mag, y / mag);
}
}
[JsonIgnore]
float _sqrMagnitude = -1;
[JsonIgnore]
public float sqrMagnitude
{
get
{
if (_sqrMagnitude < 0)
{
_sqrMagnitude = (float)(Math.Pow(x, 2) + Math.Pow(y, 2));
}
return _sqrMagnitude;
}
}
public override string ToString()
{
return $"({x.ToString("F1")},{y.ToString("F1")})";
}
public override bool Equals(Object obj)
{
//Check for null and compare run-time types.
if ((obj == null) || !this.GetType().Equals(obj.GetType()))
{
return false;
}
else
{
V2 p = (V2)obj;
return (MathUtil.closeEnough(x, p.x, .001)) && (MathUtil.closeEnough(y, p.y, .001));
}
}
We look for the intersection between the two lines, if it exists:
public static V2 intersectSegment(V2 goalStart, V2 goalEnd, V2 ballStart, V2 ballEnd)
{
/* Equations of straight lines 2D: Qx + Ry + S = 0
*
* The objective is to determine the equations of the straight line of each segment,
* on the one hand the goal line and on the other hand the ball line.
*
* Then I determine if the two lines intersect at a point, or on the contrary
* if they never intersect, that is, they are parallel.
*
* If the two lines intersect at a point, I determine the value of that point (P)
*
* Finally, it is checked if this point is contained in each of the segments. *
*
*
* r1: Point A (x_A, y_A); Point B (x_B, y_B) --> AB = (x_B - x_A, y_B - y_A)
* r2: Point C (x_C, y_C), Point D (x_D, y_D) --> CD = (x_D - x_C, y_D - y_C)
*
* r1: (x - x_A)/x_AB = (y - y_A)/y_AB --> r1: Q1x + R1y + S1 = 0
* r2: (x - x_C)/x_CD = (y - y_C)/y_CD --> r2: Q2x + R2y + S2 = 0
*
* ** Q1 = y_AB ; R1 = -x_AB ; S1 = x_AB*y_A - y_AB*x_A
* ** Q2 = y_CD ; R2 = -x_CD ; S2 = x_CD*y_C - y_CD*x_C
*
* | Q1 R1 |
* determinant = | Q2 R2 | = Q1*R2 - Q2*R1
*
* ** if determinant == 0 -> is parallell
* ** if determinant != 0 -> is secant line
*
* Cut-off point (P):
*
* Q2*S1 - Q1*S2
* y_P = -------------
* Q1*R2 - Q2*R1
*
* S1*(Q2*R1 - Q1*R2) + R1*Q2*(Q1 - S1)
* x_P = ------------------------------------
* Q1^2*R2 - Q1*Q2*R1
*
*
* ** if P c in AB or CD -> Intersection true
*
*/
var goalVector = goalEnd - goalStart;
var ballVector = ballEnd - ballStart;
var Q1 = goalVector.y;
var Q2 = ballVector.y;
var R1 = goalVector.x;
var R2 = ballVector.x;
var S1 = goalVector.x * goalStart.y - goalVector.y * goalStart.x;
var S2 = ballVector.x * ballStart.y - ballVector.y * ballStart.x;
var determinant = Q1 * R2 - Q2 * R1;
if (determinant != 0)
{
var x_P = (S2 * R1 - R2 * S1) / (R2 * Q1 - Q2 * R1);
var y_P = (S2 * Q1 - Q2 * S1) / (R2 * Q1 - Q2 * R1);
var intersectPoint = new V2(x_P, y_P);
if (MathUtil.PointContentInAB(goalStart, goalEnd, intersectPoint ) &&
MathUtil.PointContentInAB(ballStart, ballEnd, intersectPoint))
{
return intersectPoint;
}
}
return null;
}
Then, We return true or false if there is such an intersection between the two lines:
public static bool findIfIntersectsInSegment(V2 goalStart, V2 goalEnd, V2 ballStart, V2 ballEnd)
{
return intersectSegment(goalStart, goalEnd, ballStart, ballEnd)!=null;
}
Finally, if the intersection exists, we look for the intersection point of the two lines:
public static bool PointContentInAB(V2 A, V2 B, V2 P)
{
/* Equations of straight lines 2D: Qx + Ry + S = 0
*
* Point A (x_A, y_A); Point B (x_B, y_B) --> AB = (x_B - x_A, y_B - y_A)
*
* r1: (x - x_A)/x_AB = (y - y_A)/y_AB --> r1: Q1x + R1y + S1 = 0
* Q1 = y_AB ; R1 = -x_AB ; S1 = x_AB*y_A - y_AB*x_A
*
* ** if P.x <= B.x && P.x >= A.x --> Point content in AB
*/
var AB = B - A;
var P_content_in_r1 = AB.y * P.x - AB.x * P.y + (AB.x * A.y - AB.y * A.x);
if (nearzero(P_content_in_r1))
// Point content in r1
{
if (AB.x > 0f)
{
if (P.x <= B.x && P.x >= A.x)
// Point content in r1 and AB
{
if (AB.y > 0f)
{
if (P.y <= B.y && P.y >= A.y)
{
return true;
}
}
else
{
if(P.y >= B.y && P.y <= A.y)
{
return true;
}
}
}
}
else
{
if (P.x >= B.x && P.x <= A.x)
{
// Point content in r1 and AB
if (AB.y > 0f)
{
if (P.y <= B.y && P.y >= A.y)
{
return true;
}
}
else
{
if (P.y >= B.y && P.y <= A.y)
{
return true;
}
}
}
}
}
return false;
}
I hope you find it useful, best regards.
I'm trying to make a self-learning snake game and encountered an issue that I'm trying to resolve for the last several hours. Here is the code where I move the snake head:
public void moveTo()
{
int foodX = food.PosX / food.Width;
int foodY = food.PosY / food.Height;
Direction directionOfMaxForCurrentState= new Direction();
int currentX = head.PosX / head.Width;
int currentY = head.PosY / head.Height;
do
{
int tmpX = currentX;
int tmpY = currentY;
previousX = tmpX;
previousY = tmpY;
directionOfMaxForCurrentState = HighestQactionForState(currentX, currentY, previousX, previousY);
if (directionOfMaxForCurrentState == Direction.Up) { head.PosY -= head.Height; }
if (directionOfMaxForCurrentState == Direction.Down) { head.PosY += head.Height; }
if (directionOfMaxForCurrentState == Direction.Left) { head.PosX -= head.Width; }
if (directionOfMaxForCurrentState == Direction.Right) {head.PosX += head.Width; }
currentX = head.PosX / head.Width;
currentY = head.PosY / head.Height;
if (currentX == foodX && currentY == foodY) { snake.Clear(); head = new Cell(1, 1); snake.Add(head); }
} while (head.PosX == food.PosX && head.PosY == food.PosY);
}
And here is HighestQactionForState function:
public Direction HighestQactionForState(int x, int y, int px, int py)
{
var Qaround = new List<decimal>();
var actionsWithMax = new List<Direction>();
Direction toExclude = new Direction();
toExclude = directionToExclude(x, y, px, py);
foreach (Direction action in PosibleActionsForState(x, y).Where(a => a != toExclude).ToList())
{
if (action == Direction.Up && (double)Math.Abs(Q[Tuple.Create(x, y - 1, action)] - MaxQaroundState(x, y)) < 0.000000000000000000000001) actionsWithMax.Add(action);
if (action == Direction.Down && (double)Math.Abs(Q[Tuple.Create(x, y + 1, action)] - MaxQaroundState(x, y)) < 0.000000000000000000000001) actionsWithMax.Add(action);
if (action == Direction.Left && (double)Math.Abs(Q[Tuple.Create(x - 1, y, action)] - MaxQaroundState(x, y)) < 0.000000000000000000000001) actionsWithMax.Add(action);
if (action == Direction.Right && (double)Math.Abs(Q[Tuple.Create(x + 1, y, action)] - MaxQaroundState(x, y)) < 0.000000000000000000000001) actionsWithMax.Add(action);
}
return actionsWithMax.ElementAt(rnd.Next(actionsWithMax.Count));
}
So now when I put the breakpoint in the moveTo function I see that HighestQactionForState gets the right parameters, but in the HighestQactionForState function the parameters are not correct - px always has the same value as int x, and py is the same as y which causing the program to behave wrong. Any help would be appreciated!
If im not mistaken you should do this instead
directionOfMaxForCurrentState = HighestQactionForState(currentX, currentY, previousX, previousY);
previousX = currentX;
previousY = currentY;
You set previous values after you call method so by the meaning of variables they have values set in previous iteration.
Also there is no need for declaring extra temp variables. Your parameters are passed by value not by reference.
The Program Works for arrays upto 20x20 But for anything larger it throws an OutOfMemoryException.
Below is the code:
public static Point GetFinalPath(int x, int y) {
queue.Enqueue(new Point(x,y, null));
while(queue.Count>0) {
Point p = queue.Dequeue();
if (arr[p.x,p.y] == 9) {
Console.WriteLine("Found Destination");
return p;
}
if(IsOpen(p.x+1,p.y)) {
arr[p.x,p.y] = 1;
queue.Enqueue(new Point(p.x+1,p.y, p));
}
//similarly for the other directions
}
return null;
}
public int[,] SolutionMaze()
{
Point p = GetFinalPath(0, 0);
while (p.getParent() != null)
{
solvedarray[p.x, p.y] = 9;
p = p.getParent();
}
return solvedarray;
}
ok people here is the rest of the code
public static Queue<Point> queue=new Queue<Point>();
public static bool IsOpen(int x, int y)
{
//BOUND CHECKING
if ((x >= 0 && x < XMAX) && (y >= 0 && y < YMAX) && (arr[x,y] == 0 || arr[x,y] == 9))
{
return true;
}
return false;
}
public class Point
{
public int x;
public int y;
Point parent;
public Point(int x, int y, Point parent)
{
this.x = x;
this.y = y;
this.parent = parent;
}
public Point getParent()
{
return this.parent;
}
}
Assumes start to be 0,0 and final destination is set as 9 at the constructor.
Help me implement this for an array of size 500x500
Well, looking at your code I found one problem. You perform the wrong check. You should check if your point is already added to a queue. What do you do now? We'll, you are just marking processed cell as not open. It's easy to see that you can add to queue same node twice.
Let's follow my example:
1 | . .
0 | ! .
--+----
yx| 0 1
Queue: point (0,0)
We are starting at point(0,0). At this moment, we are adding points (0, 1) and (1,0) to our queue and mark point(0,0) as processed
1 | . .
0 | X !
--+----
yx| 0 1
Queue: point (0,1), point (1,0)
Now we dequeue point(0,1), marking it processed and adding point(1,1) to queue.
1 | ! .
0 | X X
--+----
yx| 0 1
Queue: point (1,0), point(1,1)
Now we dequeue point(1,0), marking it as processed and adding point(1,1) to queue:
1 | X !
0 | X X
--+----
yx| 0 1
Queue: point (1,1), point (1,1)
And now we have same point twice in a queue. And that is not your last problem. Your points have a reference to all it parents, so your previous points (doubled too) can't be processed with Garbage Collector.
Okay i found an answer to the OutOfMemory. Now the code works even for 500x500 matrix
As it turns out i just implemented a node list which keeps track of added nodes in queue using y*MAX_X_LENGTH + x formula
public static Queue<Point> queue=new Queue<Point>();
public SolveMaze(int[,] array,int staX,int staY,int finX,int finY)
{
//sets Destination as 9
arr = array;
XMAX = array.GetLength(0);
YMAX = array.GetLength(1);
finishY = finX; finishX = finY; startY = staX; startX = staY;
solvedarray = new int[XMAX, YMAX];
}
public static List<int> nodelist=new List<int>();
public void AddPointToQueueIfOpenAndNotAlreadyPresent(Point p,int direction)
{
if (nodelist.Contains(XMAX * p.y + p.x))
return;
else
{
switch(direction){
case 1:
//north
if (IsOpen(p.x, p.y - 1))
{
arr[p.x, p.y] = 1;
queue.Enqueue(new Point(p.x, p.y - 1, p));
nodelist.Add(XMAX * (p.y - 1) + p.x);
}
break;
case 0:
//east
if (IsOpen(p.x + 1, p.y))
{
arr[p.x, p.y] = 1;
queue.Enqueue(new Point(p.x + 1, p.y, p));
nodelist.Add(XMAX * (p.y) + p.x + 1);
}
break;
case 3:
//south
if (IsOpen(p.x, p.y + 1))
{
arr[p.x, p.y] = 1;
queue.Enqueue(new Point(p.x, p.y +1, p));
nodelist.Add(XMAX * (p.y + 1) + p.x);
}
break;
case 2:
//west
if (IsOpen(p.x - 1, p.y))
{
arr[p.x, p.y] = 1;
queue.Enqueue(new Point(p.x - 1, p.y, p));
nodelist.Add(XMAX * (p.y) + p.x-1);
}
break; }
}
}
public Point GetFinalPath(int x, int y) {
if (arr[finishX, finishY] == 0)
arr[finishX, finishY] = 9;
else
return null;
queue.Enqueue(new Point(x, y, null));
nodelist.Add(XMAX * y + x);
while(queue.Count>0) {
Point p = queue.Dequeue();
nodelist.Remove(p.y * XMAX + p.x);
if (arr[p.x,p.y] == 9) {
Console.WriteLine("Exit is reached!");
return p;
}
for (int i = 0; i < 4; i++)
{
AddPointToQueueIfOpenAndNotAlreadyPresent(p, i);
}
}
return null;
}
public static bool IsOpen(int x, int y)
{
//BOUND CHECKING
if ((x >= 0 && x < XMAX) && (y >= 0 && y < YMAX) && (arr[x,y] == 0 || arr[x,y] == 9))
{
return true;
}
return false;
}
public int[,] SolutionMaze()
{
Point p = GetFinalPath(startX, startY);
if(p!=null)
while (p.getParent() != null)
{
solvedarray[p.x, p.y] = 9;
p = p.getParent();
}
return solvedarray;
}
}
public class Point
{
public int x;
public int y;
Point parent;
public Point(int x, int y, Point parent)
{
this.x = x;
this.y = y;
this.parent = parent;
}
public Point getParent()
{
return this.parent;
}
}