public override IEnumerator Search(List <Node> nodes, int size, Node start, Node goal, int framesPerSecond) { IsSearching = true; Debug.LogFormat("Searching path using A* algorythm"); bool hasPath = false; bool[,] visited = new bool[size, size]; cameFrom = new Dictionary <Node, Node> (); List <NodeDistance> frontier = new List <NodeDistance> (); Node current = new Node(); float pathLength; frontier.Add(new NodeDistance(start, 0 + Mathf.Abs(Vector2.Distance(start.pos, goal.pos)))); cameFrom[start] = null; visited[(int)start.pos.x, (int)start.pos.y] = true; while (frontier.Count > 0) { current = frontier[0].node; pathLength = frontier[0].distance - Mathf.Abs(Vector2.Distance(current.pos, goal.pos)); frontier.RemoveAt(0); UIUpdate(frontier.Count, pathLength, Mathf.Abs(Vector2.Distance(current.pos, goal.pos)) + pathLength); //visualize path to current GraphSearcher.VisualizePath(cameFrom, current, start); yield return(new WaitForSeconds(1f / framesPerSecond)); if (current == goal) { hasPath = true; break; } //get the maxNodes nodes closest to the objective and keep them. for (int i = 0; i < current.links.Count; i++) { Node neighbour = current.links[i]; if (!visited[(int)neighbour.pos.x, (int)neighbour.pos.y]) { visited[(int)neighbour.pos.x, (int)neighbour.pos.y] = true; cameFrom[neighbour] = current; float totalDistance = pathLength + Mathf.Abs(Vector2.Distance(neighbour.pos, current.pos)) + Mathf.Abs(Vector2.Distance(neighbour.pos, goal.pos)); frontier.Add(new NodeDistance(neighbour, totalDistance)); UIIncrementEnqueuings(); } frontier.Sort(); } } SearchComplete(hasPath); yield break; }
public override IEnumerator Search(List <Node> nodes, int size, Node start, Node goal, int framesPerSecond) { IsSearching = true; Debug.Log("Searching path using Breadth First Search algorythm."); bool hasPath = false; bool[,] visited = new bool[size, size]; cameFrom = new Dictionary <Node, Node> (); Queue <Node> frontier = new Queue <Node> (); Node current = new Node(); frontier.Enqueue(start); cameFrom[start] = null; visited[(int)start.pos.x, (int)start.pos.y] = true; while (frontier.Count > 0) { current = frontier.Dequeue(); UIUpdate(frontier.Count, CalculatePathLength(start, current)); //visualize path to current GraphSearcher.VisualizePath(cameFrom, current, start); yield return(new WaitForSeconds(1f / framesPerSecond)); if (current == goal) { hasPath = true; break; } for (int i = 0; i < current.links.Count; i++) { Node neighbour = current.links[i]; if (neighbour == current) { Debug.LogErrorFormat("node {0} has link to itself.", current); } if (!visited[(int)neighbour.pos.x, (int)neighbour.pos.y]) { visited[(int)neighbour.pos.x, (int)neighbour.pos.y] = true; cameFrom[neighbour] = current; frontier.Enqueue(neighbour); UIIncrementEnqueuings(); } } } SearchComplete(hasPath); yield break; }
private void Awake() { if (instance == null) { instance = FindObjectOfType <GraphSearcher> (); } if (instance != this) { Destroy(instance.gameObject); } }
public void MemberWithConverterType_IsColumnMember() { var config = new ConversionConfiguration(); config.AddConvertor(new ValueConverter <Id <SingleProperty>, int>(model => model.Value, provider => (Id <SingleProperty>)provider)); var searcher = new GraphSearcher(config); var graph = searcher.SearchGraph(typeof(SingleProperty)); graph.Nodes.Should().ContainSingle() .Which.ColumnMembers.Should().ContainSingle(x => x.Name == nameof(SingleProperty.Id) && ((PropertyInfo)x).PropertyType == typeof(Id <SingleProperty>)); }
private void ClearPreviousMap() { GraphSearcher.ResetAllPaths(); foreach (Transform child in transform) { GameObject.Destroy(child.gameObject); } Node.ResetIDs(); }
public void PrimitiveTypeAsRoot_WillBeEmptyGraph() { var primitiveType = typeof(int); var config = new ConversionConfiguration { IsAllowedForColumn = x => x == primitiveType, }; var searcher = new GraphSearcher(config); var graph = searcher.SearchGraph(primitiveType); graph.Nodes.Should().BeEmpty(); }
public void CyclicGraphs_DontCauseEndlessRecursion() { var primitiveType = typeof(int); var config = new ConversionConfiguration { IsAllowedForColumn = x => false, ShouldMediateTargetProperty = x => true, }; var searcher = new GraphSearcher(config); var graph = searcher.SearchGraph(typeof(CycleLink1)); graph.Nodes.Select(_ => _.Type).Should().Contain(new[] { typeof(CycleLink1), typeof(CycleLink2) }); }
public void TryingToSearchMoreThanMaxLevelDeep_ThrowsSanityException() { var primitiveType = typeof(int); var config = new ConversionConfiguration { IsAllowedForColumn = x => false, ShouldMediateTargetProperty = x => true, MaxRecursion = 0, }; var searcher = new GraphSearcher(config); searcher.Invoking(x => x.SearchGraph(typeof(Outer))) .Should().Throw <SanityException>().WithMessage("*recursion limit*"); }
internal void AddGoalNodes(GraphSearchData gsData, Level level, Entity exceptThisGoal, HashSet <Goal> solvedGoals) { foreach (var node in Nodes) { if (node is BoxConflictNode boxNode) { level.AddWall(boxNode.Value.Ent.Pos); } } var goalCondition = new Func <(Point pos, int distance), GraphSearcher.GoalFound <(Point pos, int distance)> >(x => { return(new GraphSearcher.GoalFound <(Point, int)>(x, PositionHasNode(x.pos))); }); foreach (var goal in level.Goals) { if (solvedGoals.Contains(goal)) { continue; } if (goal.Ent == exceptThisGoal) { continue; } BoxConflictNode node = new BoxConflictNode(new EntityNodeInfo(goal.Ent, goal.EntType)); List <(Point pos, int distance)> edges = GraphSearcher.GetReachedGoalsBFS(gsData, level, goal.Ent.Pos, goalCondition); foreach (var edge in edges.Distinct()) { if (GetNodeFromPosition(edge.pos) is BoxConflictNode boxEnd) { node.AddEdge(new Edge <DistanceEdgeInfo>(boxEnd, new DistanceEdgeInfo(edge.distance))); boxEnd.AddEdge(new Edge <DistanceEdgeInfo>(node, new DistanceEdgeInfo(edge.distance))); } else if (GetNodeFromPosition(edge.pos) is FreeSpaceNode freeEnd) { node.AddEdge(new Edge <DistanceEdgeInfo>(freeEnd, new DistanceEdgeInfo())); freeEnd.AddEdge(new Edge <DistanceEdgeInfo>(node, new DistanceEdgeInfo())); } } Nodes.Add(node); } level.ResetWalls(); }
public void SelectWithMultiplePropertyChain_IsProperlyMaterialized() { using (var connection = new SQLiteConnection("data source=:memory:")) { connection.Open(); using (var ctx = new OrderContext(connection)) { var order = new Order { Items = new List <OrderItem> { new OrderItem { Product = new Product { Name = "Diapers", Cost = 10.0m }, Quantity = 5, }, new OrderItem { Product = new Product { Name = "Baby formula", Cost = 50m }, Quantity = 3, }, }, }; ctx.Orders.Add(order); ctx.SaveChanges(); var config = new ConversionConfiguration() { IsAllowedForColumn = x => x.IsValueType || x == typeof(string), }; var searcher = new GraphSearcher(config); var graph = searcher.SearchGraph(typeof(B)); var mediatorMapper = new MediatorTypeBuilder().CreateMediatorTypes(graph); var result = ctx.OrderItems.ProjectToList( x => new B { ProductCost = x.Product.Cost, }, mediatorMapper); result.Select(x => x.ProductCost).Should().BeEquivalentTo(10m, 50m); } } }
public void PropertiesMaterializedFromDbColumnAreAlwaysAllowed() { var originType = typeof(Outer); var primitiveType = new Outer().Primitive.GetType(); var config = new ConversionConfiguration { IsAllowedForColumn = x => x == primitiveType, ShouldMediateTargetProperty = x => false, }; var searcher = new GraphSearcher(config); var graph = searcher.SearchGraph(originType); graph.Nodes.Should().ContainSingle() .Which.Type.Should().Be(originType); graph.Nodes.Single().ColumnMembers .Should().Equal(typeof(Outer).GetProperty(nameof(Outer.Primitive))); }
private void AssertTargetToMediator <TParameter, TResult>( Expression <Func <TParameter, TResult> > projection, string resultTargetToMediator) { var config = new ConversionConfiguration() { IsAllowedForColumn = x => x.IsValueType || x == typeof(string), }; var searcher = new GraphSearcher(config); var graph = searcher.SearchGraph(typeof(TResult)); var mediatorMapper = new MediatorTypeBuilder().CreateMediatorTypes(graph); var targetToMediatorVisitor = new TargetToMediatorVisitor(mediatorMapper); var result = targetToMediatorVisitor.Visit(projection); result.ToString("C#").Should().Be(resultTargetToMediator.Trim()); }
public void GraphHasMultipleLevels_AllAreFound() { var originType = typeof(Outer); var config = new ConversionConfiguration { IsAllowedForColumn = x => x.IsPrimitive, ShouldMediateTargetProperty = x => true, }; var searcher = new GraphSearcher(config); var graph = searcher.SearchGraph(originType); graph.Nodes.Select(x => x.Type).Should().BeEquivalentTo(typeof(Outer), typeof(Inner), typeof(Innermost)); var outerNode = graph.Nodes.Single(x => x.Type == typeof(Outer)); var innerNode = graph.Nodes.Single(x => x.Type == typeof(Inner)); var innermostNode = graph.Nodes.Single(x => x.Type == typeof(Innermost)); graph.Edges.Should().BeEquivalentTo( new Edge(outerNode, innerNode, typeof(Outer).GetProperty(nameof(Outer.Inner))), new Edge(innerNode, innermostNode, typeof(Inner).GetProperty(nameof(Inner.Innermost)))); }
// A bit of explanation on the convert method // It is using a simple dijkstra algorithm to find the shortest path // The set of conversion rates being represented as a graph // It is using decimals for representing the numbers, which limits funny behaviors regarding float/double precision // Finally, it's converting the rates into integer so that it makes it much easier to round to the 4th decimal // You just need to always keep in mind to divide by 10 000 when necessary // But rounding to the nearest integer gives you automatically the proper adjustment (in particular for the inverse of rates) private static string convert(string toConvert, List<string> rates) { string[] splitConvertTo = toConvert.Split(SEPARATOR); if(splitConvertTo.Length != 3) { return "Invalid line describing the conversion to be done"; } string fromCurrency = splitConvertTo[FROM_CURRENCY_POSITION]; decimal originalAmount = 0; // Code is running on a French server, so numbers with . were probably creating a problem since, my guess is that it was using a French CultureInfo if (!decimal.TryParse(splitConvertTo[ORIGINAL_AMOUNT_POSITION], System.Globalization.NumberStyles.Any, System.Globalization.CultureInfo.InvariantCulture.NumberFormat, out originalAmount)) { return "Invalid line describing the conversion to be done"; } string toCurrency = splitConvertTo[TO_CURRENCY_POSITION]; GraphSearcher helper = new GraphSearcher(); foreach (string rateDescription in rates) { string[] splitRateDescription = rateDescription.Split(SEPARATOR); string fromRateCurrency = splitRateDescription[RATE_FROM_CURRENCY_POSITION]; string toRateCurrency = splitRateDescription[RATE_TO_CURRENCY_POSITION]; string rateAsString = splitRateDescription[RATE_POSITION]; // Code is running on a French server, so numbers with . were probably creating a problem since, my guess is that it was using a French CultureInfo decimal rateAsDouble = decimal.Parse(rateAsString, System.Globalization.CultureInfo.InvariantCulture.NumberFormat); decimal rate = decimal.Round(rateAsDouble * PRECISION); //We make sure our inverse rate is rounded to the 4th decimal decimal inverseRate = decimal.Round((PRECISION / rateAsDouble)); helper.addEdge(fromRateCurrency, toRateCurrency, rate); helper.addEdge(toRateCurrency, fromRateCurrency, inverseRate); } decimal convertedValue = originalAmount; foreach (decimal rate in helper.getShortestPath(fromCurrency, toCurrency)) { convertedValue = convertedValue * (rate / PRECISION); } convertedValue = decimal.Round(convertedValue); return string.Format("{0:0}",convertedValue); }
public Maze(FPseudoRandom random, FVec3 scale, FVec3 offset, int row, int col, int startIndex, int endIndex, FVec2 startPointPlace) { this.startPointPlace = startPointPlace; if (this.row < ( int )this.startPointPlace.y + 3 || this.col < ( int )this.startPointPlace.x + 3) { throw new Exception("The row or col invaild"); } this.scale = scale; this.offset = offset; this.row = row; this.col = col; this._localToWorld = FMat4.FromTRS(this.offset, FQuat.identity, this.scale); this._worldToLocal = FMat4.NonhomogeneousInverse(this._localToWorld); //创建二维图 Graph2D graph2 = this.CreateFullDigraph(random); //创建格子 this.CreateTiles(graph2); //是否随机起点和终点 this.startIndex = startIndex < 0 ? this.RandomIndexWithinBorder(random) : startIndex; if (endIndex < 0) { int[] candidate = new int[5]; for (int i = 0; i < 5; i++) { candidate[i] = this.RandomIndexWithinBorder(random); } endIndex = this.GetFarthestToStartIndex(this.startIndex, candidate); } this.endIndex = endIndex; //初始化起点范围 HashSet <int> walkablesHs = new HashSet <int>(); this.SetStart(graph2, this.startIndex, walkablesHs); //创建起点和终点 int[] coord = graph2.IndexToCoord(this.startIndex); Delaunay.DVertex startVertex = new Delaunay.DVertex(coord[0], coord[1]); coord = graph2.IndexToCoord(this.endIndex); Delaunay.DVertex endVertex = new Delaunay.DVertex(coord[0], coord[1]); //创建外圆周附近的顶点 List <Delaunay.DVertex> vertices1 = this.GenVertices(24, 1f, 0.8f, 24, random); //创建内圆周附近的顶点 List <Delaunay.DVertex> vertices2 = this.GenVertices(12, 0.2f, 0.6f, 12, random); //合并所有顶点 List <Delaunay.DVertex> vertices = new List <Delaunay.DVertex> { startVertex, endVertex }; vertices.AddRange(vertices1); vertices.AddRange(vertices2); //点集合的三角化 List <Delaunay.DTriangle> triangles = Delaunay.Triangulate(vertices); //从三角形集合创建图 GraphBase graph = Tools.TrianglesToGraph(triangles, random.NextFloat); //Prim算法创建最小生成树 List <GraphEdge> edges = GraphSearcher.PrimSearch(graph, triangles[0].v0); //每条边用A*算法生成路径 int count = edges.Count; for (int i = 0; i < count; i++) { GraphEdge edge = edges[i]; Delaunay.DVertex v0 = vertices[edge.from]; Delaunay.DVertex v1 = vertices[edge.to]; int[] path = GraphSearcher.AStarSearch(graph2, graph2.CoordToIndex(( int )v0.x, ( int )v0.y), graph2.CoordToIndex(( int )v1.x, ( int )v1.y)); this.SetPathWalkable(path, walkablesHs); //把顶点扩展成房间 if (i == 0) { this.SetIndexToWalkable(graph2, ( int )v0.x, ( int )v0.y, random.Next(1, 3), random.Next(1, 3), walkablesHs); } this.SetIndexToWalkable(graph2, ( int )v1.x, ( int )v1.y, random.Next(1, 3), random.Next(1, 3), walkablesHs); } this.walkables = walkablesHs.ToArray(); }
public override IEnumerator Search(List <Node> nodes, int size, Node start, Node goal, int framesPerSecond) { IsSearching = true; Debug.Log("Searching path using Hill Climbing algorythm."); bool hasPath = false; bool[,] visited = new bool[size, size]; cameFrom = new Dictionary <Node, Node> (); Stack <Node> frontier = new Stack <Node> (); Node current = new Node(); frontier.Push(start); cameFrom[start] = null; while (frontier.Count > 0) { current = frontier.Pop(); UIUpdate(frontier.Count, CalculatePathLength(start, current)); //visualize path to current GraphSearcher.VisualizePath(cameFrom, current, start); yield return(new WaitForSeconds(1f / framesPerSecond)); if (current == goal) { hasPath = true; break; } float closestDistance = Vector2.Distance(current.pos, goal.pos); Node closestNeighbour = null; for (int i = 0; i < current.links.Count; i++) { Node neighbour = current.links[i]; // test shortest path float neighbourDist = Vector2.Distance(neighbour.pos, goal.pos); if (neighbourDist < closestDistance || closestDistance == float.MaxValue) { closestNeighbour = neighbour; closestDistance = neighbourDist; } } // extend path if (closestNeighbour != null) { if (!visited[(int)closestNeighbour.pos.x, (int)closestNeighbour.pos.y]) { visited[(int)closestNeighbour.pos.x, (int)closestNeighbour.pos.y] = true; cameFrom[closestNeighbour] = current; frontier.Push(closestNeighbour); UIIncrementEnqueuings(); } } else { Debug.LogFormat("Stuck in local maxima. node {0}", current.ID); } } SearchComplete(hasPath); yield break; }
internal void AddFreeSpaceNodes(GraphSearchData gsData, Level level) { // //All entities need to be made into walls so the only freepsace is space //that won't block the path or be on top of other entities. // HashSet <Point> agentPositions = new HashSet <Point>(); foreach (var inode in Nodes) { if (inode is BoxConflictNode boxNode) { level.Walls[boxNode.Value.Ent.Pos.X, boxNode.Value.Ent.Pos.Y] = true; if (boxNode.Value.EntType == EntityType.AGENT) { agentPositions.Add(boxNode.Value.Ent.Pos); } } } // //Now go through the map and and find all empty spaces. When an empty space is found, bfs is used to //find all connecting spaces which makes up the free space node. // var foundFreeSpace = new Func <(Point pos, int distance), GraphSearcher.GoalFound <Point> >(x => { return(new GraphSearcher.GoalFound <Point>(x.pos, !level.IsWall(x.pos))); }); HashSet <Point> alreadySeenSpaces = new HashSet <Point>(); List <FreeSpaceNode> freeSpaceNodes = new List <FreeSpaceNode>(); for (int y = 0; y < level.Height; y++) { for (int x = 0; x < level.Width; x++) { if (!level.Walls[x, y] && !alreadySeenSpaces.Contains(new Point(x, y))) { //There is an issue here. //The list has a duplicate in it. //It's currently handled by inserting it //into a hashset. var freeSpacesFound = GraphSearcher.GetReachedGoalsBFS(gsData, level, new Point(x, y), foundFreeSpace); //A single free space can't be part of multiple nodes alreadySeenSpaces.UnionWith(freeSpacesFound); //Create node and add it to the graph. //keep a list of the freespace nodes so edges can be added later var newFreeSpaceNode = new FreeSpaceNode(new FreeSpaceNodeInfo(freeSpacesFound)); AddNode(newFreeSpaceNode); freeSpaceNodes.Add(newFreeSpaceNode); } } } // //Now it's time to add edges between the free spaces nodes and the rest of the graph. //To do that the original map has to be restored as the path may block pathways from //a freespace to any other node on the map. Agents and boxes still have to be walls //as there is no direct path through them. This excludes goals as only goals with //boxes on them should be walls and the boxes on top of them will makes them walls. //Go through all free space nodes and use bfs to check which other nodes it can reach, //then add edges to those nodes. // level.ResetWalls(); foreach (var inode in Nodes) { if (inode is BoxConflictNode boxNode && !boxNode.Value.EntType.IsGoal()) { level.Walls[boxNode.Value.Ent.Pos.X, boxNode.Value.Ent.Pos.Y] = true; } } var foundNode = new Func <(Point pos, int distance), GraphSearcher.GoalFound <INode> >(x => { if (PositionToNode.TryGetValue(x.pos, out INode node)) { return(new GraphSearcher.GoalFound <INode>(node, true)); } return(new GraphSearcher.GoalFound <INode>(null, false)); }); foreach (var freeSpaceNode in freeSpaceNodes) { var nodesFound = GraphSearcher.GetReachedGoalsBFS(gsData, level, freeSpaceNode.Value.FreeSpaces.First(), foundNode); foreach (var neighbour in nodesFound.ToHashSet()) { //The search may find itself and such edges are not necessary if (neighbour != freeSpaceNode) { //Bidirectional edges freeSpaceNode.AddEdge(new Edge <DistanceEdgeInfo>(neighbour, new DistanceEdgeInfo())); if (neighbour is BoxConflictNode boxNode) { boxNode.AddEdge(new Edge <DistanceEdgeInfo>(freeSpaceNode, new DistanceEdgeInfo())); } else if (neighbour is FreeSpaceNode freeNode) { freeNode.AddEdge(new Edge <DistanceEdgeInfo>(freeSpaceNode, new DistanceEdgeInfo())); } } } } level.ResetWalls(); foreach (var agentPos in agentPositions) { foreach (var dirDelta in Direction.NONE.DirectionDeltas()) { Point nextToAgent = agentPos + dirDelta; if (PositionHasNode(nextToAgent)) { INode node = GetNodeFromPosition(nextToAgent); if (node is FreeSpaceNode freeSpaceNode) { freeSpaceNode.Value.FreeSpaces.Add(agentPos); continue; } } } } }