public QPriority(QGameState node, BuildingGS targetGS, HiddenRequirement hiddenReq) { this.hiddenReq = hiddenReq; this.currentNode = node; this.targetGS = targetGS; this.distanceRuler = LTPHelper.estematedRemainingDistance(node.gameState, targetGS); }
protected bool processResult(QGameState engineOutput) { // Process the output result from the LTPEngine UnityEngine.Debug.Log("actual EndTime: " + Stopwatch.GetTimestamp()); UnityEngine.Debug.Log("IG time: " + engineOutput.costToGetHere); if (engineOutput == null) { this.result = new Stack <Work>(); } Stack <Work> result = new Stack <Work>(); Work finalWait = new Work(EWork.Wait, BuildingType.NONE, 10); result.Push(finalWait); // TODO: there's some bug that causes the Engine to end one step short :( while (engineOutput != null) { // Note, the finalResult value should be the targetGS. IE: We're looping backwards from // finish to start. So last element into the list == the first unit of work that // should be done if (engineOutput.transitionWork.workType != EWork.Wait) { result.Push(engineOutput.transitionWork); } engineOutput = engineOutput.parent; } this.result = result; this.finished = true; return(true); }
public QGameState(BuildingGS gs, QGameState parent, Work transitionWork, int costToGetHere) { this.gameState = gs; this.parent = parent; this.transitionWork = transitionWork; this.costToGetHere = costToGetHere; }
// This function represents an edge public static QGameState waitTransition(QGameState qe, int time) { BuildingGS endGS = waitGameState(qe.gameState, time); int timeCost = qe.costToGetHere + time; Work newWork = new Work(EWork.Wait, BuildingType.NONE, time); return(new QGameState(endGS, qe, newWork, timeCost)); }
public static QGameState buyBuilding(QGameState qGS, IBuilding newBuilding) { BuildingGS newGameState = waitGameState(qGS.gameState, newBuilding.timeToBuild()); newGameState.buyBuilding(newBuilding); int costToGetHere = qGS.costToGetHere + newBuilding.timeToBuild(); Work newWork = new Work(EWork.BuildBuilding, newBuilding.getBuildingType(), newBuilding.timeToBuild()); return(new QGameState(newGameState, qGS, newWork, costToGetHere)); }
////////////////////////////////////////////////// public static QGameState buyWorker(QGameState qGS, BuildingType bt) { BuildingGS newGameState = waitGameState(qGS.gameState, 1); newGameState.buyAndAssignWorker(bt); int costToGetHere = qGS.costToGetHere + 3; Work newWork = new Work(EWork.BuyAndAssignWorker, bt, 3); return(new QGameState(newGameState, qGS, newWork, costToGetHere)); }
private bool id_processResult(QGameState engineOutput) { if (engineOutput == null) { // If no path was found, increase the depth and try again ID_LTP(initialGS, targetGS, callback, ID_Queue.NextBount); } else { // Otherwise, we have a valid path so process as normal base.processResult(engineOutput); } return(true); }
public static HashSet <QGameState> getNeighbors(QGameState qEntry) { // For a given game state return all valid edges out of it HashSet <QGameState> result = new HashSet <QGameState>(); BuildingGS gs = qEntry.gameState; // Branches related to workers if (gs.canBuyWorker()) { // If we have the resources to build a new worker foreach (BuildingType bt in gs.getOpenSlots()) { // One branch for every possible type of worker slot we can fill QGameState neighbor = QGameStateFactory.buyWorker(qEntry, bt); result.Add(neighbor); } } // The length of all the no-op edges we want to consider HashSet <int> waitTimes = new HashSet <int>() { 10 }; // Branches related to Buildings // TODO: Why build a building if we can't populate it with a worker? foreach (BuildingType bt in BuildingFactory.allBuildings) { // One branch for every new possible building IBuilding possibleBuilding = BuildingFactory.buildNew(bt, -1, -1); // TODO: do we care about pos when doing A*? if (gs.canBuyBuilding(possibleBuilding)) { // If we can build this building, then add a branch QGameState neighbor = QGameStateFactory.buyBuilding(qEntry, possibleBuilding); result.Add(neighbor); } } // Add in some no-op edges foreach (int waitTime in waitTimes) { result.Add(QGameStateFactory.waitTransition(qEntry, waitTime)); } return(result); }
private static readonly int NODES_CHECKED_PER_YIELD = 150; // Check n nodes every frame public static IEnumerator BuildPlan(BuildingGS initialGS, BuildingGS targetGS, PriorityQueue <QPriority, QGameState> priorityQueue, Func <QGameState, bool> finishCallback) { int nodesChecked = 0; // Initialize data structures Dictionary <int, int> bestCostToGetHere = new Dictionary <int, int>(); HiddenRequirement hiddenReq = new HiddenRequirement(); // NOTE: Don't add the initial state to the bestCostToGetHere, that will be done automatically Work initialWork = new Work(EWork.EMPTY, BuildingType.NONE, 0); QGameState firstGS = new QGameState(initialGS, null, initialWork, 0); QPriority firstPriority = new QPriority(firstGS, targetGS, hiddenReq); priorityQueue.Enqueue(firstPriority, firstGS); int totalChecks = 0; while (priorityQueue.Count > 0) { nodesChecked++; if (nodesChecked >= NODES_CHECKED_PER_YIELD) { // TODO: There may be an off by 1 error, but not a major problem nodesChecked = 0; yield return(null); } totalChecks++; KeyValuePair <QPriority, QGameState> kvp = priorityQueue.Dequeue(); QGameState qe = kvp.Value; if (false) { Debug.Log("==============================================================================================="); Debug.Log("Cost to get here: " + qe.costToGetHere + " + " + kvp.Key.maxWaitTime); Debug.Log("incoming work: " + qe.transitionWork.workType + " " + qe.transitionWork.buildingType); Debug.Log(" + infinities : " + kvp.Key.numberOfInfinities); Debug.Log(" + unaquiriable : " + kvp.Key.unaquirableResourceCount); //Debug.Log(" + estTotalDist : " + kvp.Key.estTotalDist); Debug.Log(" + BestTotalDist : " + kvp.Key.bestMaxWaitTime); Debug.Log(" + totalCPTDelta : " + kvp.Key.totalCPTDelta); Debug.Log(" + preRecDelta : " + kvp.Key.reccomendedPreRecDelta); //Debug.Log(" + best CPTDelta : " + heuristic.bestCPTDelta); Debug.Log(" + fudge factor : " + kvp.Key.totalResourcesSpent); Debug.Log("iron cpt: " + qe.gameState.getChangePerTick(ResourceType.Iron)); Debug.Log("coal cpt: " + qe.gameState.getChangePerTick(ResourceType.Coal)); Debug.Log("Steel cpt: " + qe.gameState.getChangePerTick(ResourceType.Steel)); Debug.Log("building count: " + qe.gameState.totalBuildingCount()); Debug.Log("worker count: " + qe.gameState.totalWorkerCount()); } // Early exit conditions if (LTPHelper.estematedRemainingDistance(qe.gameState, targetGS).atTarget()) { // If we are 0 distance away from the target game state // IE: If we have found the target game state finishCallback(qe); yield break; } else if (totalChecks > LTPHelper.MAX_DEPTH) { finishCallback(null); yield break; } if (bestCostToGetHere.ContainsKey(qe.gameState.GetHashCode()) && bestCostToGetHere[qe.gameState.GetHashCode()] <= qe.costToGetHere) { // If we've already explored this game state // AND if some other path is to this game state is cheeper continue; } else { // Else, this Queue Entry represents a cheeper path to get to this node bestCostToGetHere[qe.gameState.GetHashCode()] = qe.costToGetHere; } foreach (QGameState neighbor in LTPHelper.getNeighbors(qe)) { // The neighbor already has an updated gamestate, a job and an updated cost if (bestCostToGetHere.ContainsKey(neighbor.gameState.GetHashCode()) && bestCostToGetHere[neighbor.gameState.GetHashCode()] <= neighbor.costToGetHere) { // If we already have a better way to get to the neighbor continue; } QPriority heuristic = new QPriority(neighbor, targetGS, hiddenReq); if (false) { Debug.Log("********************"); Debug.Log("** Adding neighbor: "); Debug.Log("** transition work: " + neighbor.transitionWork.workType + " " + neighbor.transitionWork.buildingType); Debug.Log(" + infinities : " + heuristic.numberOfInfinities); Debug.Log(" + unaquiriable : " + heuristic.unaquirableResourceCount); //Debug.Log(" + estTotalDist : " + kvp.Key.estTotalDist); Debug.Log(" + BestTotalDist : " + kvp.Key.bestMaxWaitTime); Debug.Log(" + totalCPTDelta : " + heuristic.totalCPTDelta); Debug.Log(" + preRecDelta : " + heuristic.reccomendedPreRecDelta); //Debug.Log(" + best CPTDelta : " + heuristic.bestCPTDelta); Debug.Log(" + fudge factor : " + heuristic.totalResourcesSpent); Debug.Log("iron cpt: " + neighbor.gameState.getChangePerTick(ResourceType.Iron)); Debug.Log("coal cpt: " + neighbor.gameState.getChangePerTick(ResourceType.Coal)); Debug.Log("Steel cpt: " + neighbor.gameState.getChangePerTick(ResourceType.Steel)); Debug.Log("building count: " + neighbor.gameState.totalBuildingCount()); Debug.Log("worker count: " + neighbor.gameState.totalWorkerCount()); } priorityQueue.Enqueue(heuristic, neighbor); } // End foreach neighbor } // End while queue is NOT empty finishCallback(null); yield break; }