public void CheckIsGoal(PuzzleStateNode node) { bool anyPlayers = false; bool allPlayersInGoal = true; foreach (PuzzleItem item in items) { PuzzlePlayer player = item as PuzzlePlayer; if (player == null) { continue; } anyPlayers = true; PuzzleElement element = player.GetNode(this, node.state).element; if (element == null || !(element is PuzzleGoal)) { allPlayersInGoal = false; } } node.goal = false; if (anyPlayers && allPlayersInGoal) { node.goal = true; if (goalPathSteps > 0) { goalPathSteps = Math.Min(goalPathSteps, node.step); } else { goalPathSteps = node.step; } goalStates++; } }
public PuzzleStateNode CloneAndAddChild(string name, PuzzleNode node) { PuzzleStateNode n = new PuzzleStateNode(state.Clone()); n.step = step + 1; new PuzzleStateEdge(name, node, this, n); return(n); }
private void SetState(PuzzleStateNode newState, bool animate) { PuzzleStateNode lastState = state; m_State = newState; stateChangeTime = Time.time; if (animate) { itemChangeTime = stateChangeTime + itemDelay; doneTime = stateChangeTime; } else { itemChangeTime = stateChangeTime; doneTime = stateChangeTime; } bool itemChange = false; foreach (var element in puzzle.dynamicElements) { object oldVal = puzzle.GetElementValue(lastState.state, element); object newVal = puzzle.GetElementValue(newState.state, element); if (!newVal.Equals(oldVal)) { PuzzleContainer container = puzzle.GetElementContainer(element); ContainerFront front = GetContainer(container); StartCoroutine(front.AnimateValue((bool)oldVal, (bool)newVal, animate)); itemChange = true; } } if (itemChange && animate) { doneTime += itemAndEffectDuration; } bool movement = false; foreach (var item in puzzle.items) { PuzzleNode oldNode = puzzle.GetItemNode(lastState.state, item); PuzzleNode newNode = puzzle.GetItemNode(newState.state, item); if (newNode != oldNode) { movement = true; } ItemFront front = GetItem(item); // Animate regardless of whether old and new node are the same. // Movement of other items may cause static item to change offset. StartCoroutine(front.AnimateNode(oldNode, newNode, state, animate)); } if (movement && animate) { itemChangeTime += moveDuration; doneTime += moveDuration; } }
public bool ReplaceIfSameAsExistingNode(PuzzleStateEdge edge) { PuzzleStateNode sameState = null; if (stateMap.TryGetValue(edge.toNode.state, out sameState)) { edge.ReplaceToNode(sameState); return(true); } return(false); }
public PuzzleStateEdge( string name, PuzzleNode actionNode, PuzzleStateNode fromNode, PuzzleStateNode toNode ) { this.name = name; this.actionNode = actionNode; this.fromNode = fromNode; this.toNode = toNode; fromNode.outgoing.Add(this); toNode.ingoing.Add(this); }
public IEnumerator AnimateNode(PuzzleNode oldNode, PuzzleNode newNode, PuzzleStateNode state, bool animate) { Vector3 newPos = GetPositionInNode(newNode, state); if (animate) { Vector3 oldPos = transform.position; float startTime = Time.time; while (Time.time < startTime + PuzzleFront.moveDuration) { transform.position = Vector3.Lerp(oldPos, newPos, (Time.time - startTime) / PuzzleFront.moveDuration); yield return(0); } } transform.position = newPos; }
public void AddStates(Puzzle puzzle, PuzzleStateNode parent) { PuzzleState state = parent.state; PuzzleState next; if (puzzle.GetItemsInNode <PuzzlePlayer> (state, this).Count > 0) { // Activate toggle PuzzleToggle toggle = element as PuzzleToggle; if (toggle != null) { next = parent.CloneAndAddChild("Press toggle", this).state; TriggerToggle(puzzle, next); } } }
public void AddStates(Puzzle puzzle, PuzzleStateNode parent, PuzzleNode nodeA, PuzzleNode nodeB, bool forward) { PuzzleState state = parent.state; PuzzleState next; PuzzleElement e = elementNotNull; PuzzleElement n = nodeB.elementNotNull; foreach (PuzzleItem player in puzzle.GetItemsInNode <PuzzlePlayer> (state, nodeA)) { // Walk if (e.CanWalk(puzzle, state, nodeA, nodeB, forward) && n.CanWalk(puzzle, state, nodeA, nodeB, forward)) { next = parent.CloneAndAddChild("Go here", nodeB).state; player.SetNode(puzzle, next, nodeB); } // Take or push ball List <PuzzleBall> balls = puzzle.GetItemsInNode <PuzzleBall> (state, nodeA); if (balls.Count > 0) { PuzzleBall ball = balls[0]; if (e.CanTakeBall(puzzle, state, nodeA, nodeB, forward) && n.CanTakeBall(puzzle, state, nodeA, nodeB, forward)) { next = parent.CloneAndAddChild("Bring ball here", nodeB).state; player.SetNode(puzzle, next, nodeB); ball.SetNode(puzzle, next, nodeB); } if (e.CanPushBall(puzzle, state, nodeA, nodeB, forward) && n.CanPushBall(puzzle, state, nodeA, nodeB, forward)) { next = parent.CloneAndAddChild("Push ball here", nodeB).state; ball.SetNode(puzzle, next, nodeB); } } // Hit toggle at other end if (e.CanSee(puzzle, state, nodeA, nodeB, forward) && n.CanSee(puzzle, state, nodeA, nodeB, forward)) { PuzzleToggle toggle = nodeB.element as PuzzleToggle; if (toggle != null) { next = parent.CloneAndAddChild("Shoot toggle", nodeB).state; nodeB.TriggerToggle(puzzle, next); } } } }
void CalculateDistancesToNode(PuzzleStateNode origin, PuzzleStateNode current, int dist, double[,] distances) { if (distances[origin.id, current.id] <= dist) { return; } distances[origin.id, current.id] = dist; for (int i = 0; i < current.outgoing.Count; i++) { CalculateDistancesToNode(origin, current.outgoing[i].toNode, dist + 1, distances); } for (int i = 0; i < current.ingoing.Count; i++) { CalculateDistancesToNode(origin, current.ingoing[i].fromNode, dist + 1, distances); } }
private Vector3 GetPositionInNode(PuzzleNode node, PuzzleStateNode state) { Vector3 newPos = puzzle.GetNode(node).transform.position; if (state != null) { var items = puzzle.puzzle.GetItemsInNode(state.state, node); if (items.Count > 1) { int index = items.IndexOf(item); float offset = (index + 0.5f) / items.Count * 2 - 1; newPos += Vector3.right * offset * 0.3f; } } return(newPos); }
void DrawNode(PuzzleStateNode node, bool outer) { Color color = Color.white; Pointf point = layouter.GetPoint(node.id); Vector2 p = new Vector2((float)point.x, (float)point.y); float size = 0.5f; if (outer) { size += 0.4f; if (node.goal) { color = Color.green; } } else { if (node.goalPath) { color = Color.green; } else if (node.stuck) { color = Color.red; } if (node == puzzleFront.state) { float pulse01 = 0.5f + 0.5f * Mathf.Sin(Time.unscaledTime * Mathf.PI * 2); pulse01 *= pulse01; color = Color.Lerp(color, Color.black, pulse01 * 0.5f); } } DrawGLQuad( p, Vector2.one * size, 0, new Rect(0 / 512f, 384 / 512f, 64 / 512f, 64 / 512f), Color.black); DrawGLQuad( p, Vector2.one * (size - 0.1f), 0, new Rect(0 / 512f, 384 / 512f, 64 / 512f, 64 / 512f), color); }
public void Reset() { undoStack.Clear(); m_State = puzzle.startNode; foreach (var element in puzzle.dynamicElements) { PuzzleContainer container = puzzle.GetElementContainer(element); ContainerFront front = GetContainer(container); front.SetElement(element); } foreach (var item in puzzle.items) { PuzzleNode newNode = item.defaultNode; ItemFront front = GetItem(item); front.SetNodePos(newNode, state); } }
void RefreshStatesGUI() { if (state == lastCalculatedState) { return; } actionsPerNode = new List <PuzzleStateEdge> [puzzle.nodes.Count]; for (int i = 0; i < state.outgoing.Count; i++) { PuzzleStateEdge child = state.outgoing[i]; PuzzleNode actionNode = child.actionNode; int nodeIndex = puzzle.nodes.IndexOf(actionNode); if (actionsPerNode[nodeIndex] == null) { actionsPerNode[nodeIndex] = new List <PuzzleStateEdge> (); } actionsPerNode[nodeIndex].Add(child); } lastCalculatedState = state; }
public void EvaluatePossibleStates(PuzzleStateNode stateNode) { //stateNode.children = new List<PuzzleStateEdge> (); // Get states from nodes foreach (PuzzleNode node in nodes) { node.AddStates(this, stateNode); } // Get states from edges foreach (PuzzleEdge e in edges) { e.AddStates(this, stateNode); } // Post-process states foreach (PuzzleStateEdge stateEdge in stateNode.outgoing) { foreach (PuzzleNode node in nodes) { if (node.element != null) { node.element.UpdateState(this, stateEdge.toNode.state, node); } } // Do last if (!ReplaceIfSameAsExistingNode(stateEdge)) { stateEdge.directPath = true; PuzzleStateNode newNode = stateEdge.toNode; CheckIsGoal(newNode); longestPathSteps = System.Math.Max(longestPathSteps, newNode.step); stateMap[newNode.state] = newNode; newNode.id = stateMap.Count - 1; } } }
void LayoutStates(PuzzleStateNode state, Pointf point, double angle) { if (state == null) { return; } state.point = point; int count = state.outgoing.Where(e => e.directPath).Count(); int child = 0; for (int i = 0; i < state.outgoing.Count; i++) { if (state.outgoing[i].directPath) { double childAngle = angle - Math.PI + Math.PI * 2 * ((child + 1f) / (count + 1f)); Pointf childDir = new Pointf(Math.Cos(childAngle), Math.Sin(childAngle)); LayoutStates(state.outgoing[i].toNode, point + childDir, childAngle); child++; } } }
void UpdateInternal() { int n = puzzle.stateNodes.Count; for (int i = 0; i < n; i++) { vectors[i] = Pointf.zero; } for (int i = 0; i < n; i++) { PuzzleStateNode node1 = puzzle.stateNodes[i]; for (int j = 0; j < i; j++) { PuzzleStateNode node2 = puzzle.stateNodes[j]; Pointf vector = node2.point - node1.point; double len = vector.magnitude; if (len == 0) { continue; } double dist = distances[i, j]; // Calculate the adjustment length as the difference between // the current and ideal distance, divided by the squared ideal distance. // The division by ideal distance is because it's less important // to maintain ideal distance to far off nodes than to close by one. // And also because there are more far off nodes than close by ones, // so they often have a large aggregate force. // The reason to use exactly the squared ideal distance to divide with // is largely experimentally arrived at. double adjust = (dist - len) / (dist * dist); // Multiply the normalized adjustment vector with the adjustment length. vector = (vector / len) * adjust; vectors[node1.id] += -vector; vectors[node2.id] += vector; } } double multiplier = 0.1; double maxForce = 0; for (int i = 0; i < n; i++) { int id = puzzle.stateNodes[i].id; puzzle.stateNodes[i].point += vectors[id] * multiplier; maxForce = Math.Max(maxForce, vectors[id].sqrMagnitude); } if (maxForce < 0.0001) { stabilized = true; initialized = true; } CalcGraphStats(); Pointf[] temp = oldVectors; oldVectors = vectors; vectors = temp; }
public void GoToState(PuzzleStateNode newState) { undoStack.Push(state); SetState(newState, true); }
public void Undo() { PuzzleStateNode state = undoStack.Pop(); SetState(state, true); }
public void EvaluateTree(int maxDepth) { startNode = GetStartState(); exploredAll = true; longestPathSteps = 0; goalPathSteps = 0; goalStates = 0; stateMap = new Dictionary <PuzzleState, PuzzleStateNode> (); stateMap[startNode.state] = startNode; List <PuzzleStateNode> goalNodes = new List <PuzzleStateNode> (); Queue <PuzzleStateNode> queue = new Queue <PuzzleStateNode> (); queue.Enqueue(startNode); while (queue.Count > 0) { PuzzleStateNode node = queue.Dequeue(); if (node.goal) { goalNodes.Add(node); } else if (node.step >= maxDepth) { exploredAll = false; } else { EvaluatePossibleStates(node); foreach (PuzzleStateEdge edge in node.outgoing) { if (edge.directPath) { queue.Enqueue(edge.toNode); } } } } for (int i = goalNodes.Count - 1; i >= 0; i--) { PuzzleStateNode node = goalNodes[i]; while (node != null) { if (node.goalPath) { break; } node.goalPath = true; node.stuck = false; PuzzleStateNode newNode = null; foreach (var edge in node.ingoing) { if (edge.directPath) { newNode = edge.fromNode; } else { queue.Enqueue(edge.fromNode); } } node = newNode; } } while (queue.Count > 0) { PuzzleStateNode node = queue.Dequeue(); if (node.stuck == false) { continue; } node.stuck = false; foreach (PuzzleStateEdge edge in node.ingoing) { queue.Enqueue(edge.fromNode); } } stateNodes = stateMap.Values.OrderBy(e => e.id).ToList(); }
public void AddStates(Puzzle puzzle, PuzzleStateNode parent) { AddStates(puzzle, parent, nodeA, nodeB, true); AddStates(puzzle, parent, nodeB, nodeA, false); }
void OnPostRender() { if (layouter == null) { return; } if (!manualZoom) { if (cam.orthographicSize != lastOrthographicSize) { manualZoom = false; } else { lastOrthographicSize = ((float)PuzzleStateLayouter.diameter + 6) * 0.5f; cam.orthographicSize = lastOrthographicSize; } } GL.PushMatrix(); graphMaterial.SetPass(0); GL.LoadProjectionMatrix(Camera.current.projectionMatrix); GL.Begin(GL.QUADS); // Draw outer rings for special nodes for (int i = 0; i < layouter.puzzle.stateNodes.Count; i++) { PuzzleStateNode node = layouter.puzzle.stateNodes[i]; if (node.goal || node == layouter.puzzle.startNode) { DrawNode(node, true); } } // Draw edges for (int i = 0; i < layouter.puzzle.stateNodes.Count; i++) { if (i % 100 == 0) { GL.End(); GL.Begin(GL.QUADS); } PuzzleStateNode node = layouter.puzzle.stateNodes[i]; Pointf point1 = layouter.GetPoint(i); Vector2 p1 = new Vector2((float)point1.x, (float)point1.y); for (int j = 0; j < node.outgoing.Count; j++) { PuzzleStateNode other = node.outgoing[j].toNode; Pointf point2 = layouter.GetPoint(other.id); Vector2 p2 = new Vector2((float)point2.x, (float)point2.y); Vector2 dir = p2 - p1; float length = dir.magnitude; if (length < 0.01f) { continue; } float angle = Mathf.Atan2(dir.y, dir.x) * Mathf.Rad2Deg; DrawGLQuad( (p1 + p2) * 0.5f, new Vector2(length, 0.8f), angle, new Rect(66 / 512f, 400 / 512f, 4 / 512f, 32 / 512f), Color.black); } } // Draw nodes for (int i = 0; i < layouter.puzzle.stateNodes.Count; i++) { if (i % 100 == 0) { GL.End(); GL.Begin(GL.QUADS); } DrawNode(layouter.puzzle.stateNodes[i], false); } GL.End(); GL.PopMatrix(); }
public void SetNodePos(PuzzleNode node, PuzzleStateNode state) { transform.position = GetPositionInNode(node, state); }
void CalcGraphStats() { double segmentLengthSum = 0; int segmentCount = 0; double extremeLength = 0; Pointf extreme1 = Pointf.zero; Pointf extreme2 = Pointf.zero; int n = puzzle.stateNodes.Count; for (int i = 0; i < n; i++) { PuzzleStateNode node1 = puzzle.stateNodes[i]; for (int j = 0; j < i; j++) { PuzzleStateNode node2 = puzzle.stateNodes[j]; Pointf vector = node2.point - node1.point; double len = vector.magnitude; double dist = distances[i, j]; if (dist == 1) { segmentCount++; segmentLengthSum += len; } if (len > extremeLength) { extremeLength = len; extreme1 = node1.point; extreme2 = node2.point; } } } if (segmentCount == 0) { centerTarget = Pointf.zero; diameterTarget = 0; angleTarget = 0; } else { segmentLength = segmentLengthSum / segmentCount; centerTarget = (extreme2 + extreme1) * 0.5; Pointf mainAxis = (extreme2 - extreme1); diameterTarget = mainAxis.magnitude; mainAxis = mainAxis / diameterTarget; Pointf axis2 = new Pointf(-mainAxis.y, mainAxis.x); double projectedMin = double.MaxValue; double projectedMax = double.MinValue; for (int i = 0; i < n; i++) { Pointf p = puzzle.stateNodes[i].point; Pointf rel = p - centerTarget; double relOnAxis2 = Pointf.Dot(rel, axis2); projectedMin = Math.Min(projectedMin, relOnAxis2); projectedMax = Math.Max(projectedMax, relOnAxis2); } angleTarget = Math.Atan2(mainAxis.x, -mainAxis.y); centerTarget += axis2 * (projectedMin + projectedMax) * 0.5; diameterTarget /= segmentLength; } }
public void ReplaceToNode(PuzzleStateNode newToNode) { toNode.ingoing.Remove(this); toNode = newToNode; toNode.ingoing.Add(this); }