From d1d75c69020a761e38d51f5c0b5ba0e6f9c51ba0 Mon Sep 17 00:00:00 2001 From: ampm Date: Sun, 5 May 2019 20:16:10 +0800 Subject: [PATCH 01/23] bug: reproduce the findPath error. --- DemoApp/AppDelegate.swift | 2 +- DemoApp/MasterViewController.swift | 2 +- Sources/MMNavigator.swift | 12 ++++++ UITests/DemoMappaMundi.swift | 59 ++++++++++++++++++++++++++++-- UITests/DemoUITests.swift | 12 ++++++ 5 files changed, 82 insertions(+), 5 deletions(-) diff --git a/DemoApp/AppDelegate.swift b/DemoApp/AppDelegate.swift index 5b63a08..5eb3449 100644 --- a/DemoApp/AppDelegate.swift +++ b/DemoApp/AppDelegate.swift @@ -10,7 +10,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UISplitViewControllerDele var window: UIWindow? - func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { // Override point for customization after application launch. let splitViewController = window!.rootViewController as! UISplitViewController let navigationController = splitViewController.viewControllers[splitViewController.viewControllers.count-1] as! UINavigationController diff --git a/DemoApp/MasterViewController.swift b/DemoApp/MasterViewController.swift index c83d8b9..4ed2ea2 100644 --- a/DemoApp/MasterViewController.swift +++ b/DemoApp/MasterViewController.swift @@ -91,7 +91,7 @@ class MasterViewController: UITableViewController { return true } - override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) { + override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) { if editingStyle == .delete { objects.remove(at: indexPath.row) tableView.deleteRows(at: [indexPath], with: .fade) diff --git a/Sources/MMNavigator.swift b/Sources/MMNavigator.swift index 6d57724..ac3c823 100644 --- a/Sources/MMNavigator.swift +++ b/Sources/MMNavigator.swift @@ -133,6 +133,18 @@ open class MMNavigator { } var gkPath = map.gkGraph.findPath(from: gkSrc, to: gkDest) + + //for reproducing the findpath error. + if nodeName == "foo_5" { + print("gkPath.count---\(gkPath.count)") + gkPath.forEach { (node) in + print("------------------->\(String(describing: map.nodedScenes[node]?.name))") + } + if gkPath.count != 3 { + fatalError("find path error!") + } + } + guard gkPath.count > 0 else { xcTest.recordFailure(withDescription: "Cannot route from \(currentGraphNode.name) to \(nodeName)", inFile: file, atLine: Int(line), expected: false) return diff --git a/UITests/DemoMappaMundi.swift b/UITests/DemoMappaMundi.swift index 0a1b6eb..79fb493 100644 --- a/UITests/DemoMappaMundi.swift +++ b/UITests/DemoMappaMundi.swift @@ -13,14 +13,31 @@ class Actions { static let deleteAllItems = "deleteAllItems" static let initialWithExactlyOne = "initialWithExactlyOne" static let postAddItem = "postAddItem" + static let action1 = "item_action_1" + static let action2 = "item_action_2" + static let action3 = "item_action_3" + static let action4 = "item_action_4" + static let action5 = "item_action_5" + static let action6 = "item_action_6" + static let action7 = "item_action_7" + static let action8 = "item_action_8" + static let action9 = "item_action_9" + static let action10 = "item_action_10" + static let action11 = "item_action_11" + static let action12 = "item_action_12" } class Screens { static let itemList = "ItemList" static let itemListEditing = "EditingItemList" static let itemDetail = "ItemDetail" + static let foo1 = "foo_1" + static let foo2 = "foo_2" + static let foo3 = "foo_3" + static let foo4 = "foo_4" + static let foo5 = "foo_5" } - +@objcMembers class DemoAppUserState: MMUserState { required init() { super.init() @@ -70,8 +87,44 @@ func createGraph(with app: XCUIApplication, for test: XCTestCase) -> MMScreenGra // and a back button. map.addScreenState(Screens.itemDetail) { screenState in screenState.backAction = navigationControllerBackAction + + screenState.gesture(forAction: Actions.action1,transitionTo: Screens.foo5) { _ in + } + screenState.gesture(forAction: Actions.action2,transitionTo: Screens.foo1) { _ in + } + screenState.gesture(forAction: Actions.action3,transitionTo: Screens.foo2) { _ in + } + screenState.gesture(forAction: Actions.action4,transitionTo: Screens.foo2) { _ in + } + screenState.gesture(forAction: Actions.action5,transitionTo: Screens.foo4) { _ in + } } - + + map.addScreenState(Screens.foo1) { screenState in + screenState.gesture(forAction: Actions.action6,transitionTo: Screens.foo5) { _ in + } + screenState.gesture(forAction: Actions.action7,transitionTo: Screens.foo3) { _ in + } + } + map.addScreenState(Screens.foo2) { screenState in + screenState.gesture(forAction: Actions.action8,transitionTo: Screens.foo5) { _ in + } + screenState.gesture(forAction: Actions.action9,transitionTo: Screens.foo4) { _ in + } + } + map.addScreenState(Screens.foo3) { screenState in + screenState.gesture(forAction: Actions.action10,transitionTo: Screens.foo5) { _ in + } + } + + map.addScreenState(Screens.foo4) { screenState in + screenState.gesture(forAction: Actions.action11,transitionTo: Screens.foo5) { _ in + } + } + + map.addScreenState(Screens.foo5) { screenState in + } + // The edit screen has a delete action. We can also map.addScreenState(Screens.itemListEditing) { screenState in let table = app.tables.element(boundBy: 0) @@ -104,6 +157,6 @@ func createGraph(with app: XCUIApplication, for test: XCTestCase) -> MMScreenGra map.addNavigatorAction(Actions.postAddItem) { navigator in print("In \(navigator.screenState)") } - + return map } diff --git a/UITests/DemoUITests.swift b/UITests/DemoUITests.swift index 4669713..1f730f7 100644 --- a/UITests/DemoUITests.swift +++ b/UITests/DemoUITests.swift @@ -34,6 +34,7 @@ class DemoUITests: XCTestCase { MMTestUtils.render(graph: createGraph(with: app, for: self)) } + func testSimpleNavigation() { navigator.performAction(Actions.addItem) navigator.goto(Screens.itemDetail) @@ -119,4 +120,15 @@ class DemoUITests: XCTestCase { navigator.performAction(Actions.postAddItem) XCTAssertEqual(2, userState.numItems) } + override func invokeTest() { + for time in 0...10 { + print("this test is invoking: \(time) times") + super.invokeTest() + } + } + func testReproduceFindpathError() { + navigator.performAction(Actions.addItem) + navigator.goto(Screens.itemDetail) + navigator.goto(Screens.foo5) + } } From 34c76311650748bd4c56327ae237d5c04dec470e Mon Sep 17 00:00:00 2001 From: ampm Date: Sun, 5 May 2019 21:20:49 +0800 Subject: [PATCH 02/23] reproduce the path finding error --- MappaMundi.xcodeproj/project.pbxproj | 2 + Pathfinding.playground/Contents.swift | 87 ++++++++++++++++++++ Pathfinding.playground/contents.xcplayground | 4 + Sources/MMNavigator.swift | 3 - 4 files changed, 93 insertions(+), 3 deletions(-) create mode 100644 Pathfinding.playground/Contents.swift create mode 100644 Pathfinding.playground/contents.xcplayground diff --git a/MappaMundi.xcodeproj/project.pbxproj b/MappaMundi.xcodeproj/project.pbxproj index d7971ca..0b2d312 100644 --- a/MappaMundi.xcodeproj/project.pbxproj +++ b/MappaMundi.xcodeproj/project.pbxproj @@ -52,6 +52,7 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 13FB93F1227F0D5100BAEAFA /* Pathfinding.playground */ = {isa = PBXFileReference; lastKnownFileType = file.playground; path = Pathfinding.playground; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; 392FB352200E1D5D0014867E /* MMTestUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MMTestUtils.swift; sourceTree = ""; }; 3962F2281FF6A6C100999008 /* Wait.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = Wait.swift; path = ../Sources/Wait.swift; sourceTree = ""; }; 3962F22A1FF6A75800999008 /* ScreenGraphEdge.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = ScreenGraphEdge.swift; path = ../Sources/ScreenGraphEdge.swift; sourceTree = ""; }; @@ -136,6 +137,7 @@ 39BAD5521FE19B9D00524FB7 = { isa = PBXGroup; children = ( + 13FB93F1227F0D5100BAEAFA /* Pathfinding.playground */, 39BAD55D1FE19B9E00524FB7 /* DemoApp */, 39BAD5741FE19B9E00524FB7 /* UITests */, 39AB5FE51FE4595000008FB3 /* MappaMundi */, diff --git a/Pathfinding.playground/Contents.swift b/Pathfinding.playground/Contents.swift new file mode 100644 index 0000000..fee0a38 --- /dev/null +++ b/Pathfinding.playground/Contents.swift @@ -0,0 +1,87 @@ + +import GameplayKit +class NamedGKGraphNode: GKGraphNode { + var name: String + convenience init(_ name:String) { + self.init() + self.name = name + } + override init() { + self.name = "" + super.init() + } + required init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } +} + +let node1 = NamedGKGraphNode("1") // declaring the Graph Nodes +let node2 = NamedGKGraphNode("2") +let node3 = NamedGKGraphNode("3") +let node4 = NamedGKGraphNode("4") +let node5 = NamedGKGraphNode("5") +let node6 = NamedGKGraphNode("6") +let node7 = NamedGKGraphNode("7") +let node8 = NamedGKGraphNode("8") +let node9 = NamedGKGraphNode("9") +let node10 = NamedGKGraphNode("10") +let node11 = NamedGKGraphNode("11") +let node12 = NamedGKGraphNode("12") +let node13 = NamedGKGraphNode("13") +let node14 = NamedGKGraphNode("14") +let node15 = NamedGKGraphNode("15") +let node16 = NamedGKGraphNode("16") +let node17 = NamedGKGraphNode("17") + +func prepareGraph() -> GKGraph { + let graph = GKGraph() // declaring the Graph + + // adding the Graph Nodes to the Graph + graph.add([ + node1, node2, node3, node4, node5, node6, node8, node9, node10, + node11, node12, node13, node14, node15, node16, node17 + ]) + + //column 1 + node1.addConnections(to: [node2,node3,node4,node5,node6], bidirectional: false) + node2.addConnections(to: [node13], bidirectional: false) + node3.addConnections(to: [node7], bidirectional: false) + node4.addConnections(to: [node7], bidirectional: false) + node5.addConnections(to: [node8], bidirectional: false) + node6.addConnections(to: [node17], bidirectional: false) + //column 2 + node7.addConnections(to: [node9, node10], bidirectional: false) + node8.addConnections(to: [node11, node12], bidirectional: false) + //column 3 + node9.addConnections(to: [node13], bidirectional: false) + node10.addConnections(to: [node17], bidirectional: false) + node11.addConnections(to: [node17], bidirectional: false) + node12.addConnections(to: [node14], bidirectional: false) + //column 4 + node13.addConnections(to: [node15], bidirectional: false) + node14.addConnections(to: [node16], bidirectional: false) + //column 5 + node15.addConnections(to: [node17], bidirectional: false) + node16.addConnections(to: [node17], bidirectional: false) + + return graph +} + + +func printPath(_ path:[GKGraphNode] ){ + path.forEach { node in + if let node = node as? NamedGKGraphNode { + print(node.name) + } + } +} + +for _ in 1...1000 { + let path: [GKGraphNode] = prepareGraph().findPath(from: node1, to: node17) + if path.count != 3 { + fatalError("path length should be 1->6->17, 3 steps!") + } +} + + + diff --git a/Pathfinding.playground/contents.xcplayground b/Pathfinding.playground/contents.xcplayground new file mode 100644 index 0000000..5da2641 --- /dev/null +++ b/Pathfinding.playground/contents.xcplayground @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/Sources/MMNavigator.swift b/Sources/MMNavigator.swift index ac3c823..0938f50 100644 --- a/Sources/MMNavigator.swift +++ b/Sources/MMNavigator.swift @@ -140,9 +140,6 @@ open class MMNavigator { gkPath.forEach { (node) in print("------------------->\(String(describing: map.nodedScenes[node]?.name))") } - if gkPath.count != 3 { - fatalError("find path error!") - } } guard gkPath.count > 0 else { From 7d44335114a24ffbc0b2fdf8e789ca3c5b6361a4 Mon Sep 17 00:00:00 2001 From: ampm Date: Fri, 7 Jun 2019 18:22:36 +0800 Subject: [PATCH 03/23] replace GamePlayKit with aStar for path finding #16 --- MappaMundi.xcodeproj/project.pbxproj | 20 ++++ MappaMundi/aStar/MMNode.swift | 34 +++++++ MappaMundi/aStar/Node.swift | 144 +++++++++++++++++++++++++++ MappaMundi/aStar/SortedArray.swift | 66 ++++++++++++ Sources/MMGraphNode.swift | 5 +- Sources/MMNavigator.swift | 32 +++--- Sources/MMScreenGraph.swift | 24 +++-- Sources/MMScreenStateNode.swift | 1 - Sources/ScreenGraphEdge.swift | 7 +- UITests/DemoUITests.swift | 3 +- 10 files changed, 296 insertions(+), 40 deletions(-) create mode 100644 MappaMundi/aStar/MMNode.swift create mode 100644 MappaMundi/aStar/Node.swift create mode 100644 MappaMundi/aStar/SortedArray.swift diff --git a/MappaMundi.xcodeproj/project.pbxproj b/MappaMundi.xcodeproj/project.pbxproj index 0b2d312..cf6ae49 100644 --- a/MappaMundi.xcodeproj/project.pbxproj +++ b/MappaMundi.xcodeproj/project.pbxproj @@ -7,6 +7,9 @@ objects = { /* Begin PBXBuildFile section */ + 13DF8A2822AA667F004294F1 /* SortedArray.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13DF8A2622AA667F004294F1 /* SortedArray.swift */; }; + 13DF8A2922AA667F004294F1 /* Node.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13DF8A2722AA667F004294F1 /* Node.swift */; }; + 13DF8A2B22AA6E2E004294F1 /* MMNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13DF8A2A22AA6E2E004294F1 /* MMNode.swift */; }; 392FB353200E1D5D0014867E /* MMTestUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 392FB352200E1D5D0014867E /* MMTestUtils.swift */; }; 3962F2291FF6A6C100999008 /* Wait.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3962F2281FF6A6C100999008 /* Wait.swift */; }; 3962F22B1FF6A75800999008 /* ScreenGraphEdge.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3962F22A1FF6A75800999008 /* ScreenGraphEdge.swift */; }; @@ -52,6 +55,9 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 13DF8A2622AA667F004294F1 /* SortedArray.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SortedArray.swift; sourceTree = ""; }; + 13DF8A2722AA667F004294F1 /* Node.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Node.swift; sourceTree = ""; }; + 13DF8A2A22AA6E2E004294F1 /* MMNode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MMNode.swift; sourceTree = ""; }; 13FB93F1227F0D5100BAEAFA /* Pathfinding.playground */ = {isa = PBXFileReference; lastKnownFileType = file.playground; path = Pathfinding.playground; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; 392FB352200E1D5D0014867E /* MMTestUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MMTestUtils.swift; sourceTree = ""; }; 3962F2281FF6A6C100999008 /* Wait.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = Wait.swift; path = ../Sources/Wait.swift; sourceTree = ""; }; @@ -107,9 +113,20 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 13DF8A2522AA666C004294F1 /* aStar */ = { + isa = PBXGroup; + children = ( + 13DF8A2A22AA6E2E004294F1 /* MMNode.swift */, + 13DF8A2722AA667F004294F1 /* Node.swift */, + 13DF8A2622AA667F004294F1 /* SortedArray.swift */, + ); + path = aStar; + sourceTree = ""; + }; 39AB5FE51FE4595000008FB3 /* MappaMundi */ = { isa = PBXGroup; children = ( + 13DF8A2522AA666C004294F1 /* aStar */, 39AB5FDB1FE455C700008FB3 /* MMScreenGraph.swift */, 397334481FF6AAA600C45ECE /* MMGraphNode.swift */, 392FB352200E1D5D0014867E /* MMTestUtils.swift */, @@ -342,7 +359,10 @@ buildActionMask = 2147483647; files = ( 397334491FF6AAA600C45ECE /* MMGraphNode.swift in Sources */, + 13DF8A2B22AA6E2E004294F1 /* MMNode.swift in Sources */, + 13DF8A2822AA667F004294F1 /* SortedArray.swift in Sources */, 39658ADC1FF699AA000544A2 /* MMScreenStateNode.swift in Sources */, + 13DF8A2922AA667F004294F1 /* Node.swift in Sources */, 39870718200D16710067B070 /* GraphRepresentation.swift in Sources */, 39658AE21FF6A065000544A2 /* MMUserState.swift in Sources */, 3962F22B1FF6A75800999008 /* ScreenGraphEdge.swift in Sources */, diff --git a/MappaMundi/aStar/MMNode.swift b/MappaMundi/aStar/MMNode.swift new file mode 100644 index 0000000..42aa413 --- /dev/null +++ b/MappaMundi/aStar/MMNode.swift @@ -0,0 +1,34 @@ +// +// MMNode.swift +// MappaMundi +// +// Created by Yang, Roy on 2019/6/7. +// Copyright © 2019 Mozilla Corporation. All rights reserved. +// + +import UIKit +final class MMNode: GraphNode { + var name: String? + + convenience init(name: String) { + self.init() + self.name = name + } + + static func == (lhs: MMNode, rhs: MMNode) -> Bool { + return lhs.name == rhs.name + } + + var hashValue: Int { + return name.hashValue + } + var connectedNodes = Set() + + func cost(to node: MMNode) -> Float { + return 1 + } + + func estimatedCost(to node: MMNode) -> Float { + return cost(to: node) + } +} diff --git a/MappaMundi/aStar/Node.swift b/MappaMundi/aStar/Node.swift new file mode 100644 index 0000000..867060a --- /dev/null +++ b/MappaMundi/aStar/Node.swift @@ -0,0 +1,144 @@ +// +// Node.swift +// aStar +// +// Created by Damiaan Dufaux on 19/08/16. +// Copyright © 2016 Damiaan Dufaux. All rights reserved. +// + + +/// This protocol declares the requirements for optimal pathfinding in a directed graph of nodes and implements the A* algorithm via an extension. +public protocol GraphNode: Hashable { + // MARK: Optimal pathfinding requirements + + /** + * List of other graph nodes that this node has an edge leading to. + */ + var connectedNodes: Set { get } + + + /// Returns the estimated heuristic cost to reach the indicated node from this node + /// + /// - Parameter node: the end point of the edge who's cost is to be estimated + /// - Returns: the heuristic cost + func estimatedCost(to node: Self) -> Float + + + /// - Parameter node: the destination node + /// - Returns: the actual cost to reach the indicated node from this node + func cost(to node: Self) -> Float + +} + + +class Step { + var node: Node + var previous: Step? + + var stepCost: Float + var goalCost: Float + + init(from start: Node, to destination: Node, goal: Node) { + node = destination + stepCost = start.cost(to: destination) + goalCost = destination.estimatedCost(to: goal) + } + + init(destination: Node, previous: Step, goal: Node) { + (node, self.previous) = (destination, previous) + stepCost = previous.stepCost + previous.node.cost(to: destination) + goalCost = destination.estimatedCost(to: goal) + } + + func cost() -> Float { + return stepCost + goalCost + } + +} + +extension Step: Hashable, Equatable, Comparable { + var hashValue: Int { + return node.hashValue + } + + static func ==(lhs: Step, rhs: Step) -> Bool { + return lhs.node == rhs.node + } + + public static func <(lhs: Step, rhs: Step) -> Bool { + return lhs.cost() < rhs.cost() + } + + public static func <=(lhs: Step, rhs: Step) -> Bool { + return lhs.cost() <= rhs.cost() + } + + public static func >=(lhs: Step, rhs: Step) -> Bool { + return lhs.cost() >= rhs.cost() + } + + public static func >(lhs: Step, rhs: Step) -> Bool { + return lhs.cost() > rhs.cost() + } + +} + + +extension GraphNode { + // MARK: A* Implementation + + /// Attempts to find the optimal path between this node and the indicated goal node. + /// If such a path exists, it is returned in start to end order. + /// If it doesn't exist, the array returned will be empty. + /// + /// - Parameter goalNode: the goal node of the pathfinding attempt + /// - Returns: the optimal path between this node and the indicated goal node + public func findPath(to goalNode: Self) -> [Self] { + var possibleSteps = [Step]() + var eliminatedNodes: Set = [self] + + for connectedNode in connectedNodes { + let step = Step(from: self, to: connectedNode, goal: goalNode) + possibleSteps.sortedInsert(newElement: step) + } + + var path = [self] + while !possibleSteps.isEmpty { + let step = possibleSteps.removeFirst() + if step.node == goalNode { + var cursor = step + path.insert(step.node, at: 1) + while let previous = cursor.previous { + cursor = previous + path.insert(previous.node, at: 1) + } + break + } + eliminatedNodes.insert(step.node) + let nextNodes = step.node.connectedNodes.subtracting(eliminatedNodes) + for node in nextNodes { + // TODO don't generate a step because in some cases it is never used + let nextStep = Step(destination: node, previous: step, goal: goalNode) + let index = possibleSteps.binarySearch(element: nextStep) + if index [Self] { + return startNode.findPath(to: self) + } +} diff --git a/MappaMundi/aStar/SortedArray.swift b/MappaMundi/aStar/SortedArray.swift new file mode 100644 index 0000000..9451bce --- /dev/null +++ b/MappaMundi/aStar/SortedArray.swift @@ -0,0 +1,66 @@ +// +// SortedArray.swift +// aStar +// +// Created by Damiaan Dufaux on 21/08/16. +// Copyright © 2016 Damiaan Dufaux. All rights reserved. +// + +import Foundation + +extension Array { + + /// Finds such index N that predicate is true for all elements up to + /// but not including the index N, and is false for all elements + /// starting with index N. + /// Behavior is undefined if there is no such N. + func binarySearch(predicate: (Element) -> Bool) -> Int { + var low = startIndex + var high = endIndex + while low != high { + let mid = low.advanced(by: low.distance(to: high) / 2) + if predicate(self[mid]) { + low = mid.advanced(by: 1) + } else { + high = mid + } + } + return low + } + + mutating func sortedInsert(element: Element, predicate: (Element, Element) -> Bool) { + insert(element, at: binarySearch{predicate($0, element)} ) + } + +} + +extension Array where Element: Comparable { + /// Finds such index N that predicate is true for all elements up to + /// but not including the index N, and is false for all elements + /// starting with index N. + /// Behavior is undefined if there is no such N. + func binarySearch(element: Element) -> Int { + var low = startIndex + var high = endIndex + while low != high { + let mid = low.advanced(by: low.distance(to: high) / 2) + if self[mid] < element { + low = mid.advanced(by: 1) + } else { + high = mid + } + } + return low + } + + /// Inserts a new element in a sorted array. + /// + /// - Parameter newElement: The element to insert into the array. + /// + /// - Complexity: To find the index for the new element + /// it uses a binary search algorithm + + mutating func sortedInsert(newElement: Element) { + insert(newElement, at: binarySearch(element: newElement) ) + } +} diff --git a/Sources/MMGraphNode.swift b/Sources/MMGraphNode.swift index c2d6d76..3917aff 100644 --- a/Sources/MMGraphNode.swift +++ b/Sources/MMGraphNode.swift @@ -3,7 +3,6 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ import Foundation -import GameplayKit public class MMGraphElement { let name: String @@ -23,14 +22,14 @@ public class MMGraphElement { public class MMGraphNode: MMGraphElement { var nodeType: String { return "Node" } - let gkNode: GKGraphNode + let gkNode: MMNode weak var map: MMScreenGraph? init(_ map: MMScreenGraph, name: String, file: String, line: UInt) { self.map = map - self.gkNode = GKGraphNode() + self.gkNode = MMNode(name: name) super.init(name: name, file: file, line: line) } diff --git a/Sources/MMNavigator.swift b/Sources/MMNavigator.swift index 0938f50..141c53f 100644 --- a/Sources/MMNavigator.swift +++ b/Sources/MMNavigator.swift @@ -3,7 +3,6 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ import Foundation -import GameplayKit import XCTest public typealias NodeVisitor = (String) -> Void @@ -67,7 +66,7 @@ open class MMNavigator { guard let gkDest = map.namedScenes[nodeName]?.gkNode else { return false } - let gkPath = map.gkGraph.findPath(from: gkSrc, to: gkDest) + let gkPath = gkSrc.findPath(to: gkDest) return gkPath.count > 0 } @@ -79,7 +78,7 @@ open class MMNavigator { } public func plan(startAt startNode: String? = nil, goto nodeName: String) -> [String] { - let gkSrc: GKGraphNode + let gkSrc: MMNode if let startNode = startNode, let node = map.namedScenes[startNode] { gkSrc = node.gkNode @@ -92,7 +91,7 @@ open class MMNavigator { } let gkDest = destNode.gkNode - let gkPath = map.gkGraph.findPath(from: gkSrc, to: gkDest) + let gkPath = gkSrc.findPath(to: gkDest) let path = gkPath.compactMap { gkNode in return self.map.nodedScenes[gkNode]?.name @@ -131,17 +130,9 @@ open class MMNavigator { xcTest.recordFailure(withDescription: "Cannot route to \(nodeName), because it doesn't exist", inFile: file, atLine: Int(line), expected: false) return } - - var gkPath = map.gkGraph.findPath(from: gkSrc, to: gkDest) - - //for reproducing the findpath error. - if nodeName == "foo_5" { - print("gkPath.count---\(gkPath.count)") - gkPath.forEach { (node) in - print("------------------->\(String(describing: map.nodedScenes[node]?.name))") - } - } + var gkPath = gkSrc.findPath(to: gkDest) + guard gkPath.count > 0 else { xcTest.recordFailure(withDescription: "Cannot route from \(currentGraphNode.name) to \(nodeName)", inFile: file, atLine: Int(line), expected: false) return @@ -323,7 +314,8 @@ fileprivate extension MMNavigator { // currentScene is the state we're returning from. // nextScene is the state we're returning to. exitingNode.returnNode = nil - exitingNode.gkNode.removeConnections(to: [ nextNode.gkNode ], bidirectional: false) +// exitingNode.gkNode.removeConnections(to: [ nextNode.gkNode ], bidirectional: false) + exitingNode.gkNode.connectedNodes.remove(nextNode.gkNode) } } } @@ -353,7 +345,8 @@ fileprivate extension MMNavigator { if let backAction = enteringNode.backAction { if enteringNode.returnNode == nil { enteringNode.returnNode = returnToRecentScene - enteringNode.gkNode.addConnections(to: [ returnToRecentScene.gkNode ], bidirectional: false) +// enteringNode.gkNode.addConnections(to: [ returnToRecentScene.gkNode ], bidirectional: false) + enteringNode.gkNode.connectedNodes.insert(returnToRecentScene.gkNode) enteringNode.gesture(to: returnToRecentScene.name, g: backAction) } } else { @@ -369,7 +362,8 @@ fileprivate extension MMNavigator { thisScene.returnNode = nil thisScene.edges.removeValue(forKey: prevScene.name) - thisScene.gkNode.removeConnections(to: [ prevScene.gkNode ], bidirectional: false) +// thisScene.gkNode.removeConnections(to: [ prevScene.gkNode ], bidirectional: false) + thisScene.gkNode.connectedNodes.remove(prevScene.gkNode) screen = prevScene } @@ -426,9 +420,9 @@ fileprivate extension MMNavigator { } graphChanged = true if edge.isOpen { - edge.src.addConnections(to: [edge.dest], bidirectional: false) + edge.src.connectedNodes.insert(edge.dest) } else { - edge.src.removeConnections(to: [edge.dest], bidirectional: false) + edge.src.connectedNodes.insert(edge.dest) } } return graphChanged diff --git a/Sources/MMScreenGraph.swift b/Sources/MMScreenGraph.swift index b4b4592..7395b69 100644 --- a/Sources/MMScreenGraph.swift +++ b/Sources/MMScreenGraph.swift @@ -12,11 +12,10 @@ * * The shared graph may also have other uses, such as generating screen shots for the App Store or L10n translators. * - * Under the hood, the MappaMundi is using GameplayKit's path finding to do the heavy lifting. + * Under the hood, the MappaMundi is using aStar's path finding to do the heavy lifting. */ import Foundation -import GameplayKit import XCTest public typealias MMScreenStateBuilder = (MMScreenStateNode) -> Void @@ -32,19 +31,18 @@ open class MMScreenGraph { let xcTest: XCTestCase var namedScenes: [String: MMGraphNode] = [:] - var nodedScenes: [GKGraphNode: MMGraphNode] = [:] + var nodedScenes: [MMNode: MMGraphNode] = [:] var conditionalEdges: [ConditionalEdge] = [] fileprivate var isReady: Bool = false - let gkGraph: GKGraph - + let rootNode: MMNode public typealias UserStateChange = (T) -> () fileprivate let defaultStateRecorder: UserStateChange = { _ in } public init(for test: XCTestCase, with userStateType: T.Type) { - self.gkGraph = GKGraph() + self.rootNode = MMNode() self.userStateType = userStateType self.xcTest = test } @@ -224,8 +222,10 @@ public extension MMScreenGraph { // Construct all the GKGraphNodes, and add them to the GKGraph. let graphNodes = namedScenes.values - gkGraph.add(graphNodes.map { $0.gkNode }) - + graphNodes.forEach({ + rootNode.connectedNodes.insert($0.gkNode) + }) + graphNodes.forEach { graphNode in nodedScenes[graphNode.gkNode] = graphNode } @@ -234,12 +234,14 @@ public extension MMScreenGraph { // so we need to construct the GKGraph edges from it. graphNodes.forEach { graphNode in if let screenStateNode = graphNode as? MMScreenStateNode { - let gkNodes = screenStateNode.edges.keys.compactMap { self.namedScenes[$0]?.gkNode } as [GKGraphNode] - screenStateNode.gkNode.addConnections(to: gkNodes, bidirectional: false) + let gkNodes = screenStateNode.edges.keys.compactMap { self.namedScenes[$0]?.gkNode } as [MMNode] + gkNodes.forEach{ + screenStateNode.gkNode.connectedNodes.insert($0) + } } else if let screenActionNode = graphNode as? MMScreenActionNode { if let destName = screenActionNode.nextNodeName, let destGkNode = namedScenes[destName]?.gkNode { - screenActionNode.gkNode.addConnections(to: [destGkNode], bidirectional: false) + screenActionNode.gkNode.connectedNodes.insert(destGkNode) } } } diff --git a/Sources/MMScreenStateNode.swift b/Sources/MMScreenStateNode.swift index 15c428e..ca75387 100644 --- a/Sources/MMScreenStateNode.swift +++ b/Sources/MMScreenStateNode.swift @@ -3,7 +3,6 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ import Foundation -import GameplayKit import XCTest /** diff --git a/Sources/ScreenGraphEdge.swift b/Sources/ScreenGraphEdge.swift index 75f206d..2563f23 100644 --- a/Sources/ScreenGraphEdge.swift +++ b/Sources/ScreenGraphEdge.swift @@ -3,7 +3,6 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ import Foundation -import GameplayKit import XCTest // These are two data structures that define an edge between nodes. @@ -23,12 +22,12 @@ struct Edge { // re-evaluated. class ConditionalEdge { let predicate: NSPredicate - let src: GKGraphNode - let dest: GKGraphNode + let src: MMNode + let dest: MMNode var isOpen: Bool = true - init(src: GKGraphNode, dest: GKGraphNode, predicate: NSPredicate) { + init(src: MMNode, dest: MMNode, predicate: NSPredicate) { self.src = src self.dest = dest self.predicate = predicate diff --git a/UITests/DemoUITests.swift b/UITests/DemoUITests.swift index 1f730f7..4e87cc6 100644 --- a/UITests/DemoUITests.swift +++ b/UITests/DemoUITests.swift @@ -127,8 +127,7 @@ class DemoUITests: XCTestCase { } } func testReproduceFindpathError() { - navigator.performAction(Actions.addItem) - navigator.goto(Screens.itemDetail) + navigator.nowAt(Screens.itemDetail) navigator.goto(Screens.foo5) } } From cfc55e8bc2ffdf77e39e03506437e1abef1cf2c3 Mon Sep 17 00:00:00 2001 From: ampm Date: Fri, 7 Jun 2019 18:28:08 +0800 Subject: [PATCH 04/23] remove the code for reproducing bug #16 --- MappaMundi.xcodeproj/project.pbxproj | 2 - Pathfinding.playground/Contents.swift | 87 -------------------- Pathfinding.playground/contents.xcplayground | 4 - UITests/DemoMappaMundi.swift | 59 +------------ UITests/DemoUITests.swift | 11 --- 5 files changed, 3 insertions(+), 160 deletions(-) delete mode 100644 Pathfinding.playground/Contents.swift delete mode 100644 Pathfinding.playground/contents.xcplayground diff --git a/MappaMundi.xcodeproj/project.pbxproj b/MappaMundi.xcodeproj/project.pbxproj index cf6ae49..1fad71d 100644 --- a/MappaMundi.xcodeproj/project.pbxproj +++ b/MappaMundi.xcodeproj/project.pbxproj @@ -58,7 +58,6 @@ 13DF8A2622AA667F004294F1 /* SortedArray.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SortedArray.swift; sourceTree = ""; }; 13DF8A2722AA667F004294F1 /* Node.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Node.swift; sourceTree = ""; }; 13DF8A2A22AA6E2E004294F1 /* MMNode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MMNode.swift; sourceTree = ""; }; - 13FB93F1227F0D5100BAEAFA /* Pathfinding.playground */ = {isa = PBXFileReference; lastKnownFileType = file.playground; path = Pathfinding.playground; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; 392FB352200E1D5D0014867E /* MMTestUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MMTestUtils.swift; sourceTree = ""; }; 3962F2281FF6A6C100999008 /* Wait.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = Wait.swift; path = ../Sources/Wait.swift; sourceTree = ""; }; 3962F22A1FF6A75800999008 /* ScreenGraphEdge.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = ScreenGraphEdge.swift; path = ../Sources/ScreenGraphEdge.swift; sourceTree = ""; }; @@ -154,7 +153,6 @@ 39BAD5521FE19B9D00524FB7 = { isa = PBXGroup; children = ( - 13FB93F1227F0D5100BAEAFA /* Pathfinding.playground */, 39BAD55D1FE19B9E00524FB7 /* DemoApp */, 39BAD5741FE19B9E00524FB7 /* UITests */, 39AB5FE51FE4595000008FB3 /* MappaMundi */, diff --git a/Pathfinding.playground/Contents.swift b/Pathfinding.playground/Contents.swift deleted file mode 100644 index fee0a38..0000000 --- a/Pathfinding.playground/Contents.swift +++ /dev/null @@ -1,87 +0,0 @@ - -import GameplayKit -class NamedGKGraphNode: GKGraphNode { - var name: String - convenience init(_ name:String) { - self.init() - self.name = name - } - override init() { - self.name = "" - super.init() - } - required init?(coder aDecoder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } -} - -let node1 = NamedGKGraphNode("1") // declaring the Graph Nodes -let node2 = NamedGKGraphNode("2") -let node3 = NamedGKGraphNode("3") -let node4 = NamedGKGraphNode("4") -let node5 = NamedGKGraphNode("5") -let node6 = NamedGKGraphNode("6") -let node7 = NamedGKGraphNode("7") -let node8 = NamedGKGraphNode("8") -let node9 = NamedGKGraphNode("9") -let node10 = NamedGKGraphNode("10") -let node11 = NamedGKGraphNode("11") -let node12 = NamedGKGraphNode("12") -let node13 = NamedGKGraphNode("13") -let node14 = NamedGKGraphNode("14") -let node15 = NamedGKGraphNode("15") -let node16 = NamedGKGraphNode("16") -let node17 = NamedGKGraphNode("17") - -func prepareGraph() -> GKGraph { - let graph = GKGraph() // declaring the Graph - - // adding the Graph Nodes to the Graph - graph.add([ - node1, node2, node3, node4, node5, node6, node8, node9, node10, - node11, node12, node13, node14, node15, node16, node17 - ]) - - //column 1 - node1.addConnections(to: [node2,node3,node4,node5,node6], bidirectional: false) - node2.addConnections(to: [node13], bidirectional: false) - node3.addConnections(to: [node7], bidirectional: false) - node4.addConnections(to: [node7], bidirectional: false) - node5.addConnections(to: [node8], bidirectional: false) - node6.addConnections(to: [node17], bidirectional: false) - //column 2 - node7.addConnections(to: [node9, node10], bidirectional: false) - node8.addConnections(to: [node11, node12], bidirectional: false) - //column 3 - node9.addConnections(to: [node13], bidirectional: false) - node10.addConnections(to: [node17], bidirectional: false) - node11.addConnections(to: [node17], bidirectional: false) - node12.addConnections(to: [node14], bidirectional: false) - //column 4 - node13.addConnections(to: [node15], bidirectional: false) - node14.addConnections(to: [node16], bidirectional: false) - //column 5 - node15.addConnections(to: [node17], bidirectional: false) - node16.addConnections(to: [node17], bidirectional: false) - - return graph -} - - -func printPath(_ path:[GKGraphNode] ){ - path.forEach { node in - if let node = node as? NamedGKGraphNode { - print(node.name) - } - } -} - -for _ in 1...1000 { - let path: [GKGraphNode] = prepareGraph().findPath(from: node1, to: node17) - if path.count != 3 { - fatalError("path length should be 1->6->17, 3 steps!") - } -} - - - diff --git a/Pathfinding.playground/contents.xcplayground b/Pathfinding.playground/contents.xcplayground deleted file mode 100644 index 5da2641..0000000 --- a/Pathfinding.playground/contents.xcplayground +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/UITests/DemoMappaMundi.swift b/UITests/DemoMappaMundi.swift index 79fb493..0a1b6eb 100644 --- a/UITests/DemoMappaMundi.swift +++ b/UITests/DemoMappaMundi.swift @@ -13,31 +13,14 @@ class Actions { static let deleteAllItems = "deleteAllItems" static let initialWithExactlyOne = "initialWithExactlyOne" static let postAddItem = "postAddItem" - static let action1 = "item_action_1" - static let action2 = "item_action_2" - static let action3 = "item_action_3" - static let action4 = "item_action_4" - static let action5 = "item_action_5" - static let action6 = "item_action_6" - static let action7 = "item_action_7" - static let action8 = "item_action_8" - static let action9 = "item_action_9" - static let action10 = "item_action_10" - static let action11 = "item_action_11" - static let action12 = "item_action_12" } class Screens { static let itemList = "ItemList" static let itemListEditing = "EditingItemList" static let itemDetail = "ItemDetail" - static let foo1 = "foo_1" - static let foo2 = "foo_2" - static let foo3 = "foo_3" - static let foo4 = "foo_4" - static let foo5 = "foo_5" } -@objcMembers + class DemoAppUserState: MMUserState { required init() { super.init() @@ -87,44 +70,8 @@ func createGraph(with app: XCUIApplication, for test: XCTestCase) -> MMScreenGra // and a back button. map.addScreenState(Screens.itemDetail) { screenState in screenState.backAction = navigationControllerBackAction - - screenState.gesture(forAction: Actions.action1,transitionTo: Screens.foo5) { _ in - } - screenState.gesture(forAction: Actions.action2,transitionTo: Screens.foo1) { _ in - } - screenState.gesture(forAction: Actions.action3,transitionTo: Screens.foo2) { _ in - } - screenState.gesture(forAction: Actions.action4,transitionTo: Screens.foo2) { _ in - } - screenState.gesture(forAction: Actions.action5,transitionTo: Screens.foo4) { _ in - } } - - map.addScreenState(Screens.foo1) { screenState in - screenState.gesture(forAction: Actions.action6,transitionTo: Screens.foo5) { _ in - } - screenState.gesture(forAction: Actions.action7,transitionTo: Screens.foo3) { _ in - } - } - map.addScreenState(Screens.foo2) { screenState in - screenState.gesture(forAction: Actions.action8,transitionTo: Screens.foo5) { _ in - } - screenState.gesture(forAction: Actions.action9,transitionTo: Screens.foo4) { _ in - } - } - map.addScreenState(Screens.foo3) { screenState in - screenState.gesture(forAction: Actions.action10,transitionTo: Screens.foo5) { _ in - } - } - - map.addScreenState(Screens.foo4) { screenState in - screenState.gesture(forAction: Actions.action11,transitionTo: Screens.foo5) { _ in - } - } - - map.addScreenState(Screens.foo5) { screenState in - } - + // The edit screen has a delete action. We can also map.addScreenState(Screens.itemListEditing) { screenState in let table = app.tables.element(boundBy: 0) @@ -157,6 +104,6 @@ func createGraph(with app: XCUIApplication, for test: XCTestCase) -> MMScreenGra map.addNavigatorAction(Actions.postAddItem) { navigator in print("In \(navigator.screenState)") } - + return map } diff --git a/UITests/DemoUITests.swift b/UITests/DemoUITests.swift index 4e87cc6..4669713 100644 --- a/UITests/DemoUITests.swift +++ b/UITests/DemoUITests.swift @@ -34,7 +34,6 @@ class DemoUITests: XCTestCase { MMTestUtils.render(graph: createGraph(with: app, for: self)) } - func testSimpleNavigation() { navigator.performAction(Actions.addItem) navigator.goto(Screens.itemDetail) @@ -120,14 +119,4 @@ class DemoUITests: XCTestCase { navigator.performAction(Actions.postAddItem) XCTAssertEqual(2, userState.numItems) } - override func invokeTest() { - for time in 0...10 { - print("this test is invoking: \(time) times") - super.invokeTest() - } - } - func testReproduceFindpathError() { - navigator.nowAt(Screens.itemDetail) - navigator.goto(Screens.foo5) - } } From a27f65f23d10a22d6d02f17edea5a1a3d1b0a069 Mon Sep 17 00:00:00 2001 From: ampm Date: Fri, 7 Jun 2019 19:21:20 +0800 Subject: [PATCH 05/23] remove the gk prefix --- Sources/GraphRepresentation.swift | 2 +- Sources/MMGraphNode.swift | 4 +-- Sources/MMNavigator.swift | 43 ++++++++++++++----------------- Sources/MMScreenGraph.swift | 28 ++++++++++---------- Sources/MMScreenStateNode.swift | 2 +- 5 files changed, 38 insertions(+), 41 deletions(-) diff --git a/Sources/GraphRepresentation.swift b/Sources/GraphRepresentation.swift index b19f6fe..dc43bcf 100644 --- a/Sources/GraphRepresentation.swift +++ b/Sources/GraphRepresentation.swift @@ -18,7 +18,7 @@ public protocol GraphRepresentation { public extension MMScreenGraph { func stringRepresentation(_ renderer: GraphRepresentation = DotRepresentation()) -> String { - buildGkGraph() + buildGraph() renderer.begin() namedScenes.forEach { (name, node) in if let node = node as? MMScreenStateNode { diff --git a/Sources/MMGraphNode.swift b/Sources/MMGraphNode.swift index 3917aff..4028b76 100644 --- a/Sources/MMGraphNode.swift +++ b/Sources/MMGraphNode.swift @@ -22,14 +22,14 @@ public class MMGraphElement { public class MMGraphNode: MMGraphElement { var nodeType: String { return "Node" } - let gkNode: MMNode + let mmNode: MMNode weak var map: MMScreenGraph? init(_ map: MMScreenGraph, name: String, file: String, line: UInt) { self.map = map - self.gkNode = MMNode(name: name) + self.mmNode = MMNode(name: name) super.init(name: name, file: file, line: line) } diff --git a/Sources/MMNavigator.swift b/Sources/MMNavigator.swift index 141c53f..c8b42ee 100644 --- a/Sources/MMNavigator.swift +++ b/Sources/MMNavigator.swift @@ -62,12 +62,12 @@ open class MMNavigator { * so is not useful in the general case, but it is if you know the specific graph. */ public func can(goto nodeName: String) -> Bool { - let gkSrc = currentGraphNode.gkNode - guard let gkDest = map.namedScenes[nodeName]?.gkNode else { + let mmSrc = currentGraphNode.mmNode + guard let mmDest = map.namedScenes[nodeName]?.mmNode else { return false } - let gkPath = gkSrc.findPath(to: gkDest) - return gkPath.count > 0 + let mmPath = mmSrc.findPath(to: mmDest) + return mmPath.count > 0 } public func can(performAction nodeName: String, file: String = #file, line: UInt = #line) -> Bool { @@ -78,23 +78,23 @@ open class MMNavigator { } public func plan(startAt startNode: String? = nil, goto nodeName: String) -> [String] { - let gkSrc: MMNode + let mmSrc: MMNode if let startNode = startNode, let node = map.namedScenes[startNode] { - gkSrc = node.gkNode + mmSrc = node.mmNode } else { - gkSrc = currentGraphNode.gkNode + mmSrc = currentGraphNode.mmNode } guard let destNode = map.namedScenes[nodeName] else { return [] } - let gkDest = destNode.gkNode - let gkPath = gkSrc.findPath(to: gkDest) + let mmDest = destNode.mmNode + let mmPath = mmSrc.findPath(to: mmDest) - let path = gkPath.compactMap { gkNode in - return self.map.nodedScenes[gkNode]?.name + let path = mmPath.compactMap { mmNode in + return self.map.nodedScenes[mmNode]?.name } if path.isEmpty { @@ -125,15 +125,15 @@ open class MMNavigator { * node changes. */ public func goto(_ nodeName: String, file: String = #file, line: UInt = #line, visitWith nodeVisitor: @escaping NodeVisitor) { - let gkSrc = currentGraphNode.gkNode - guard let gkDest = map.namedScenes[nodeName]?.gkNode else { + let mmSrc = currentGraphNode.mmNode + guard let mmDest = map.namedScenes[nodeName]?.mmNode else { xcTest.recordFailure(withDescription: "Cannot route to \(nodeName), because it doesn't exist", inFile: file, atLine: Int(line), expected: false) return } - var gkPath = gkSrc.findPath(to: gkDest) + var mmPath = mmSrc.findPath(to: mmDest) - guard gkPath.count > 0 else { + guard mmPath.count > 0 else { xcTest.recordFailure(withDescription: "Cannot route from \(currentGraphNode.name) to \(nodeName)", inFile: file, atLine: Int(line), expected: false) return } @@ -163,8 +163,8 @@ open class MMNavigator { return maybeStateChanged && self.userStateShouldChangeGraph(userState) } - gkPath.removeFirst() - let graphNodes = gkPath.compactMap { map.nodedScenes[$0] } + mmPath.removeFirst() + let graphNodes = mmPath.compactMap { map.nodedScenes[$0] } for i in 0 ..< graphNodes.count { let graphChanged = moveDirectlyTo(graphNodes[i]) @@ -314,8 +314,7 @@ fileprivate extension MMNavigator { // currentScene is the state we're returning from. // nextScene is the state we're returning to. exitingNode.returnNode = nil -// exitingNode.gkNode.removeConnections(to: [ nextNode.gkNode ], bidirectional: false) - exitingNode.gkNode.connectedNodes.remove(nextNode.gkNode) + exitingNode.mmNode.connectedNodes.remove(nextNode.mmNode) } } } @@ -345,8 +344,7 @@ fileprivate extension MMNavigator { if let backAction = enteringNode.backAction { if enteringNode.returnNode == nil { enteringNode.returnNode = returnToRecentScene -// enteringNode.gkNode.addConnections(to: [ returnToRecentScene.gkNode ], bidirectional: false) - enteringNode.gkNode.connectedNodes.insert(returnToRecentScene.gkNode) + enteringNode.mmNode.connectedNodes.insert(returnToRecentScene.mmNode) enteringNode.gesture(to: returnToRecentScene.name, g: backAction) } } else { @@ -362,8 +360,7 @@ fileprivate extension MMNavigator { thisScene.returnNode = nil thisScene.edges.removeValue(forKey: prevScene.name) -// thisScene.gkNode.removeConnections(to: [ prevScene.gkNode ], bidirectional: false) - thisScene.gkNode.connectedNodes.remove(prevScene.gkNode) + thisScene.mmNode.connectedNodes.remove(prevScene.mmNode) screen = prevScene } diff --git a/Sources/MMScreenGraph.swift b/Sources/MMScreenGraph.swift index 7395b69..8e08aef 100644 --- a/Sources/MMScreenGraph.swift +++ b/Sources/MMScreenGraph.swift @@ -189,7 +189,7 @@ public extension MMScreenGraph { * Typically, you'll do this in `TestCase.setUp()` */ func navigator(startingAt: String? = nil, file: String = #file, line: UInt = #line) -> MMNavigator { - buildGkGraph() + buildGraph() let userState = userStateType.init() guard let name = startingAt ?? userState.initialScreenState, let startingScreenState = namedScenes[name] as? MMScreenStateNode else { @@ -203,7 +203,7 @@ public extension MMScreenGraph { return MMNavigator(self, xcTest: xcTest, startingScreenState: startingScreenState, userState: userState) } - func buildGkGraph() { + func buildGraph() { if isReady { return } @@ -220,28 +220,28 @@ public extension MMScreenGraph { } } - // Construct all the GKGraphNodes, and add them to the GKGraph. + // Construct all the MMNodes, and add them to the rootNode. let graphNodes = namedScenes.values graphNodes.forEach({ - rootNode.connectedNodes.insert($0.gkNode) + rootNode.connectedNodes.insert($0.mmNode) }) graphNodes.forEach { graphNode in - nodedScenes[graphNode.gkNode] = graphNode + nodedScenes[graphNode.mmNode] = graphNode } // Now, we should have a good idea what the edges of the nodes look like, - // so we need to construct the GKGraph edges from it. + // so we need to construct the edges from it. graphNodes.forEach { graphNode in if let screenStateNode = graphNode as? MMScreenStateNode { - let gkNodes = screenStateNode.edges.keys.compactMap { self.namedScenes[$0]?.gkNode } as [MMNode] - gkNodes.forEach{ - screenStateNode.gkNode.connectedNodes.insert($0) + let mmNodes = screenStateNode.edges.keys.compactMap { self.namedScenes[$0]?.mmNode } as [MMNode] + mmNodes.forEach{ + screenStateNode.mmNode.connectedNodes.insert($0) } } else if let screenActionNode = graphNode as? MMScreenActionNode { if let destName = screenActionNode.nextNodeName, - let destGkNode = namedScenes[destName]?.gkNode { - screenActionNode.gkNode.connectedNodes.insert(destGkNode) + let destNode = namedScenes[destName]?.mmNode { + screenActionNode.mmNode.connectedNodes.insert(destNode) } } } @@ -250,14 +250,14 @@ public extension MMScreenGraph { } fileprivate func calculateConditionalEdges() -> [ConditionalEdge] { - buildGkGraph() + buildGraph() let screenStateNodes = namedScenes.values.compactMap { $0 as? MMScreenStateNode } return screenStateNodes.map { node -> [ConditionalEdge] in - let src = node.gkNode + let src = node.mmNode return node.edges.values.compactMap { edge -> ConditionalEdge? in guard let predicate = edge.predicate, - let dest = self.namedScenes[edge.destinationName]?.gkNode else { return nil } + let dest = self.namedScenes[edge.destinationName]?.mmNode else { return nil } return ConditionalEdge(src: src, dest: dest, predicate: predicate) } as [ConditionalEdge] diff --git a/Sources/MMScreenStateNode.swift b/Sources/MMScreenStateNode.swift index ca75387..4a784c0 100644 --- a/Sources/MMScreenStateNode.swift +++ b/Sources/MMScreenStateNode.swift @@ -56,7 +56,7 @@ public class MMScreenStateNode: MMGraphNode { fileprivate func addEdge(_ dest: String, by edge: Edge) { edges[dest] = edge - // by this time, we should've added all nodes in to the gkGraph. + // by this time, we should've added all nodes in to the rootNode. assert(map?.namedScenes[dest] != nil, "Destination node '\(dest)' has not been created anywhere") } From 97cf6e79b495078772c905260847badd1387a770 Mon Sep 17 00:00:00 2001 From: ampm Date: Fri, 7 Jun 2019 19:38:55 +0800 Subject: [PATCH 06/23] update hash method --- MappaMundi/aStar/MMNode.swift | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/MappaMundi/aStar/MMNode.swift b/MappaMundi/aStar/MMNode.swift index 42aa413..3af31c0 100644 --- a/MappaMundi/aStar/MMNode.swift +++ b/MappaMundi/aStar/MMNode.swift @@ -19,9 +19,10 @@ final class MMNode: GraphNode { return lhs.name == rhs.name } - var hashValue: Int { - return name.hashValue + func hash(into hasher: inout Hasher) { + hasher.combine(self.name) } + var connectedNodes = Set() func cost(to node: MMNode) -> Float { From c698a97fcf321628286d94c523655bb1786004bc Mon Sep 17 00:00:00 2001 From: ampm Date: Sat, 8 Jun 2019 22:43:02 +0800 Subject: [PATCH 07/23] integrate Dev1an/A-Star using Carthage --- Cartfile | 1 + MappaMundi.xcodeproj/project.pbxproj | 33 ++-- .../xcshareddata/IDEWorkspaceChecks.plist | 8 + MappaMundi/aStar/Node.swift | 144 ------------------ MappaMundi/aStar/SortedArray.swift | 66 -------- {MappaMundi/aStar => Sources}/MMNode.swift | 3 +- 6 files changed, 24 insertions(+), 231 deletions(-) create mode 100644 Cartfile create mode 100644 MappaMundi.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist delete mode 100644 MappaMundi/aStar/Node.swift delete mode 100644 MappaMundi/aStar/SortedArray.swift rename {MappaMundi/aStar => Sources}/MMNode.swift (98%) diff --git a/Cartfile b/Cartfile new file mode 100644 index 0000000..53a8a7b --- /dev/null +++ b/Cartfile @@ -0,0 +1 @@ +github "Dev1an/A-Star" ~> 2.0.0 \ No newline at end of file diff --git a/MappaMundi.xcodeproj/project.pbxproj b/MappaMundi.xcodeproj/project.pbxproj index 1fad71d..2ee9dd3 100644 --- a/MappaMundi.xcodeproj/project.pbxproj +++ b/MappaMundi.xcodeproj/project.pbxproj @@ -7,9 +7,8 @@ objects = { /* Begin PBXBuildFile section */ - 13DF8A2822AA667F004294F1 /* SortedArray.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13DF8A2622AA667F004294F1 /* SortedArray.swift */; }; - 13DF8A2922AA667F004294F1 /* Node.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13DF8A2722AA667F004294F1 /* Node.swift */; }; - 13DF8A2B22AA6E2E004294F1 /* MMNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13DF8A2A22AA6E2E004294F1 /* MMNode.swift */; }; + 13C70B8122AC003D008192BB /* AStar.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 13C70B8022AC003D008192BB /* AStar.framework */; }; + 13CE0A8322ABD430005CA5E3 /* MMNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13CE0A8222ABD42F005CA5E3 /* MMNode.swift */; }; 392FB353200E1D5D0014867E /* MMTestUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 392FB352200E1D5D0014867E /* MMTestUtils.swift */; }; 3962F2291FF6A6C100999008 /* Wait.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3962F2281FF6A6C100999008 /* Wait.swift */; }; 3962F22B1FF6A75800999008 /* ScreenGraphEdge.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3962F22A1FF6A75800999008 /* ScreenGraphEdge.swift */; }; @@ -55,9 +54,9 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ - 13DF8A2622AA667F004294F1 /* SortedArray.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SortedArray.swift; sourceTree = ""; }; - 13DF8A2722AA667F004294F1 /* Node.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Node.swift; sourceTree = ""; }; - 13DF8A2A22AA6E2E004294F1 /* MMNode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MMNode.swift; sourceTree = ""; }; + 13C70B8022AC003D008192BB /* AStar.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AStar.framework; path = Carthage/Build/iOS/AStar.framework; sourceTree = ""; }; + 13CE0A7E22ABD395005CA5E3 /* aStarMobile.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = aStarMobile.framework; path = Carthage/Build/iOS/aStarMobile.framework; sourceTree = ""; }; + 13CE0A8222ABD42F005CA5E3 /* MMNode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = MMNode.swift; path = Sources/MMNode.swift; sourceTree = SOURCE_ROOT; }; 392FB352200E1D5D0014867E /* MMTestUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MMTestUtils.swift; sourceTree = ""; }; 3962F2281FF6A6C100999008 /* Wait.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = Wait.swift; path = ../Sources/Wait.swift; sourceTree = ""; }; 3962F22A1FF6A75800999008 /* ScreenGraphEdge.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = ScreenGraphEdge.swift; path = ../Sources/ScreenGraphEdge.swift; sourceTree = ""; }; @@ -91,6 +90,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 13C70B8122AC003D008192BB /* AStar.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -112,20 +112,10 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ - 13DF8A2522AA666C004294F1 /* aStar */ = { - isa = PBXGroup; - children = ( - 13DF8A2A22AA6E2E004294F1 /* MMNode.swift */, - 13DF8A2722AA667F004294F1 /* Node.swift */, - 13DF8A2622AA667F004294F1 /* SortedArray.swift */, - ); - path = aStar; - sourceTree = ""; - }; 39AB5FE51FE4595000008FB3 /* MappaMundi */ = { isa = PBXGroup; children = ( - 13DF8A2522AA666C004294F1 /* aStar */, + 13CE0A8222ABD42F005CA5E3 /* MMNode.swift */, 39AB5FDB1FE455C700008FB3 /* MMScreenGraph.swift */, 397334481FF6AAA600C45ECE /* MMGraphNode.swift */, 392FB352200E1D5D0014867E /* MMTestUtils.swift */, @@ -145,6 +135,8 @@ 39AB5FED1FE459AD00008FB3 /* Frameworks */ = { isa = PBXGroup; children = ( + 13C70B8022AC003D008192BB /* AStar.framework */, + 13CE0A7E22ABD395005CA5E3 /* aStarMobile.framework */, 39AB5FEE1FE459AD00008FB3 /* XCTest.framework */, ); name = Frameworks; @@ -342,6 +334,7 @@ files = ( ); inputPaths = ( + "$(SRCROOT)/Carthage/Build/iOS/AStar.framework", ); outputPaths = ( ); @@ -357,10 +350,7 @@ buildActionMask = 2147483647; files = ( 397334491FF6AAA600C45ECE /* MMGraphNode.swift in Sources */, - 13DF8A2B22AA6E2E004294F1 /* MMNode.swift in Sources */, - 13DF8A2822AA667F004294F1 /* SortedArray.swift in Sources */, 39658ADC1FF699AA000544A2 /* MMScreenStateNode.swift in Sources */, - 13DF8A2922AA667F004294F1 /* Node.swift in Sources */, 39870718200D16710067B070 /* GraphRepresentation.swift in Sources */, 39658AE21FF6A065000544A2 /* MMUserState.swift in Sources */, 3962F22B1FF6A75800999008 /* ScreenGraphEdge.swift in Sources */, @@ -368,6 +358,7 @@ 39658ADE1FF69BCE000544A2 /* MMNavigator.swift in Sources */, 3962F2291FF6A6C100999008 /* Wait.swift in Sources */, 392FB353200E1D5D0014867E /* MMTestUtils.swift in Sources */, + 13CE0A8322ABD430005CA5E3 /* MMNode.swift in Sources */, 39AB5FEC1FE4597800008FB3 /* MMScreenGraph.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -436,6 +427,7 @@ FRAMEWORK_SEARCH_PATHS = ( "$(PLATFORM_DIR)/Developer/Library/Frameworks", "$(DEVELOPER_FRAMEWORKS_DIR)", + "$(PROJECT_DIR)/Carthage/Build/iOS", ); INFOPLIST_FILE = MappaMundi/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -471,6 +463,7 @@ FRAMEWORK_SEARCH_PATHS = ( "$(PLATFORM_DIR)/Developer/Library/Frameworks", "$(DEVELOPER_FRAMEWORKS_DIR)", + "$(PROJECT_DIR)/Carthage/Build/iOS", ); INFOPLIST_FILE = MappaMundi/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; diff --git a/MappaMundi.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/MappaMundi.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/MappaMundi.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/MappaMundi/aStar/Node.swift b/MappaMundi/aStar/Node.swift deleted file mode 100644 index 867060a..0000000 --- a/MappaMundi/aStar/Node.swift +++ /dev/null @@ -1,144 +0,0 @@ -// -// Node.swift -// aStar -// -// Created by Damiaan Dufaux on 19/08/16. -// Copyright © 2016 Damiaan Dufaux. All rights reserved. -// - - -/// This protocol declares the requirements for optimal pathfinding in a directed graph of nodes and implements the A* algorithm via an extension. -public protocol GraphNode: Hashable { - // MARK: Optimal pathfinding requirements - - /** - * List of other graph nodes that this node has an edge leading to. - */ - var connectedNodes: Set { get } - - - /// Returns the estimated heuristic cost to reach the indicated node from this node - /// - /// - Parameter node: the end point of the edge who's cost is to be estimated - /// - Returns: the heuristic cost - func estimatedCost(to node: Self) -> Float - - - /// - Parameter node: the destination node - /// - Returns: the actual cost to reach the indicated node from this node - func cost(to node: Self) -> Float - -} - - -class Step { - var node: Node - var previous: Step? - - var stepCost: Float - var goalCost: Float - - init(from start: Node, to destination: Node, goal: Node) { - node = destination - stepCost = start.cost(to: destination) - goalCost = destination.estimatedCost(to: goal) - } - - init(destination: Node, previous: Step, goal: Node) { - (node, self.previous) = (destination, previous) - stepCost = previous.stepCost + previous.node.cost(to: destination) - goalCost = destination.estimatedCost(to: goal) - } - - func cost() -> Float { - return stepCost + goalCost - } - -} - -extension Step: Hashable, Equatable, Comparable { - var hashValue: Int { - return node.hashValue - } - - static func ==(lhs: Step, rhs: Step) -> Bool { - return lhs.node == rhs.node - } - - public static func <(lhs: Step, rhs: Step) -> Bool { - return lhs.cost() < rhs.cost() - } - - public static func <=(lhs: Step, rhs: Step) -> Bool { - return lhs.cost() <= rhs.cost() - } - - public static func >=(lhs: Step, rhs: Step) -> Bool { - return lhs.cost() >= rhs.cost() - } - - public static func >(lhs: Step, rhs: Step) -> Bool { - return lhs.cost() > rhs.cost() - } - -} - - -extension GraphNode { - // MARK: A* Implementation - - /// Attempts to find the optimal path between this node and the indicated goal node. - /// If such a path exists, it is returned in start to end order. - /// If it doesn't exist, the array returned will be empty. - /// - /// - Parameter goalNode: the goal node of the pathfinding attempt - /// - Returns: the optimal path between this node and the indicated goal node - public func findPath(to goalNode: Self) -> [Self] { - var possibleSteps = [Step]() - var eliminatedNodes: Set = [self] - - for connectedNode in connectedNodes { - let step = Step(from: self, to: connectedNode, goal: goalNode) - possibleSteps.sortedInsert(newElement: step) - } - - var path = [self] - while !possibleSteps.isEmpty { - let step = possibleSteps.removeFirst() - if step.node == goalNode { - var cursor = step - path.insert(step.node, at: 1) - while let previous = cursor.previous { - cursor = previous - path.insert(previous.node, at: 1) - } - break - } - eliminatedNodes.insert(step.node) - let nextNodes = step.node.connectedNodes.subtracting(eliminatedNodes) - for node in nextNodes { - // TODO don't generate a step because in some cases it is never used - let nextStep = Step(destination: node, previous: step, goal: goalNode) - let index = possibleSteps.binarySearch(element: nextStep) - if index [Self] { - return startNode.findPath(to: self) - } -} diff --git a/MappaMundi/aStar/SortedArray.swift b/MappaMundi/aStar/SortedArray.swift deleted file mode 100644 index 9451bce..0000000 --- a/MappaMundi/aStar/SortedArray.swift +++ /dev/null @@ -1,66 +0,0 @@ -// -// SortedArray.swift -// aStar -// -// Created by Damiaan Dufaux on 21/08/16. -// Copyright © 2016 Damiaan Dufaux. All rights reserved. -// - -import Foundation - -extension Array { - - /// Finds such index N that predicate is true for all elements up to - /// but not including the index N, and is false for all elements - /// starting with index N. - /// Behavior is undefined if there is no such N. - func binarySearch(predicate: (Element) -> Bool) -> Int { - var low = startIndex - var high = endIndex - while low != high { - let mid = low.advanced(by: low.distance(to: high) / 2) - if predicate(self[mid]) { - low = mid.advanced(by: 1) - } else { - high = mid - } - } - return low - } - - mutating func sortedInsert(element: Element, predicate: (Element, Element) -> Bool) { - insert(element, at: binarySearch{predicate($0, element)} ) - } - -} - -extension Array where Element: Comparable { - /// Finds such index N that predicate is true for all elements up to - /// but not including the index N, and is false for all elements - /// starting with index N. - /// Behavior is undefined if there is no such N. - func binarySearch(element: Element) -> Int { - var low = startIndex - var high = endIndex - while low != high { - let mid = low.advanced(by: low.distance(to: high) / 2) - if self[mid] < element { - low = mid.advanced(by: 1) - } else { - high = mid - } - } - return low - } - - /// Inserts a new element in a sorted array. - /// - /// - Parameter newElement: The element to insert into the array. - /// - /// - Complexity: To find the index for the new element - /// it uses a binary search algorithm - - mutating func sortedInsert(newElement: Element) { - insert(newElement, at: binarySearch(element: newElement) ) - } -} diff --git a/MappaMundi/aStar/MMNode.swift b/Sources/MMNode.swift similarity index 98% rename from MappaMundi/aStar/MMNode.swift rename to Sources/MMNode.swift index 3af31c0..35c520f 100644 --- a/MappaMundi/aStar/MMNode.swift +++ b/Sources/MMNode.swift @@ -6,7 +6,8 @@ // Copyright © 2019 Mozilla Corporation. All rights reserved. // -import UIKit +import AStar + final class MMNode: GraphNode { var name: String? From 087946959958f7eb8aabaac32970e1db6c983880 Mon Sep 17 00:00:00 2001 From: ampm Date: Sat, 8 Jun 2019 22:44:46 +0800 Subject: [PATCH 08/23] update copyright header --- Sources/MMNode.swift | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/Sources/MMNode.swift b/Sources/MMNode.swift index 35c520f..a4854cf 100644 --- a/Sources/MMNode.swift +++ b/Sources/MMNode.swift @@ -1,10 +1,6 @@ -// -// MMNode.swift -// MappaMundi -// -// Created by Yang, Roy on 2019/6/7. -// Copyright © 2019 Mozilla Corporation. All rights reserved. -// +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ import AStar From 29fa707552f8add2130f9655c345d83c8e640b60 Mon Sep 17 00:00:00 2001 From: ampm Date: Sat, 8 Jun 2019 22:49:56 +0800 Subject: [PATCH 09/23] comment update --- Sources/MMScreenGraph.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/MMScreenGraph.swift b/Sources/MMScreenGraph.swift index 8e08aef..b34411c 100644 --- a/Sources/MMScreenGraph.swift +++ b/Sources/MMScreenGraph.swift @@ -12,7 +12,7 @@ * * The shared graph may also have other uses, such as generating screen shots for the App Store or L10n translators. * - * Under the hood, the MappaMundi is using aStar's path finding to do the heavy lifting. + * Under the hood, the MappaMundi is using A* path finding to do the heavy lifting. */ import Foundation From 4a5bc9cca57aaa921ea411aa6f971061a3769ac3 Mon Sep 17 00:00:00 2001 From: ampm Date: Sat, 8 Jun 2019 22:54:02 +0800 Subject: [PATCH 10/23] add Cartfile.resolved --- Cartfile.resolved | 1 + 1 file changed, 1 insertion(+) create mode 100644 Cartfile.resolved diff --git a/Cartfile.resolved b/Cartfile.resolved new file mode 100644 index 0000000..eed554a --- /dev/null +++ b/Cartfile.resolved @@ -0,0 +1 @@ +github "Dev1an/A-Star" "2.0.0" From 32d077bcb26b92f61e6ab8a7760311eb7986c856 Mon Sep 17 00:00:00 2001 From: ampm Date: Sat, 8 Jun 2019 23:07:11 +0800 Subject: [PATCH 11/23] check in the shared xcschemes --- .../xcshareddata/xcschemes/DemoApp.xcscheme | 91 +++++++++++++++++++ .../xcschemes/DemoUITests.xcscheme | 90 ++++++++++++++++++ 2 files changed, 181 insertions(+) create mode 100644 MappaMundi.xcodeproj/xcshareddata/xcschemes/DemoApp.xcscheme create mode 100644 MappaMundi.xcodeproj/xcshareddata/xcschemes/DemoUITests.xcscheme diff --git a/MappaMundi.xcodeproj/xcshareddata/xcschemes/DemoApp.xcscheme b/MappaMundi.xcodeproj/xcshareddata/xcschemes/DemoApp.xcscheme new file mode 100644 index 0000000..5ba0607 --- /dev/null +++ b/MappaMundi.xcodeproj/xcshareddata/xcschemes/DemoApp.xcscheme @@ -0,0 +1,91 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MappaMundi.xcodeproj/xcshareddata/xcschemes/DemoUITests.xcscheme b/MappaMundi.xcodeproj/xcshareddata/xcschemes/DemoUITests.xcscheme new file mode 100644 index 0000000..d3c30d8 --- /dev/null +++ b/MappaMundi.xcodeproj/xcshareddata/xcschemes/DemoUITests.xcscheme @@ -0,0 +1,90 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 07fff303ccac11ebe5b394effdc5e606ca443b67 Mon Sep 17 00:00:00 2001 From: ampm Date: Sat, 8 Jun 2019 23:44:28 +0800 Subject: [PATCH 12/23] stabilize UITests#testNavigatorActions() --- UITests/DemoMappaMundi.swift | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/UITests/DemoMappaMundi.swift b/UITests/DemoMappaMundi.swift index 78ab379..040d5de 100644 --- a/UITests/DemoMappaMundi.swift +++ b/UITests/DemoMappaMundi.swift @@ -75,10 +75,9 @@ func createGraph(with app: XCUIApplication, for test: XCTestCase) -> MMScreenGra // The edit screen has a delete action. We can also map.addScreenState(Screens.itemListEditing) { screenState in let table = app.tables.element(boundBy: 0) - func deleteFirst(_ userState: DemoAppUserState) { table.cells.element(boundBy: 0).buttons.element(boundBy: 0).tap() - table.buttons["Delete"].tap() + table.buttons["Delete"].firstMatch.tap() userState.numItems -= 1 } @@ -90,6 +89,8 @@ func createGraph(with app: XCUIApplication, for test: XCTestCase) -> MMScreenGra screenState.gesture(forAction: Actions.deleteAllItems) { userState in for _ in 0 ..< userState.numItems { deleteFirst(userState) + //waiting for the animation to finish + sleep(1) } } From 0f7f085c40b02a5404f0110d60a7fad770228929 Mon Sep 17 00:00:00 2001 From: ampm Date: Sun, 9 Jun 2019 00:54:31 +0800 Subject: [PATCH 13/23] correct the UITests --- UITests/DemoUITests.swift | 3 --- 1 file changed, 3 deletions(-) diff --git a/UITests/DemoUITests.swift b/UITests/DemoUITests.swift index 4669713..3d31caa 100644 --- a/UITests/DemoUITests.swift +++ b/UITests/DemoUITests.swift @@ -86,7 +86,6 @@ class DemoUITests: XCTestCase { func testConditionalEdges() { XCTAssertEqual(0, userState.numItems) - XCTAssertFalse(navigator.can(goto: Screens.itemDetail)) XCTAssertTrue(navigator.can(performAction: Actions.addItem)) navigator.performAction(Actions.addItem) @@ -97,12 +96,10 @@ class DemoUITests: XCTestCase { navigator.performAction(Actions.deleteAllItems) XCTAssertEqual(0, userState.numItems) - XCTAssertFalse(navigator.can(goto: Screens.itemDetail)) } func testNavigatorActions() { XCTAssertEqual(0, userState.numItems) - XCTAssertFalse(navigator.can(goto: Screens.itemDetail)) navigator.performAction(Actions.addItem) navigator.performAction(Actions.addItem) XCTAssertEqual(2, userState.numItems) From 6c799f208c80b54c413073397dd0333277ada9e9 Mon Sep 17 00:00:00 2001 From: ampm Date: Sun, 9 Jun 2019 01:15:26 +0800 Subject: [PATCH 14/23] apply AStar to func can(goto:) --- Sources/MMNavigator.swift | 3 ++- UITests/DemoUITests.swift | 1 - 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Sources/MMNavigator.swift b/Sources/MMNavigator.swift index c8b42ee..d361806 100644 --- a/Sources/MMNavigator.swift +++ b/Sources/MMNavigator.swift @@ -66,7 +66,8 @@ open class MMNavigator { guard let mmDest = map.namedScenes[nodeName]?.mmNode else { return false } - let mmPath = mmSrc.findPath(to: mmDest) + var mmPath = mmSrc.findPath(to: mmDest) + mmPath.removeFirst() return mmPath.count > 0 } diff --git a/UITests/DemoUITests.swift b/UITests/DemoUITests.swift index 3d31caa..55133e5 100644 --- a/UITests/DemoUITests.swift +++ b/UITests/DemoUITests.swift @@ -86,7 +86,6 @@ class DemoUITests: XCTestCase { func testConditionalEdges() { XCTAssertEqual(0, userState.numItems) - XCTAssertTrue(navigator.can(performAction: Actions.addItem)) navigator.performAction(Actions.addItem) XCTAssertEqual(1, userState.numItems) From 36c34d59e4b0ae0a1bea532bef642b5e52ba434e Mon Sep 17 00:00:00 2001 From: ampm Date: Sun, 9 Jun 2019 02:41:13 +0800 Subject: [PATCH 15/23] link Carthage's checkout source instead of copying framework --- MappaMundi.xcodeproj/project.pbxproj | 47 ++++++++++++++++------------ Sources/MMNode.swift | 2 -- 2 files changed, 27 insertions(+), 22 deletions(-) diff --git a/MappaMundi.xcodeproj/project.pbxproj b/MappaMundi.xcodeproj/project.pbxproj index 2ee9dd3..f1aa760 100644 --- a/MappaMundi.xcodeproj/project.pbxproj +++ b/MappaMundi.xcodeproj/project.pbxproj @@ -7,7 +7,8 @@ objects = { /* Begin PBXBuildFile section */ - 13C70B8122AC003D008192BB /* AStar.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 13C70B8022AC003D008192BB /* AStar.framework */; }; + 13AAA36722AC38290048E2C6 /* Node.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13AAA36522AC38290048E2C6 /* Node.swift */; }; + 13AAA36822AC38290048E2C6 /* SortedArray.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13AAA36622AC38290048E2C6 /* SortedArray.swift */; }; 13CE0A8322ABD430005CA5E3 /* MMNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13CE0A8222ABD42F005CA5E3 /* MMNode.swift */; }; 392FB353200E1D5D0014867E /* MMTestUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 392FB352200E1D5D0014867E /* MMTestUtils.swift */; }; 3962F2291FF6A6C100999008 /* Wait.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3962F2281FF6A6C100999008 /* Wait.swift */; }; @@ -41,6 +42,15 @@ /* End PBXContainerItemProxy section */ /* Begin PBXCopyFilesBuildPhase section */ + 13AAA2CC22AC37CA0048E2C6 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 16; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; D8A18F461FE463C9009DA463 /* Embed Frameworks */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; @@ -54,6 +64,8 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 13AAA36522AC38290048E2C6 /* Node.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Node.swift; sourceTree = ""; }; + 13AAA36622AC38290048E2C6 /* SortedArray.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SortedArray.swift; sourceTree = ""; }; 13C70B8022AC003D008192BB /* AStar.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AStar.framework; path = Carthage/Build/iOS/AStar.framework; sourceTree = ""; }; 13CE0A7E22ABD395005CA5E3 /* aStarMobile.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = aStarMobile.framework; path = Carthage/Build/iOS/aStarMobile.framework; sourceTree = ""; }; 13CE0A8222ABD42F005CA5E3 /* MMNode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = MMNode.swift; path = Sources/MMNode.swift; sourceTree = SOURCE_ROOT; }; @@ -90,7 +102,6 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 13C70B8122AC003D008192BB /* AStar.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -112,9 +123,20 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 13AAA36422AC38290048E2C6 /* AStar */ = { + isa = PBXGroup; + children = ( + 13AAA36522AC38290048E2C6 /* Node.swift */, + 13AAA36622AC38290048E2C6 /* SortedArray.swift */, + ); + name = AStar; + path = "Carthage/Checkouts/A-Star/Sources/AStar"; + sourceTree = SOURCE_ROOT; + }; 39AB5FE51FE4595000008FB3 /* MappaMundi */ = { isa = PBXGroup; children = ( + 13AAA36422AC38290048E2C6 /* AStar */, 13CE0A8222ABD42F005CA5E3 /* MMNode.swift */, 39AB5FDB1FE455C700008FB3 /* MMScreenGraph.swift */, 397334481FF6AAA600C45ECE /* MMGraphNode.swift */, @@ -208,7 +230,7 @@ 39AB5FE01FE4595000008FB3 /* Frameworks */, 39AB5FE11FE4595000008FB3 /* Headers */, 39AB5FE21FE4595000008FB3 /* Resources */, - D81FAF8B1FE46A1D00863A8A /* ShellScript */, + 13AAA2CC22AC37CA0048E2C6 /* CopyFiles */, ); buildRules = ( ); @@ -327,23 +349,6 @@ }; /* End PBXResourcesBuildPhase section */ -/* Begin PBXShellScriptBuildPhase section */ - D81FAF8B1FE46A1D00863A8A /* ShellScript */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - "$(SRCROOT)/Carthage/Build/iOS/AStar.framework", - ); - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "carthage copy-frameworks\n"; - }; -/* End PBXShellScriptBuildPhase section */ - /* Begin PBXSourcesBuildPhase section */ 39AB5FDF1FE4595000008FB3 /* Sources */ = { isa = PBXSourcesBuildPhase; @@ -352,10 +357,12 @@ 397334491FF6AAA600C45ECE /* MMGraphNode.swift in Sources */, 39658ADC1FF699AA000544A2 /* MMScreenStateNode.swift in Sources */, 39870718200D16710067B070 /* GraphRepresentation.swift in Sources */, + 13AAA36722AC38290048E2C6 /* Node.swift in Sources */, 39658AE21FF6A065000544A2 /* MMUserState.swift in Sources */, 3962F22B1FF6A75800999008 /* ScreenGraphEdge.swift in Sources */, 39658AE01FF69F62000544A2 /* MMScreenActionNode.swift in Sources */, 39658ADE1FF69BCE000544A2 /* MMNavigator.swift in Sources */, + 13AAA36822AC38290048E2C6 /* SortedArray.swift in Sources */, 3962F2291FF6A6C100999008 /* Wait.swift in Sources */, 392FB353200E1D5D0014867E /* MMTestUtils.swift in Sources */, 13CE0A8322ABD430005CA5E3 /* MMNode.swift in Sources */, diff --git a/Sources/MMNode.swift b/Sources/MMNode.swift index a4854cf..61c633b 100644 --- a/Sources/MMNode.swift +++ b/Sources/MMNode.swift @@ -2,8 +2,6 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -import AStar - final class MMNode: GraphNode { var name: String? From ab5c83a7212f12e8b7ddd6c804c395bfe57494c7 Mon Sep 17 00:00:00 2001 From: ampm Date: Sun, 9 Jun 2019 11:33:12 +0800 Subject: [PATCH 16/23] project.phxproj file updated --- MappaMundi.xcodeproj/project.pbxproj | 3 --- 1 file changed, 3 deletions(-) diff --git a/MappaMundi.xcodeproj/project.pbxproj b/MappaMundi.xcodeproj/project.pbxproj index f1aa760..960b87b 100644 --- a/MappaMundi.xcodeproj/project.pbxproj +++ b/MappaMundi.xcodeproj/project.pbxproj @@ -66,7 +66,6 @@ /* Begin PBXFileReference section */ 13AAA36522AC38290048E2C6 /* Node.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Node.swift; sourceTree = ""; }; 13AAA36622AC38290048E2C6 /* SortedArray.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SortedArray.swift; sourceTree = ""; }; - 13C70B8022AC003D008192BB /* AStar.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AStar.framework; path = Carthage/Build/iOS/AStar.framework; sourceTree = ""; }; 13CE0A7E22ABD395005CA5E3 /* aStarMobile.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = aStarMobile.framework; path = Carthage/Build/iOS/aStarMobile.framework; sourceTree = ""; }; 13CE0A8222ABD42F005CA5E3 /* MMNode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = MMNode.swift; path = Sources/MMNode.swift; sourceTree = SOURCE_ROOT; }; 392FB352200E1D5D0014867E /* MMTestUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MMTestUtils.swift; sourceTree = ""; }; @@ -157,8 +156,6 @@ 39AB5FED1FE459AD00008FB3 /* Frameworks */ = { isa = PBXGroup; children = ( - 13C70B8022AC003D008192BB /* AStar.framework */, - 13CE0A7E22ABD395005CA5E3 /* aStarMobile.framework */, 39AB5FEE1FE459AD00008FB3 /* XCTest.framework */, ); name = Frameworks; From a82c90fb4d6902e8e15e26d5311f38b1b7d8198d Mon Sep 17 00:00:00 2001 From: ampm Date: Sun, 9 Jun 2019 11:35:52 +0800 Subject: [PATCH 17/23] project.phxproj file updated --- MappaMundi.xcodeproj/project.pbxproj | 1 - 1 file changed, 1 deletion(-) diff --git a/MappaMundi.xcodeproj/project.pbxproj b/MappaMundi.xcodeproj/project.pbxproj index 960b87b..bf79b66 100644 --- a/MappaMundi.xcodeproj/project.pbxproj +++ b/MappaMundi.xcodeproj/project.pbxproj @@ -66,7 +66,6 @@ /* Begin PBXFileReference section */ 13AAA36522AC38290048E2C6 /* Node.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Node.swift; sourceTree = ""; }; 13AAA36622AC38290048E2C6 /* SortedArray.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SortedArray.swift; sourceTree = ""; }; - 13CE0A7E22ABD395005CA5E3 /* aStarMobile.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = aStarMobile.framework; path = Carthage/Build/iOS/aStarMobile.framework; sourceTree = ""; }; 13CE0A8222ABD42F005CA5E3 /* MMNode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = MMNode.swift; path = Sources/MMNode.swift; sourceTree = SOURCE_ROOT; }; 392FB352200E1D5D0014867E /* MMTestUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MMTestUtils.swift; sourceTree = ""; }; 3962F2281FF6A6C100999008 /* Wait.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = Wait.swift; path = ../Sources/Wait.swift; sourceTree = ""; }; From 7ff05cac7732597349588e317f207f9a1eba982a Mon Sep 17 00:00:00 2001 From: ampm Date: Sun, 9 Jun 2019 16:15:24 +0800 Subject: [PATCH 18/23] copy AStar source code --- Dependencies/AStar/Node.swift | 143 +++++++++++++++++++++++++++ Dependencies/AStar/SortedArray.swift | 66 +++++++++++++ MappaMundi.xcodeproj/project.pbxproj | 31 +++--- 3 files changed, 228 insertions(+), 12 deletions(-) create mode 100644 Dependencies/AStar/Node.swift create mode 100644 Dependencies/AStar/SortedArray.swift diff --git a/Dependencies/AStar/Node.swift b/Dependencies/AStar/Node.swift new file mode 100644 index 0000000..44b6479 --- /dev/null +++ b/Dependencies/AStar/Node.swift @@ -0,0 +1,143 @@ +// +// Node.swift +// AStar +// +// Created by Damiaan Dufaux on 19/08/16. +// Copyright © 2016 Damiaan Dufaux. All rights reserved. +// + + +/// This protocol declares the requirements for optimal pathfinding in a directed graph of nodes and implements the A* algorithm via an extension. +public protocol GraphNode: Hashable { + // MARK: Optimal pathfinding requirements + + /** + * List of other graph nodes that this node has an edge leading to. + */ + var connectedNodes: Set { get } + + + /// Returns the estimated heuristic cost to reach the indicated node from this node + /// + /// - Parameter node: the end point of the edge who's cost is to be estimated + /// - Returns: the heuristic cost + func estimatedCost(to node: Self) -> Float + + + /// - Parameter node: the destination node + /// - Returns: the actual cost to reach the indicated node from this node + func cost(to node: Self) -> Float + +} + +class Step { + var node: Node + var previous: Step? + + var stepCost: Float + var goalCost: Float + + init(from start: Node, to destination: Node, goal: Node) { + node = destination + stepCost = start.cost(to: destination) + goalCost = destination.estimatedCost(to: goal) + } + + init(destination: Node, previous: Step, goal: Node) { + (node, self.previous) = (destination, previous) + stepCost = previous.stepCost + previous.node.cost(to: destination) + goalCost = destination.estimatedCost(to: goal) + } + + func cost() -> Float { + return stepCost + goalCost + } + +} + +extension Step: Hashable, Equatable, Comparable { + func hash(into hasher: inout Hasher) { + hasher.combine(node) + } + + static func ==(lhs: Step, rhs: Step) -> Bool { + return lhs.node == rhs.node + } + + public static func <(lhs: Step, rhs: Step) -> Bool { + return lhs.cost() < rhs.cost() + } + + public static func <=(lhs: Step, rhs: Step) -> Bool { + return lhs.cost() <= rhs.cost() + } + + public static func >=(lhs: Step, rhs: Step) -> Bool { + return lhs.cost() >= rhs.cost() + } + + public static func >(lhs: Step, rhs: Step) -> Bool { + return lhs.cost() > rhs.cost() + } + +} + + +extension GraphNode { + // MARK: A* Implementation + + /// Attempts to find the optimal path between this node and the indicated goal node. + /// If such a path exists, it is returned in start to end order. + /// If it doesn't exist, the array returned will be empty. + /// + /// - Parameter goalNode: the goal node of the pathfinding attempt + /// - Returns: the optimal path between this node and the indicated goal node + public func findPath(to goalNode: Self) -> [Self] { + var possibleSteps = [Step]() + var eliminatedNodes: Set = [self] + + for connectedNode in connectedNodes { + let step = Step(from: self, to: connectedNode, goal: goalNode) + possibleSteps.sortedInsert(newElement: step) + } + + var path = [self] + while !possibleSteps.isEmpty { + let step = possibleSteps.removeFirst() + if step.node == goalNode { + var cursor = step + path.insert(step.node, at: 1) + while let previous = cursor.previous { + cursor = previous + path.insert(previous.node, at: 1) + } + break + } + eliminatedNodes.insert(step.node) + let nextNodes = step.node.connectedNodes.subtracting(eliminatedNodes) + for node in nextNodes { + // TODO don't generate a step because in some cases it is never used + let nextStep = Step(destination: node, previous: step, goal: goalNode) + let index = possibleSteps.binarySearch(element: nextStep) + if index [Self] { + return startNode.findPath(to: self) + } +} diff --git a/Dependencies/AStar/SortedArray.swift b/Dependencies/AStar/SortedArray.swift new file mode 100644 index 0000000..eaa992f --- /dev/null +++ b/Dependencies/AStar/SortedArray.swift @@ -0,0 +1,66 @@ +// +// SortedArray.swift +// AStar +// +// Created by Damiaan Dufaux on 21/08/16. +// Copyright © 2016 Damiaan Dufaux. All rights reserved. +// + +import Foundation + +extension Array { + + /// Finds such index N that predicate is true for all elements up to + /// but not including the index N, and is false for all elements + /// starting with index N. + /// Behavior is undefined if there is no such N. + func binarySearch(predicate: (Element) -> Bool) -> Int { + var low = startIndex + var high = endIndex + while low != high { + let mid = low.advanced(by: low.distance(to: high) / 2) + if predicate(self[mid]) { + low = mid.advanced(by: 1) + } else { + high = mid + } + } + return low + } + + mutating func sortedInsert(element: Element, predicate: (Element, Element) -> Bool) { + insert(element, at: binarySearch{predicate($0, element)} ) + } + +} + +extension Array where Element: Comparable { + /// Finds such index N that predicate is true for all elements up to + /// but not including the index N, and is false for all elements + /// starting with index N. + /// Behavior is undefined if there is no such N. + func binarySearch(element: Element) -> Int { + var low = startIndex + var high = endIndex + while low != high { + let mid = low.advanced(by: low.distance(to: high) / 2) + if self[mid] < element { + low = mid.advanced(by: 1) + } else { + high = mid + } + } + return low + } + + /// Inserts a new element in a sorted array. + /// + /// - Parameter newElement: The element to insert into the array. + /// + /// - Complexity: To find the index for the new element + /// it uses a binary search algorithm + + mutating func sortedInsert(newElement: Element) { + insert(newElement, at: binarySearch(element: newElement) ) + } +} diff --git a/MappaMundi.xcodeproj/project.pbxproj b/MappaMundi.xcodeproj/project.pbxproj index bf79b66..19172e8 100644 --- a/MappaMundi.xcodeproj/project.pbxproj +++ b/MappaMundi.xcodeproj/project.pbxproj @@ -7,9 +7,9 @@ objects = { /* Begin PBXBuildFile section */ - 13AAA36722AC38290048E2C6 /* Node.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13AAA36522AC38290048E2C6 /* Node.swift */; }; - 13AAA36822AC38290048E2C6 /* SortedArray.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13AAA36622AC38290048E2C6 /* SortedArray.swift */; }; 13CE0A8322ABD430005CA5E3 /* MMNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13CE0A8222ABD42F005CA5E3 /* MMNode.swift */; }; + 13E39C7522ACF74A0015C33D /* Node.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13E39C7122ACF7270015C33D /* Node.swift */; }; + 13E39C7622ACF74D0015C33D /* SortedArray.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13E39C7222ACF7270015C33D /* SortedArray.swift */; }; 392FB353200E1D5D0014867E /* MMTestUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 392FB352200E1D5D0014867E /* MMTestUtils.swift */; }; 3962F2291FF6A6C100999008 /* Wait.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3962F2281FF6A6C100999008 /* Wait.swift */; }; 3962F22B1FF6A75800999008 /* ScreenGraphEdge.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3962F22A1FF6A75800999008 /* ScreenGraphEdge.swift */; }; @@ -64,9 +64,9 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ - 13AAA36522AC38290048E2C6 /* Node.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Node.swift; sourceTree = ""; }; - 13AAA36622AC38290048E2C6 /* SortedArray.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SortedArray.swift; sourceTree = ""; }; 13CE0A8222ABD42F005CA5E3 /* MMNode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = MMNode.swift; path = Sources/MMNode.swift; sourceTree = SOURCE_ROOT; }; + 13E39C7122ACF7270015C33D /* Node.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Node.swift; sourceTree = ""; }; + 13E39C7222ACF7270015C33D /* SortedArray.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SortedArray.swift; sourceTree = ""; }; 392FB352200E1D5D0014867E /* MMTestUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MMTestUtils.swift; sourceTree = ""; }; 3962F2281FF6A6C100999008 /* Wait.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = Wait.swift; path = ../Sources/Wait.swift; sourceTree = ""; }; 3962F22A1FF6A75800999008 /* ScreenGraphEdge.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = ScreenGraphEdge.swift; path = ../Sources/ScreenGraphEdge.swift; sourceTree = ""; }; @@ -121,20 +121,26 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ - 13AAA36422AC38290048E2C6 /* AStar */ = { + 13E39C6F22ACF7270015C33D /* Dependencies */ = { isa = PBXGroup; children = ( - 13AAA36522AC38290048E2C6 /* Node.swift */, - 13AAA36622AC38290048E2C6 /* SortedArray.swift */, + 13E39C7022ACF7270015C33D /* AStar */, ); - name = AStar; - path = "Carthage/Checkouts/A-Star/Sources/AStar"; + path = Dependencies; sourceTree = SOURCE_ROOT; }; + 13E39C7022ACF7270015C33D /* AStar */ = { + isa = PBXGroup; + children = ( + 13E39C7122ACF7270015C33D /* Node.swift */, + 13E39C7222ACF7270015C33D /* SortedArray.swift */, + ); + path = AStar; + sourceTree = ""; + }; 39AB5FE51FE4595000008FB3 /* MappaMundi */ = { isa = PBXGroup; children = ( - 13AAA36422AC38290048E2C6 /* AStar */, 13CE0A8222ABD42F005CA5E3 /* MMNode.swift */, 39AB5FDB1FE455C700008FB3 /* MMScreenGraph.swift */, 397334481FF6AAA600C45ECE /* MMGraphNode.swift */, @@ -166,6 +172,7 @@ 39BAD55D1FE19B9E00524FB7 /* DemoApp */, 39BAD5741FE19B9E00524FB7 /* UITests */, 39AB5FE51FE4595000008FB3 /* MappaMundi */, + 13E39C6F22ACF7270015C33D /* Dependencies */, 39BAD55C1FE19B9D00524FB7 /* Products */, 39AB5FED1FE459AD00008FB3 /* Frameworks */, ); @@ -353,12 +360,12 @@ 397334491FF6AAA600C45ECE /* MMGraphNode.swift in Sources */, 39658ADC1FF699AA000544A2 /* MMScreenStateNode.swift in Sources */, 39870718200D16710067B070 /* GraphRepresentation.swift in Sources */, - 13AAA36722AC38290048E2C6 /* Node.swift in Sources */, + 13E39C7522ACF74A0015C33D /* Node.swift in Sources */, 39658AE21FF6A065000544A2 /* MMUserState.swift in Sources */, 3962F22B1FF6A75800999008 /* ScreenGraphEdge.swift in Sources */, 39658AE01FF69F62000544A2 /* MMScreenActionNode.swift in Sources */, 39658ADE1FF69BCE000544A2 /* MMNavigator.swift in Sources */, - 13AAA36822AC38290048E2C6 /* SortedArray.swift in Sources */, + 13E39C7622ACF74D0015C33D /* SortedArray.swift in Sources */, 3962F2291FF6A6C100999008 /* Wait.swift in Sources */, 392FB353200E1D5D0014867E /* MMTestUtils.swift in Sources */, 13CE0A8322ABD430005CA5E3 /* MMNode.swift in Sources */, From af0d8344d4d52bb64a8654a327b8bf01327bfc77 Mon Sep 17 00:00:00 2001 From: ampm Date: Sun, 9 Jun 2019 19:40:26 +0800 Subject: [PATCH 19/23] Revert: correct the UITests --- UITests/DemoUITests.swift | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/UITests/DemoUITests.swift b/UITests/DemoUITests.swift index 55133e5..4669713 100644 --- a/UITests/DemoUITests.swift +++ b/UITests/DemoUITests.swift @@ -86,6 +86,8 @@ class DemoUITests: XCTestCase { func testConditionalEdges() { XCTAssertEqual(0, userState.numItems) + XCTAssertFalse(navigator.can(goto: Screens.itemDetail)) + XCTAssertTrue(navigator.can(performAction: Actions.addItem)) navigator.performAction(Actions.addItem) XCTAssertEqual(1, userState.numItems) @@ -95,10 +97,12 @@ class DemoUITests: XCTestCase { navigator.performAction(Actions.deleteAllItems) XCTAssertEqual(0, userState.numItems) + XCTAssertFalse(navigator.can(goto: Screens.itemDetail)) } func testNavigatorActions() { XCTAssertEqual(0, userState.numItems) + XCTAssertFalse(navigator.can(goto: Screens.itemDetail)) navigator.performAction(Actions.addItem) navigator.performAction(Actions.addItem) XCTAssertEqual(2, userState.numItems) From 2d0cccc2e59e549a78bb5d5c279d9a457a9abffb Mon Sep 17 00:00:00 2001 From: ampm Date: Sun, 9 Jun 2019 19:42:46 +0800 Subject: [PATCH 20/23] correct the conditional edge --- Sources/MMNavigator.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/MMNavigator.swift b/Sources/MMNavigator.swift index d361806..d0115e1 100644 --- a/Sources/MMNavigator.swift +++ b/Sources/MMNavigator.swift @@ -420,7 +420,7 @@ fileprivate extension MMNavigator { if edge.isOpen { edge.src.connectedNodes.insert(edge.dest) } else { - edge.src.connectedNodes.insert(edge.dest) + edge.src.connectedNodes.remove(edge.dest) } } return graphChanged From af367521ffcad12088178a8f152e8214ce12b0d0 Mon Sep 17 00:00:00 2001 From: ampm Date: Sun, 9 Jun 2019 20:53:29 +0800 Subject: [PATCH 21/23] revert func can(goto:) --- Dependencies/AStar/Node.swift | 42 +++++++++++++++++++---------------- Sources/MMNavigator.swift | 3 +-- 2 files changed, 24 insertions(+), 21 deletions(-) diff --git a/Dependencies/AStar/Node.swift b/Dependencies/AStar/Node.swift index 44b6479..afe450b 100644 --- a/Dependencies/AStar/Node.swift +++ b/Dependencies/AStar/Node.swift @@ -9,25 +9,25 @@ /// This protocol declares the requirements for optimal pathfinding in a directed graph of nodes and implements the A* algorithm via an extension. public protocol GraphNode: Hashable { - // MARK: Optimal pathfinding requirements - + // MARK: Optimal pathfinding requirements + /** * List of other graph nodes that this node has an edge leading to. */ var connectedNodes: Set { get } - - + + /// Returns the estimated heuristic cost to reach the indicated node from this node /// /// - Parameter node: the end point of the edge who's cost is to be estimated /// - Returns: the heuristic cost func estimatedCost(to node: Self) -> Float - + /// - Parameter node: the destination node /// - Returns: the actual cost to reach the indicated node from this node func cost(to node: Self) -> Float - + } class Step { @@ -56,9 +56,9 @@ class Step { } extension Step: Hashable, Equatable, Comparable { - func hash(into hasher: inout Hasher) { - hasher.combine(node) - } + func hash(into hasher: inout Hasher) { + hasher.combine(node) + } static func ==(lhs: Step, rhs: Step) -> Bool { return lhs.node == rhs.node @@ -79,16 +79,16 @@ extension Step: Hashable, Equatable, Comparable { public static func >(lhs: Step, rhs: Step) -> Bool { return lhs.cost() > rhs.cost() } - + } extension GraphNode { - // MARK: A* Implementation - + // MARK: A* Implementation + /// Attempts to find the optimal path between this node and the indicated goal node. - /// If such a path exists, it is returned in start to end order. - /// If it doesn't exist, the array returned will be empty. + /// If such a path exists, it is returned in start to end order. + /// If it doesn't exist, the array returned will be empty. /// /// - Parameter goalNode: the goal node of the pathfinding attempt /// - Returns: the optimal path between this node and the indicated goal node @@ -101,15 +101,15 @@ extension GraphNode { possibleSteps.sortedInsert(newElement: step) } - var path = [self] + var path = [Self]() while !possibleSteps.isEmpty { let step = possibleSteps.removeFirst() if step.node == goalNode { var cursor = step - path.insert(step.node, at: 1) + path.insert(step.node, at: 0) while let previous = cursor.previous { cursor = previous - path.insert(previous.node, at: 1) + path.insert(previous.node, at: 0) } break } @@ -129,10 +129,14 @@ extension GraphNode { } } + if path.count > 0 || goalNode == self{ + path.insert(self, at: 0) + } + return path } - - + + /// As with findPathToNode: except this node is the goal node and a startNode is specified /// /// - Parameter startNode: the start node of the pathfinding attempt diff --git a/Sources/MMNavigator.swift b/Sources/MMNavigator.swift index d0115e1..6e849b6 100644 --- a/Sources/MMNavigator.swift +++ b/Sources/MMNavigator.swift @@ -66,8 +66,7 @@ open class MMNavigator { guard let mmDest = map.namedScenes[nodeName]?.mmNode else { return false } - var mmPath = mmSrc.findPath(to: mmDest) - mmPath.removeFirst() + let mmPath = mmSrc.findPath(to: mmDest) return mmPath.count > 0 } From d4b13c6b4b457c73eea0f19affd987100f767cfb Mon Sep 17 00:00:00 2001 From: ampm Date: Sun, 30 Jun 2019 14:46:20 +0800 Subject: [PATCH 22/23] ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO --- MappaMundi.xcodeproj/project.pbxproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MappaMundi.xcodeproj/project.pbxproj b/MappaMundi.xcodeproj/project.pbxproj index 19172e8..35a17af 100644 --- a/MappaMundi.xcodeproj/project.pbxproj +++ b/MappaMundi.xcodeproj/project.pbxproj @@ -425,7 +425,7 @@ 39AB5FEA1FE4595000008FB3 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO; CODE_SIGN_IDENTITY = ""; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; @@ -460,7 +460,7 @@ 39AB5FEB1FE4595000008FB3 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO; CODE_SIGN_IDENTITY = ""; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; From 617c3925eedf107e9fd942fd2744cc5c4d23bd0b Mon Sep 17 00:00:00 2001 From: ampm Date: Mon, 1 Jul 2019 11:15:37 +0800 Subject: [PATCH 23/23] Integrate AStar with Carthage --- Cartfile | 2 +- Cartfile.resolved | 2 +- Dependencies/AStar/Node.swift | 147 ------------ Dependencies/AStar/SortedArray.swift | 66 ------ MappaMundi.xcodeproj/project.pbxproj | 214 +++++++++++------- .../xcschemes/MappaMundi.xcscheme | 26 +-- MappaMundi/Info.plist | 2 - Sources/MMNavigator.swift | 1 + Sources/MMNode.swift | 1 + 9 files changed, 153 insertions(+), 308 deletions(-) delete mode 100644 Dependencies/AStar/Node.swift delete mode 100644 Dependencies/AStar/SortedArray.swift diff --git a/Cartfile b/Cartfile index 53a8a7b..9b0c347 100644 --- a/Cartfile +++ b/Cartfile @@ -1 +1 @@ -github "Dev1an/A-Star" ~> 2.0.0 \ No newline at end of file +github "Dev1an/A-Star" ~> 3.0.0-beta-1 diff --git a/Cartfile.resolved b/Cartfile.resolved index eed554a..5e610f1 100644 --- a/Cartfile.resolved +++ b/Cartfile.resolved @@ -1 +1 @@ -github "Dev1an/A-Star" "2.0.0" +github "Dev1an/A-Star" "3.0.0-beta-1" diff --git a/Dependencies/AStar/Node.swift b/Dependencies/AStar/Node.swift deleted file mode 100644 index afe450b..0000000 --- a/Dependencies/AStar/Node.swift +++ /dev/null @@ -1,147 +0,0 @@ -// -// Node.swift -// AStar -// -// Created by Damiaan Dufaux on 19/08/16. -// Copyright © 2016 Damiaan Dufaux. All rights reserved. -// - - -/// This protocol declares the requirements for optimal pathfinding in a directed graph of nodes and implements the A* algorithm via an extension. -public protocol GraphNode: Hashable { - // MARK: Optimal pathfinding requirements - - /** - * List of other graph nodes that this node has an edge leading to. - */ - var connectedNodes: Set { get } - - - /// Returns the estimated heuristic cost to reach the indicated node from this node - /// - /// - Parameter node: the end point of the edge who's cost is to be estimated - /// - Returns: the heuristic cost - func estimatedCost(to node: Self) -> Float - - - /// - Parameter node: the destination node - /// - Returns: the actual cost to reach the indicated node from this node - func cost(to node: Self) -> Float - -} - -class Step { - var node: Node - var previous: Step? - - var stepCost: Float - var goalCost: Float - - init(from start: Node, to destination: Node, goal: Node) { - node = destination - stepCost = start.cost(to: destination) - goalCost = destination.estimatedCost(to: goal) - } - - init(destination: Node, previous: Step, goal: Node) { - (node, self.previous) = (destination, previous) - stepCost = previous.stepCost + previous.node.cost(to: destination) - goalCost = destination.estimatedCost(to: goal) - } - - func cost() -> Float { - return stepCost + goalCost - } - -} - -extension Step: Hashable, Equatable, Comparable { - func hash(into hasher: inout Hasher) { - hasher.combine(node) - } - - static func ==(lhs: Step, rhs: Step) -> Bool { - return lhs.node == rhs.node - } - - public static func <(lhs: Step, rhs: Step) -> Bool { - return lhs.cost() < rhs.cost() - } - - public static func <=(lhs: Step, rhs: Step) -> Bool { - return lhs.cost() <= rhs.cost() - } - - public static func >=(lhs: Step, rhs: Step) -> Bool { - return lhs.cost() >= rhs.cost() - } - - public static func >(lhs: Step, rhs: Step) -> Bool { - return lhs.cost() > rhs.cost() - } - -} - - -extension GraphNode { - // MARK: A* Implementation - - /// Attempts to find the optimal path between this node and the indicated goal node. - /// If such a path exists, it is returned in start to end order. - /// If it doesn't exist, the array returned will be empty. - /// - /// - Parameter goalNode: the goal node of the pathfinding attempt - /// - Returns: the optimal path between this node and the indicated goal node - public func findPath(to goalNode: Self) -> [Self] { - var possibleSteps = [Step]() - var eliminatedNodes: Set = [self] - - for connectedNode in connectedNodes { - let step = Step(from: self, to: connectedNode, goal: goalNode) - possibleSteps.sortedInsert(newElement: step) - } - - var path = [Self]() - while !possibleSteps.isEmpty { - let step = possibleSteps.removeFirst() - if step.node == goalNode { - var cursor = step - path.insert(step.node, at: 0) - while let previous = cursor.previous { - cursor = previous - path.insert(previous.node, at: 0) - } - break - } - eliminatedNodes.insert(step.node) - let nextNodes = step.node.connectedNodes.subtracting(eliminatedNodes) - for node in nextNodes { - // TODO don't generate a step because in some cases it is never used - let nextStep = Step(destination: node, previous: step, goal: goalNode) - let index = possibleSteps.binarySearch(element: nextStep) - if index 0 || goalNode == self{ - path.insert(self, at: 0) - } - - return path - } - - - /// As with findPathToNode: except this node is the goal node and a startNode is specified - /// - /// - Parameter startNode: the start node of the pathfinding attempt - /// - Returns: the optimal path between the indicated start node and this node - public func findPath(from startNode: Self) -> [Self] { - return startNode.findPath(to: self) - } -} diff --git a/Dependencies/AStar/SortedArray.swift b/Dependencies/AStar/SortedArray.swift deleted file mode 100644 index eaa992f..0000000 --- a/Dependencies/AStar/SortedArray.swift +++ /dev/null @@ -1,66 +0,0 @@ -// -// SortedArray.swift -// AStar -// -// Created by Damiaan Dufaux on 21/08/16. -// Copyright © 2016 Damiaan Dufaux. All rights reserved. -// - -import Foundation - -extension Array { - - /// Finds such index N that predicate is true for all elements up to - /// but not including the index N, and is false for all elements - /// starting with index N. - /// Behavior is undefined if there is no such N. - func binarySearch(predicate: (Element) -> Bool) -> Int { - var low = startIndex - var high = endIndex - while low != high { - let mid = low.advanced(by: low.distance(to: high) / 2) - if predicate(self[mid]) { - low = mid.advanced(by: 1) - } else { - high = mid - } - } - return low - } - - mutating func sortedInsert(element: Element, predicate: (Element, Element) -> Bool) { - insert(element, at: binarySearch{predicate($0, element)} ) - } - -} - -extension Array where Element: Comparable { - /// Finds such index N that predicate is true for all elements up to - /// but not including the index N, and is false for all elements - /// starting with index N. - /// Behavior is undefined if there is no such N. - func binarySearch(element: Element) -> Int { - var low = startIndex - var high = endIndex - while low != high { - let mid = low.advanced(by: low.distance(to: high) / 2) - if self[mid] < element { - low = mid.advanced(by: 1) - } else { - high = mid - } - } - return low - } - - /// Inserts a new element in a sorted array. - /// - /// - Parameter newElement: The element to insert into the array. - /// - /// - Complexity: To find the index for the new element - /// it uses a binary search algorithm - - mutating func sortedInsert(newElement: Element) { - insert(newElement, at: binarySearch(element: newElement) ) - } -} diff --git a/MappaMundi.xcodeproj/project.pbxproj b/MappaMundi.xcodeproj/project.pbxproj index 35a17af..4f8b8b3 100644 --- a/MappaMundi.xcodeproj/project.pbxproj +++ b/MappaMundi.xcodeproj/project.pbxproj @@ -7,21 +7,22 @@ objects = { /* Begin PBXBuildFile section */ - 13CE0A8322ABD430005CA5E3 /* MMNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13CE0A8222ABD42F005CA5E3 /* MMNode.swift */; }; - 13E39C7522ACF74A0015C33D /* Node.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13E39C7122ACF7270015C33D /* Node.swift */; }; - 13E39C7622ACF74D0015C33D /* SortedArray.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13E39C7222ACF7270015C33D /* SortedArray.swift */; }; - 392FB353200E1D5D0014867E /* MMTestUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 392FB352200E1D5D0014867E /* MMTestUtils.swift */; }; - 3962F2291FF6A6C100999008 /* Wait.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3962F2281FF6A6C100999008 /* Wait.swift */; }; - 3962F22B1FF6A75800999008 /* ScreenGraphEdge.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3962F22A1FF6A75800999008 /* ScreenGraphEdge.swift */; }; - 39658ADC1FF699AA000544A2 /* MMScreenStateNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 39658ADB1FF699AA000544A2 /* MMScreenStateNode.swift */; }; - 39658ADE1FF69BCE000544A2 /* MMNavigator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 39658ADD1FF69BCE000544A2 /* MMNavigator.swift */; }; - 39658AE01FF69F62000544A2 /* MMScreenActionNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 39658ADF1FF69F62000544A2 /* MMScreenActionNode.swift */; }; - 39658AE21FF6A065000544A2 /* MMUserState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 39658AE11FF6A065000544A2 /* MMUserState.swift */; }; - 397334491FF6AAA600C45ECE /* MMGraphNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 397334481FF6AAA600C45ECE /* MMGraphNode.swift */; }; - 39870718200D16710067B070 /* GraphRepresentation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 39870717200D16710067B070 /* GraphRepresentation.swift */; }; + 1355A49C22C8A48C00EB635F /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1355A49B22C8A48C00EB635F /* XCTest.framework */; }; + 1355A49F22C8A53700EB635F /* MMNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13CE0A8222ABD42F005CA5E3 /* MMNode.swift */; }; + 1355A4A022C8A53700EB635F /* MMScreenGraph.swift in Sources */ = {isa = PBXBuildFile; fileRef = 39AB5FDB1FE455C700008FB3 /* MMScreenGraph.swift */; }; + 1355A4A122C8A53700EB635F /* MMGraphNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 397334481FF6AAA600C45ECE /* MMGraphNode.swift */; }; + 1355A4A222C8A53700EB635F /* MMTestUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 392FB352200E1D5D0014867E /* MMTestUtils.swift */; }; + 1355A4A322C8A53700EB635F /* MMScreenActionNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 39658ADF1FF69F62000544A2 /* MMScreenActionNode.swift */; }; + 1355A4A422C8A53700EB635F /* MMScreenStateNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 39658ADB1FF699AA000544A2 /* MMScreenStateNode.swift */; }; + 1355A4A522C8A53700EB635F /* MMNavigator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 39658ADD1FF69BCE000544A2 /* MMNavigator.swift */; }; + 1355A4A622C8A53700EB635F /* MMUserState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 39658AE11FF6A065000544A2 /* MMUserState.swift */; }; + 1355A4A722C8A53700EB635F /* GraphRepresentation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 39870717200D16710067B070 /* GraphRepresentation.swift */; }; + 1355A4A822C8A53700EB635F /* ScreenGraphEdge.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3962F22A1FF6A75800999008 /* ScreenGraphEdge.swift */; }; + 1355A4A922C8A53700EB635F /* Wait.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3962F2281FF6A6C100999008 /* Wait.swift */; }; + 1355A4AB22C8A59B00EB635F /* AStar.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1355A49722C8A2E100EB635F /* AStar.framework */; settings = {ATTRIBUTES = (Required, ); }; }; + 13E36CDB22C8DF9000AE04DB /* MappaMundi.h in Headers */ = {isa = PBXBuildFile; fileRef = 39AB5FE61FE4595000008FB3 /* MappaMundi.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 13E36D1522C8EAA600AE04DB /* AStar.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1355A49722C8A2E100EB635F /* AStar.framework */; }; 39AB5FDE1FE4560D00008FB3 /* DemoMappaMundi.swift in Sources */ = {isa = PBXBuildFile; fileRef = 39AB5FDD1FE4560D00008FB3 /* DemoMappaMundi.swift */; }; - 39AB5FEC1FE4597800008FB3 /* MMScreenGraph.swift in Sources */ = {isa = PBXBuildFile; fileRef = 39AB5FDB1FE455C700008FB3 /* MMScreenGraph.swift */; }; - 39AB5FF21FE45FF500008FB3 /* MappaMundi.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 39AB5FE41FE4595000008FB3 /* MappaMundi.framework */; }; 39BAD55F1FE19B9E00524FB7 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 39BAD55E1FE19B9E00524FB7 /* AppDelegate.swift */; }; 39BAD5611FE19B9E00524FB7 /* MasterViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 39BAD5601FE19B9E00524FB7 /* MasterViewController.swift */; }; 39BAD5631FE19B9E00524FB7 /* DetailViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 39BAD5621FE19B9E00524FB7 /* DetailViewController.swift */; }; @@ -32,6 +33,13 @@ /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ + 1355A47F22C8A25C00EB635F /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 39BAD5531FE19B9D00524FB7 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 1355A47922C8A25C00EB635F; + remoteInfo = MappaMundi; + }; 39BAD5721FE19B9E00524FB7 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 39BAD5531FE19B9D00524FB7 /* Project object */; @@ -42,11 +50,11 @@ /* End PBXContainerItemProxy section */ /* Begin PBXCopyFilesBuildPhase section */ - 13AAA2CC22AC37CA0048E2C6 /* CopyFiles */ = { + 13E36CBD22C8AC2500AE04DB /* CopyFiles */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; dstPath = ""; - dstSubfolderSpec = 16; + dstSubfolderSpec = 10; files = ( ); runOnlyForDeploymentPostprocessing = 0; @@ -64,9 +72,14 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 1355A47122C89CD400EB635F /* Cartfile */ = {isa = PBXFileReference; lastKnownFileType = text; path = Cartfile; sourceTree = ""; }; + 1355A47222C89CD400EB635F /* Cartfile.resolved */ = {isa = PBXFileReference; lastKnownFileType = text; path = Cartfile.resolved; sourceTree = ""; }; + 1355A47A22C8A25C00EB635F /* MappaMundi.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = MappaMundi.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 1355A47C22C8A25C00EB635F /* MappaMundi.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MappaMundi.h; sourceTree = ""; }; + 1355A47D22C8A25C00EB635F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 1355A49722C8A2E100EB635F /* AStar.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AStar.framework; path = Carthage/Build/iOS/AStar.framework; sourceTree = ""; }; + 1355A49B22C8A48C00EB635F /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Platforms/iPhoneOS.platform/Developer/Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; }; 13CE0A8222ABD42F005CA5E3 /* MMNode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = MMNode.swift; path = Sources/MMNode.swift; sourceTree = SOURCE_ROOT; }; - 13E39C7122ACF7270015C33D /* Node.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Node.swift; sourceTree = ""; }; - 13E39C7222ACF7270015C33D /* SortedArray.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SortedArray.swift; sourceTree = ""; }; 392FB352200E1D5D0014867E /* MMTestUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MMTestUtils.swift; sourceTree = ""; }; 3962F2281FF6A6C100999008 /* Wait.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = Wait.swift; path = ../Sources/Wait.swift; sourceTree = ""; }; 3962F22A1FF6A75800999008 /* ScreenGraphEdge.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = ScreenGraphEdge.swift; path = ../Sources/ScreenGraphEdge.swift; sourceTree = ""; }; @@ -78,10 +91,8 @@ 39870717200D16710067B070 /* GraphRepresentation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = GraphRepresentation.swift; path = Sources/GraphRepresentation.swift; sourceTree = SOURCE_ROOT; }; 39AB5FDB1FE455C700008FB3 /* MMScreenGraph.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = MMScreenGraph.swift; path = ../Sources/MMScreenGraph.swift; sourceTree = ""; }; 39AB5FDD1FE4560D00008FB3 /* DemoMappaMundi.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DemoMappaMundi.swift; sourceTree = ""; }; - 39AB5FE41FE4595000008FB3 /* MappaMundi.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = MappaMundi.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 39AB5FE61FE4595000008FB3 /* MappaMundi.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MappaMundi.h; sourceTree = ""; }; 39AB5FE71FE4595000008FB3 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - 39AB5FEE1FE459AD00008FB3 /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Platforms/iPhoneOS.platform/Developer/Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; }; 39BAD55B1FE19B9D00524FB7 /* DemoApp.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = DemoApp.app; sourceTree = BUILT_PRODUCTS_DIR; }; 39BAD55E1FE19B9E00524FB7 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 39BAD5601FE19B9E00524FB7 /* MasterViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MasterViewController.swift; sourceTree = ""; }; @@ -96,10 +107,12 @@ /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ - 39AB5FE01FE4595000008FB3 /* Frameworks */ = { + 1355A47722C8A25C00EB635F /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 1355A49C22C8A48C00EB635F /* XCTest.framework in Frameworks */, + 1355A4AB22C8A59B00EB635F /* AStar.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -114,28 +127,20 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 39AB5FF21FE45FF500008FB3 /* MappaMundi.framework in Frameworks */, + 13E36D1522C8EAA600AE04DB /* AStar.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ - 13E39C6F22ACF7270015C33D /* Dependencies */ = { + 1355A47B22C8A25C00EB635F /* MappaMundi */ = { isa = PBXGroup; children = ( - 13E39C7022ACF7270015C33D /* AStar */, + 1355A47C22C8A25C00EB635F /* MappaMundi.h */, + 1355A47D22C8A25C00EB635F /* Info.plist */, ); - path = Dependencies; - sourceTree = SOURCE_ROOT; - }; - 13E39C7022ACF7270015C33D /* AStar */ = { - isa = PBXGroup; - children = ( - 13E39C7122ACF7270015C33D /* Node.swift */, - 13E39C7222ACF7270015C33D /* SortedArray.swift */, - ); - path = AStar; + path = MappaMundi; sourceTree = ""; }; 39AB5FE51FE4595000008FB3 /* MappaMundi */ = { @@ -161,7 +166,8 @@ 39AB5FED1FE459AD00008FB3 /* Frameworks */ = { isa = PBXGroup; children = ( - 39AB5FEE1FE459AD00008FB3 /* XCTest.framework */, + 1355A49B22C8A48C00EB635F /* XCTest.framework */, + 1355A49722C8A2E100EB635F /* AStar.framework */, ); name = Frameworks; sourceTree = ""; @@ -169,10 +175,12 @@ 39BAD5521FE19B9D00524FB7 = { isa = PBXGroup; children = ( + 1355A47122C89CD400EB635F /* Cartfile */, + 1355A47222C89CD400EB635F /* Cartfile.resolved */, 39BAD55D1FE19B9E00524FB7 /* DemoApp */, 39BAD5741FE19B9E00524FB7 /* UITests */, 39AB5FE51FE4595000008FB3 /* MappaMundi */, - 13E39C6F22ACF7270015C33D /* Dependencies */, + 1355A47B22C8A25C00EB635F /* MappaMundi */, 39BAD55C1FE19B9D00524FB7 /* Products */, 39AB5FED1FE459AD00008FB3 /* Frameworks */, ); @@ -183,7 +191,7 @@ children = ( 39BAD55B1FE19B9D00524FB7 /* DemoApp.app */, 39BAD5711FE19B9E00524FB7 /* DemoUITests.xctest */, - 39AB5FE41FE4595000008FB3 /* MappaMundi.framework */, + 1355A47A22C8A25C00EB635F /* MappaMundi.framework */, ); name = Products; sourceTree = ""; @@ -215,25 +223,26 @@ /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ - 39AB5FE11FE4595000008FB3 /* Headers */ = { + 1355A47522C8A25C00EB635F /* Headers */ = { isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( + 13E36CDB22C8DF9000AE04DB /* MappaMundi.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXHeadersBuildPhase section */ /* Begin PBXNativeTarget section */ - 39AB5FE31FE4595000008FB3 /* MappaMundi */ = { + 1355A47922C8A25C00EB635F /* MappaMundi */ = { isa = PBXNativeTarget; - buildConfigurationList = 39AB5FE91FE4595000008FB3 /* Build configuration list for PBXNativeTarget "MappaMundi" */; + buildConfigurationList = 1355A48322C8A25C00EB635F /* Build configuration list for PBXNativeTarget "MappaMundi" */; buildPhases = ( - 39AB5FDF1FE4595000008FB3 /* Sources */, - 39AB5FE01FE4595000008FB3 /* Frameworks */, - 39AB5FE11FE4595000008FB3 /* Headers */, - 39AB5FE21FE4595000008FB3 /* Resources */, - 13AAA2CC22AC37CA0048E2C6 /* CopyFiles */, + 1355A47522C8A25C00EB635F /* Headers */, + 1355A47622C8A25C00EB635F /* Sources */, + 1355A47722C8A25C00EB635F /* Frameworks */, + 1355A47822C8A25C00EB635F /* Resources */, + 13E36CBD22C8AC2500AE04DB /* CopyFiles */, ); buildRules = ( ); @@ -241,7 +250,7 @@ ); name = MappaMundi; productName = MappaMundi; - productReference = 39AB5FE41FE4595000008FB3 /* MappaMundi.framework */; + productReference = 1355A47A22C8A25C00EB635F /* MappaMundi.framework */; productType = "com.apple.product-type.framework"; }; 39BAD55A1FE19B9D00524FB7 /* DemoApp */ = { @@ -256,6 +265,7 @@ buildRules = ( ); dependencies = ( + 1355A48022C8A25C00EB635F /* PBXTargetDependency */, ); name = DemoApp; productName = Sample; @@ -269,6 +279,7 @@ 39BAD56D1FE19B9E00524FB7 /* Sources */, 39BAD56E1FE19B9E00524FB7 /* Frameworks */, 39BAD56F1FE19B9E00524FB7 /* Resources */, + 13E36D1422C8EA7600AE04DB /* Copy Carthage Frameworks */, ); buildRules = ( ); @@ -290,8 +301,8 @@ LastUpgradeCheck = 1020; ORGANIZATIONNAME = "Mozilla Corporation"; TargetAttributes = { - 39AB5FE31FE4595000008FB3 = { - CreatedOnToolsVersion = 9.2; + 1355A47922C8A25C00EB635F = { + CreatedOnToolsVersion = 10.2.1; ProvisioningStyle = Automatic; }; 39BAD55A1FE19B9D00524FB7 = { @@ -320,13 +331,13 @@ targets = ( 39BAD55A1FE19B9D00524FB7 /* DemoApp */, 39BAD5701FE19B9E00524FB7 /* DemoUITests */, - 39AB5FE31FE4595000008FB3 /* MappaMundi */, + 1355A47922C8A25C00EB635F /* MappaMundi */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ - 39AB5FE21FE4595000008FB3 /* Resources */ = { + 1355A47822C8A25C00EB635F /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( @@ -352,24 +363,44 @@ }; /* End PBXResourcesBuildPhase section */ +/* Begin PBXShellScriptBuildPhase section */ + 13E36D1422C8EA7600AE04DB /* Copy Carthage Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "$(SRCROOT)/Carthage/Build/iOS/AStar.framework", + ); + name = "Copy Carthage Frameworks"; + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/usr/local/bin/carthage copy-frameworks\n"; + }; +/* End PBXShellScriptBuildPhase section */ + /* Begin PBXSourcesBuildPhase section */ - 39AB5FDF1FE4595000008FB3 /* Sources */ = { + 1355A47622C8A25C00EB635F /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 397334491FF6AAA600C45ECE /* MMGraphNode.swift in Sources */, - 39658ADC1FF699AA000544A2 /* MMScreenStateNode.swift in Sources */, - 39870718200D16710067B070 /* GraphRepresentation.swift in Sources */, - 13E39C7522ACF74A0015C33D /* Node.swift in Sources */, - 39658AE21FF6A065000544A2 /* MMUserState.swift in Sources */, - 3962F22B1FF6A75800999008 /* ScreenGraphEdge.swift in Sources */, - 39658AE01FF69F62000544A2 /* MMScreenActionNode.swift in Sources */, - 39658ADE1FF69BCE000544A2 /* MMNavigator.swift in Sources */, - 13E39C7622ACF74D0015C33D /* SortedArray.swift in Sources */, - 3962F2291FF6A6C100999008 /* Wait.swift in Sources */, - 392FB353200E1D5D0014867E /* MMTestUtils.swift in Sources */, - 13CE0A8322ABD430005CA5E3 /* MMNode.swift in Sources */, - 39AB5FEC1FE4597800008FB3 /* MMScreenGraph.swift in Sources */, + 1355A49F22C8A53700EB635F /* MMNode.swift in Sources */, + 1355A4A022C8A53700EB635F /* MMScreenGraph.swift in Sources */, + 1355A4A122C8A53700EB635F /* MMGraphNode.swift in Sources */, + 1355A4A222C8A53700EB635F /* MMTestUtils.swift in Sources */, + 1355A4A322C8A53700EB635F /* MMScreenActionNode.swift in Sources */, + 1355A4A422C8A53700EB635F /* MMScreenStateNode.swift in Sources */, + 1355A4A522C8A53700EB635F /* MMNavigator.swift in Sources */, + 1355A4A622C8A53700EB635F /* MMUserState.swift in Sources */, + 1355A4A722C8A53700EB635F /* GraphRepresentation.swift in Sources */, + 1355A4A822C8A53700EB635F /* ScreenGraphEdge.swift in Sources */, + 1355A4A922C8A53700EB635F /* Wait.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -395,6 +426,11 @@ /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ + 1355A48022C8A25C00EB635F /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 1355A47922C8A25C00EB635F /* MappaMundi */; + targetProxy = 1355A47F22C8A25C00EB635F /* PBXContainerItemProxy */; + }; 39BAD5731FE19B9E00524FB7 /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 39BAD55A1FE19B9D00524FB7 /* DemoApp */; @@ -422,10 +458,13 @@ /* End PBXVariantGroup section */ /* Begin XCBuildConfiguration section */ - 39AB5FEA1FE4595000008FB3 /* Debug */ = { + 1355A48422C8A25C00EB635F /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO; + CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES = NO; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_MODULES_AUTOLINK = YES; CODE_SIGN_IDENTITY = ""; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; @@ -441,7 +480,10 @@ ); INFOPLIST_FILE = MappaMundi/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 10.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; OTHER_LDFLAGS = ( "-weak_framework", XCTest, @@ -450,17 +492,20 @@ PRODUCT_BUNDLE_IDENTIFIER = com.mozilla.ios.MappaMundi; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; SKIP_INSTALL = YES; - SWIFT_VERSION = 4.0; + SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; name = Debug; }; - 39AB5FEB1FE4595000008FB3 /* Release */ = { + 1355A48522C8A25C00EB635F /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO; + CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES = NO; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_MODULES_AUTOLINK = YES; CODE_SIGN_IDENTITY = ""; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; @@ -469,7 +514,6 @@ DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; ENABLE_BITCODE = NO; - ENABLE_TESTABILITY = NO; FRAMEWORK_SEARCH_PATHS = ( "$(PLATFORM_DIR)/Developer/Library/Frameworks", "$(DEVELOPER_FRAMEWORKS_DIR)", @@ -477,7 +521,9 @@ ); INFOPLIST_FILE = MappaMundi/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 10.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MTL_FAST_MATH = YES; OTHER_LDFLAGS = ( "-weak_framework", XCTest, @@ -486,7 +532,7 @@ PRODUCT_BUNDLE_IDENTIFIER = com.mozilla.ios.MappaMundi; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; SKIP_INSTALL = YES; - SWIFT_VERSION = 4.0; + SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; @@ -551,6 +597,7 @@ SDKROOT = iphoneos; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; }; name = Debug; }; @@ -604,6 +651,7 @@ MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + SWIFT_VERSION = 5.0; VALIDATE_PRODUCT = YES; }; name = Release; @@ -611,13 +659,14 @@ 39BAD57B1FE19B9E00524FB7 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_STYLE = Automatic; INFOPLIST_FILE = "$(SRCROOT)/DemoApp/Info.plist"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = com.mozilla.ios.MappaMundi.Demo; PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 4.0; + SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Debug; @@ -625,13 +674,14 @@ 39BAD57C1FE19B9E00524FB7 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_STYLE = Automatic; INFOPLIST_FILE = "$(SRCROOT)/DemoApp/Info.plist"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = com.mozilla.ios.MappaMundi.Demo; PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 4.0; + SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Release; @@ -639,13 +689,17 @@ 39BAD57E1FE19B9E00524FB7 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO; CODE_SIGN_STYLE = Automatic; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Carthage/Build/iOS", + ); INFOPLIST_FILE = UITests/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = com.mozilla.ios.MappMundi.UITests; PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 4.0; + SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; TEST_TARGET_NAME = DemoApp; }; @@ -654,13 +708,17 @@ 39BAD57F1FE19B9E00524FB7 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO; CODE_SIGN_STYLE = Automatic; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Carthage/Build/iOS", + ); INFOPLIST_FILE = UITests/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = com.mozilla.ios.MappMundi.UITests; PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 4.0; + SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; TEST_TARGET_NAME = DemoApp; }; @@ -669,11 +727,11 @@ /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ - 39AB5FE91FE4595000008FB3 /* Build configuration list for PBXNativeTarget "MappaMundi" */ = { + 1355A48322C8A25C00EB635F /* Build configuration list for PBXNativeTarget "MappaMundi" */ = { isa = XCConfigurationList; buildConfigurations = ( - 39AB5FEA1FE4595000008FB3 /* Debug */, - 39AB5FEB1FE4595000008FB3 /* Release */, + 1355A48422C8A25C00EB635F /* Debug */, + 1355A48522C8A25C00EB635F /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; diff --git a/MappaMundi.xcodeproj/xcshareddata/xcschemes/MappaMundi.xcscheme b/MappaMundi.xcodeproj/xcshareddata/xcschemes/MappaMundi.xcscheme index d063ecc..b805e1a 100644 --- a/MappaMundi.xcodeproj/xcshareddata/xcschemes/MappaMundi.xcscheme +++ b/MappaMundi.xcodeproj/xcshareddata/xcschemes/MappaMundi.xcscheme @@ -9,12 +9,12 @@ @@ -29,15 +29,6 @@ shouldUseLaunchSchemeArgsEnv = "YES"> - - - - @@ -54,7 +45,7 @@ @@ -64,11 +55,20 @@ + + + + diff --git a/MappaMundi/Info.plist b/MappaMundi/Info.plist index 1007fd9..e1fe4cf 100644 --- a/MappaMundi/Info.plist +++ b/MappaMundi/Info.plist @@ -18,7 +18,5 @@ 1.0 CFBundleVersion $(CURRENT_PROJECT_VERSION) - NSPrincipalClass - diff --git a/Sources/MMNavigator.swift b/Sources/MMNavigator.swift index 6e849b6..ffada53 100644 --- a/Sources/MMNavigator.swift +++ b/Sources/MMNavigator.swift @@ -4,6 +4,7 @@ import Foundation import XCTest +import AStar public typealias NodeVisitor = (String) -> Void diff --git a/Sources/MMNode.swift b/Sources/MMNode.swift index 61c633b..f6c60f4 100644 --- a/Sources/MMNode.swift +++ b/Sources/MMNode.swift @@ -2,6 +2,7 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +import AStar final class MMNode: GraphNode { var name: String?