예제 #1
0
    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++;
        }
    }
예제 #2
0
    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);
    }
예제 #3
0
    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;
        }
    }
예제 #4
0
    public bool ReplaceIfSameAsExistingNode(PuzzleStateEdge edge)
    {
        PuzzleStateNode sameState = null;

        if (stateMap.TryGetValue(edge.toNode.state, out sameState))
        {
            edge.ReplaceToNode(sameState);
            return(true);
        }
        return(false);
    }
예제 #5
0
 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);
 }
예제 #6
0
    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;
    }
예제 #7
0
    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);
            }
        }
    }
예제 #8
0
    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);
        }
    }
예제 #10
0
    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);
    }
예제 #11
0
    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);
    }
예제 #12
0
    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);
        }
    }
예제 #13
0
 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;
 }
예제 #14
0
    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;
    }
예제 #17
0
 public void GoToState(PuzzleStateNode newState)
 {
     undoStack.Push(state);
     SetState(newState, true);
 }
예제 #18
0
    public void Undo()
    {
        PuzzleStateNode state = undoStack.Pop();

        SetState(state, true);
    }
예제 #19
0
    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();
    }
예제 #20
0
 public void AddStates(Puzzle puzzle, PuzzleStateNode parent)
 {
     AddStates(puzzle, parent, nodeA, nodeB, true);
     AddStates(puzzle, parent, nodeB, nodeA, false);
 }
예제 #21
0
    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();
    }
예제 #22
0
 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;
        }
    }
예제 #24
0
 public void ReplaceToNode(PuzzleStateNode newToNode)
 {
     toNode.ingoing.Remove(this);
     toNode = newToNode;
     toNode.ingoing.Add(this);
 }