void Start()
    {
        // FIXME is new object the way to go?? Or scriptable object without new keyword?? Or static??
        MazeFrameCreator mazeFrameCreator = new MazeFrameCreatorMeshIcosahedron(2, 4)
        {
            Scale  = new Vector3(10, 10, 10),
            Jitter = 0f
        };

        mazeFrame = mazeFrameCreator.GenerateEmptyMazeFrame();
        for (int i = 0; i <= 3; i++)
        {
            mazeFrame.SetActiveQuadrant(i);
            mazeFrameCreator.GenerateMaze(ref mazeFrame);
            mazeFrame.SetOnShortestPath(mazeFrame.Quadrants[i], i); // HACKY
        }
        mazeFrame.ActivateAllNodes();

        //mazeFrame.KeepOnlyShortestPathConnections();

        mazeFrame.AddOffsetStartAndEndNodes(Vector3.Scale(Vector3.forward, mazeFrameCreator.Scale) * 3, true);
        mazeFrame.AddPathSegments();

        //mazeFrameSplines = new MazeFrameSplines(mazeFrame);
        //GameObject mazeObjects = null;
        //MazePopulator.PopulateWithSplineFittedBars(mazeFrame, ref mazeFrameSplines, ref mazeObjects, new Vector3(2, .15f, 1));


        //GameObject player = null;
        //GameObject cameraRig = null;
        //MazePopulator.PlacePlayerAndCamera(ref player, ref cameraRig, mazeFrame.StartNode.Position + (Vector3.Scale(Vector3.forward, mazeFrameCreator.Scale) * 1.5f));
    }
Exemple #2
0
 private void DestroyCurrentLevel()
 {
     if (!LevelIsPresent)
     {
         return;
     }
     mazeFrame              = null;
     mazeFrameSplines       = null;
     gravityFrameController = null;
     Destroy(mazeObjects);
     if (player != null)
     {
         player.SetActive(false);
     }
     if (cameraRig != null)
     {
         cameraRig.SetActive(false);
     }
     if (endPortal != null)
     {
         endPortal.SetActive(false);
     }
     if (cubeOfDeath != null)
     {
         Destroy(cubeOfDeath);
     }
     if (mindWarpTriggers != null)
     {
         Destroy(mindWarpTriggers);
     }
     //Destroy(player);
     //Destroy(cameraRig);
     //Destroy(cubeOfDeath);
     //Destroy(endPortal);
 }
Exemple #3
0
    /// <summary>
    /// Initializes a new gravity frame from maze frame fitted splines.
    /// </summary>
    /// <param name="mazeFrame">Maze frame.</param>
    /// <param name="mazeFrameSplines">Maze frame fitted splines.</param>
    /// <param name="closebyDistFac"> Units of max(mazeFrame.Scale) at which nodes are considered close by.</param>
    /// <param name="junctionDistFac"> Units of max(mazeFrame.Scale) at which nodes are considered close to junction.</param>
    public GravityFrameController(MazeFrame mazeFrame, MazeFrameSplines mazeFrameSplines, float junctionDistFac, float planeSize)
    {
        //float currCloseByDist = mazeFrame.Scale.ComponentMax() * closebyDistFac;
        float currJunctionDist = mazeFrame.Scale.ComponentMax() * junctionDistFac;

        GenerateGravityFrameFromSplines(mazeFrame, mazeFrameSplines, currJunctionDist, planeSize);
    }
Exemple #4
0
 /// <summary>
 /// Updates the Virus for the next frame to be drawn.
 /// </summary>
 /// <param name="time">Time elapsed during gameplay.</param>
 /// <param name="frame"> The frame that is currently drawn, used for storing playSessions for evaluation</param>
 /// <returns>An updated version of the frame-parameter, with all the relevant information of this Virus</returns>
 internal override MazeFrame Update(GameTime time, MazeFrame frame)
 {
     if (Vector2.Distance(maze.player.Position, Position) < 100.0f && maze.LettersCollected() && maze.GetSpeechManager().WordWasSaid(maze.requiredWord))
     {
         maze.resetGame(true);
     }
     return(frame);
 }
    /// <summary>
    /// Generate maze from maze frame.
    /// </summary>
    /// <returns>A maze object containing the maze.</returns>
    /// <param name="mazeFrame">Maze frame.</param>
    public void GenerateMaze(ref MazeFrame mazeFrame)
    {
        // Build maze
        //BuildMazeFrameUsingRecursiveBacktracking(ref mazeFrame);
        BuildMazeFrameUsingHuntAndKill(ref mazeFrame, 50, 0.50f);

        // Label all nodes
        LabelNodesWRTPath(ref mazeFrame);
    }
    /// <summary>
    /// Method to generate maze.
    /// </summary>
    /// <returns>The maze.</returns>
    public MazeFrame GenerateMaze()
    {
        // Initialize mazeFrame
        MazeFrame mazeFrame = GenerateEmptyMazeFrame();

        // Build maze and label
        GenerateMaze(ref mazeFrame);

        return(mazeFrame);
    }
    /// <summary>
    /// Generate maze from maze frame, inside quadrant specified by <paramref name="quadrantInd"/>.
    /// </summary>
    /// <returns>A maze object containing the maze.</returns>
    /// <param name="mazeFrame">Maze frame.</param>
    /// <param name="quadrantInd">Index of quadrant in which to build maze.</param>
    public void GenerateMaze(ref MazeFrame mazeFrame, int quadrantInd)
    {
        // Active quadrant
        mazeFrame.SetActiveQuadrant(quadrantInd);

        // Build maze and label
        GenerateMaze(ref mazeFrame);

        // Reactivate all nodes
        mazeFrame.ActivateAllNodes();
    }
Exemple #8
0
 /// <summary>
 /// Updates the Letter for the next frame to be drawn.
 /// </summary>
 /// <param name="time">Time elapsed during gameplay.</param>
 /// <param name="frame"> The frame that is currently drawn, used for storing playSessions for evaluation</param>
 /// <returns>An updated version of the frame-parameter, with all the relevant information of this Letter</returns>
 internal override MazeFrame Update(GameTime time, MazeFrame frame)
 {
     if (Vector2.Distance(maze.player.Position, Position) < 50) // If close we pick up the letter
     {
         pickedUp = true;
     }
     frame.Letters.Add(new LetterInfo()
     {
         Position = Position, PickedUp = pickedUp, Text = LetterString
     });                                                                                                    // Add Session information
     return(frame);
 }
    public void PopulateWithSplineFittedShape(MazeFrame mazeFrame, ref MazeFrameSplines mazeFrameSplines, ref GameObject mazeObjects, Vector3 resize, string baseShape)
    {
        int objPerCurveSegment = 2;

        // First, fit splines to maze path segments
        mazeFrameSplines = new MazeFrameSplines(mazeFrame);

        // Set color list to use for paths
        List <Color> colorList = new List <Color>();

        GetColorBasedOnPathIndex(new List <int>(1), ref colorList);

        // Create parent object to hold maze objects
        mazeObjects       = new GameObject("mazeObjects");
        mazeObjects.layer = 10;
        mazeObjects.transform.position = Vector3.zero;
        mazeObjects.transform.rotation = Quaternion.identity;
        // Load and resize prefab
        //string prefabObjName = baseShape + "/" + baseShape + "-20-" + objN;
        string     prefabObjName        = baseShape + "/" + baseShape + "-20-1";
        GameObject prefabInst           = Instantiate(Resources.Load("Prefabs/" + prefabObjName) as GameObject);
        Vector3    mazeObjectPrefabSize = prefabInst.GetComponent <MeshRenderer>().bounds.size;

        if (!resize.ComponentsAreApproxEqualTo(mazeObjectPrefabSize))
        {
            Utilities.MeshResize(prefabInst, Vector3.Scale(resize, prefabInst.GetComponent <MeshRenderer>().bounds.size.ComponentInverse()));
            mazeObjectPrefabSize = prefabInst.GetComponent <MeshRenderer>().bounds.size;
        }
        // Create objects and add meshes to fit
        StartCoroutine(CreateSplineFittedMeshesCoroutine(mazeObjects, mazeFrameSplines, prefabInst, objPerCurveSegment, colorList));
        //CreateSplineFittedMeshesCoroutine(mazeObjects, mazeFrameSplines, prefabInst, objPerCurveSegment, colorList);

        //// Add lights to the sides
        //GameObject mazeLightObjects = new GameObject("mazeLightObjects");
        //mazeLightObjects.transform.position = Vector3.zero;
        //mazeLightObjects.transform.rotation = Quaternion.identity;
        //// Load prefab
        //string prefabLightName = "PathObjectLightBar";
        //GameObject prefabLightInst = Instantiate(Resources.Load("Prefabs/" + prefabLightName) as GameObject);
        //float spacing = 1f;
        //// Add lights
        //AddPathObjectLights(mazeLightObjects, mazeFrameSplines, prefabLightInst, spacing, mazeObjectPrefabSize, colorList);
    }
    public void PlaceMindWarpTriggers(ref GameObject triggerParent, MazeFrame mazeFrame, float scale, float triggerProb, int intensity)
    {
        if (triggerParent != null)
        {
            Object.Destroy(triggerParent);
            triggerParent = null;
        }
        triggerParent = new GameObject("MindWarpTriggers");
        GameObject prefabInst = Object.Instantiate(Resources.Load("Prefabs/" + "MindWarpTrigger")) as GameObject;

        prefabInst.GetComponent <SphereCollider>().radius = scale / 2f;
        Transform parent = triggerParent.GetComponent <Transform>();

        foreach (MazeNode node in mazeFrame.Nodes)
        {
            if (node == mazeFrame.StartNode ||
                node == mazeFrame.EndNode ||
                node.Identifier.Contains("entry") ||
                node.Identifier.Contains("exit"))
            {
                continue;
            }

            // Place trigger
            if (node.ConnectedNeighbors.Count >= 3)
            {
                GameObject trigger = Object.Instantiate(prefabInst, parent);
                trigger.transform.position = node.Position;
                trigger.name = "trigger-" + node.Identifier;
                Vector3[] neighborPosition = new Vector3[node.ConnectedNeighbors.Count];
                for (int i = 0; i < node.ConnectedNeighbors.Count; i++)
                {
                    neighborPosition[i] = node.ConnectedNeighbors[i].Position;
                }
                trigger.GetComponent <MindWarpController>().NeighborPosition = neighborPosition;
            }
        }
        Object.Destroy(prefabInst);
        // Set statics
        MindWarpController.TriggerProb    = triggerProb;
        MindWarpController.IntensityLevel = intensity;
    }
Exemple #11
0
    /// <summary>
    /// Generate gravity frame from maze frame object, should only be used when paths between nodes are perfectly straight.
    /// </summary>
    /// <param name="mazeFrame">MazeFrame object.</param>
    private void GenerateGravityFrameFromMazeObj(MazeFrame mazeFrame, float closebyDist)
    {
        // Generate gravity frame based on maze nodes
        gravityFrame = new List <GravityNode>(mazeFrame.Nodes.Count);

        // First create base list, then add neighbors
        foreach (MazeNode mazeNode in mazeFrame.Nodes)
        {
            // Create gravity node from maze node
            GravityNode gravNode = new GravityNode(mazeNode.Position, mazeNode.Identifier);
            gravityFrame.Add(gravNode);
        }
        for (int i = 0; i < gravityFrame.Count; i++)
        {
            foreach (MazeNode neighbor in mazeFrame.Nodes[i].ConnectedNeighbors)
            {
                gravityFrame[i].neighbors.Add(gravityFrame.Find(x => x.identifier == neighbor.Identifier));
            }
        }

        // Add closeby list to each node
        AddClosebyNodes(closebyDist);
    }
    private Dictionary <string, Vector3> PopulateWithShape(MazeFrame mazeFrame, float fracConnSpace, int nNodesPerConnectionObj, string baseShape, string connShape)
    {
        // Set space allowed for connection as fraction of average connection length between nodes
        fracConnSpace = Mathf.Clamp01(fracConnSpace);
        float connSpace = (mazeFrame.Scale[0] + mazeFrame.Scale[1] + mazeFrame.Scale[2]) / 3 * fracConnSpace;

        // Prep storing of object center line nodes
        //int nNodesPerConnectionObj = 3;
        int nNodesPerBaseObj = 2;
        int nNodes           = (mazeFrame.NumberOfConnections() * nNodesPerBaseObj) + (mazeFrame.NumberOfUniquePairwiseConnections() * nNodesPerConnectionObj);
        Dictionary <string, Vector3> objCenterLineNodes = new Dictionary <string, Vector3>(nNodes);


        // Load prefabs and set parent for game objects
        GameObject prefabCyl     = Resources.Load("Prefabs/" + baseShape) as GameObject;
        GameObject prefabCylConn = Resources.Load("Prefabs/" + connShape) as GameObject;
        GameObject mazeObjects   = new GameObject("mazeObjects");

        mazeObjects.transform.position = Vector3.zero;
        mazeObjects.transform.rotation = Quaternion.identity;
        mazeObjects.AddComponent <MeshFilter>();
        mazeObjects.AddComponent <MeshRenderer>();
        mazeObjects.GetComponent <MeshRenderer>().material = prefabCyl.GetComponent <MeshRenderer>().sharedMaterial;

        // First, go through each node and built appropriately bended connections
        foreach (MazeNode node in mazeFrame.Nodes)
        {
            // Check whether we're at a dead end
            if (node.ConnectedNeighbors.Count <= 1)
            {
                continue;
            }
            // Place connection object and pass center line nodes to main dictionary
            Dictionary <string, Vector3> newCenterLineNodes  = PlaceShapeConnectionAtJunction(mazeObjects, node, prefabCylConn, connSpace, nNodesPerConnectionObj);
            Dictionary <string, Vector3> .KeyCollection keys = newCenterLineNodes.Keys;
            foreach (string key in keys)
            {
                objCenterLineNodes.Add(key, newCenterLineNodes[key]);
            }

            // Increment global counter
            MazeObjectsPlaced++;
        }


        // Then, build the base connections (store visit record)
        Dictionary <string, bool> isVisited = new Dictionary <string, bool>(mazeFrame.Nodes.Count);

        foreach (MazeNode nd in mazeFrame.Nodes)
        {
            isVisited.Add(nd.Identifier, false);
        }
        foreach (MazeNode node in mazeFrame.Nodes)
        {
            foreach (MazeNode neighbor in node.ConnectedNeighbors)
            {
                // Check whether all connections involving this neighbor have been built already
                if (isVisited[neighbor.Identifier])
                {
                    continue;
                }

                // set object properties
                Vector3 position = (node.Position + neighbor.Position) / 2;
                float   scale    = Vector3.Distance(node.Position, neighbor.Position);
                scale = scale - connSpace;
                Quaternion rot = prefabCyl.transform.rotation * Quaternion.FromToRotation(Vector3.forward, Vector3.Normalize(neighbor.Position - node.Position));

                // Check dead end ends/start/end
                if (node.ConnectedNeighbors.Count == 1 || node.Identifier == "start" || node.Identifier == "end")
                {
                    position = position + ((node.Position - neighbor.Position).normalized * (connSpace / 4));
                    scale    = scale + (connSpace / 2);
                }
                if (neighbor.ConnectedNeighbors.Count == 1 || neighbor.Identifier == "start" || neighbor.Identifier == "end")
                {
                    position = position + ((neighbor.Position - node.Position).normalized * (connSpace / 4));
                    scale    = scale + (connSpace / 2);
                }

                // Place object and set scale
                GameObject cyl = Object.Instantiate(prefabCyl, position, rot, mazeObjects.transform);
                cyl.transform.localScale = new Vector3(cyl.transform.localScale.x, cyl.transform.localScale.y, cyl.transform.localScale.z * scale);
                cyl.name = "base-" + node.Identifier + "-" + neighbor.Identifier;
                cyl.AddComponent <MeshCollider>();
                cyl.GetComponent <MeshCollider>().convex = true;

                // Add two nodes (start/end) to storage with naming scheme: base-<nodeid>-<neighid>
                // If on dead end ends/start/end --> nodes at the extremes
                // If not --> shift nodes from the extremes to the center (as the conn nodes start at the extremes as well
                Vector3 nodeGravPos;
                Vector3 neighbGravPos;
                if (node.ConnectedNeighbors.Count == 1 || node.Identifier == "start" || node.Identifier == "end")
                {
                    nodeGravPos = position + (node.Position - neighbor.Position).normalized * (scale / 2);
                }
                else
                {
                    nodeGravPos = position + (node.Position - neighbor.Position).normalized * (scale / 4);
                }
                if (neighbor.ConnectedNeighbors.Count == 1 || neighbor.Identifier == "start" || neighbor.Identifier == "end")
                {
                    neighbGravPos = position + (neighbor.Position - node.Position).normalized * (scale / 2);
                }
                else
                {
                    { neighbGravPos = position + (neighbor.Position - node.Position).normalized * (scale / 4); }
                }
                objCenterLineNodes.Add("base-" + node.Identifier + "-" + neighbor.Identifier, nodeGravPos);
                objCenterLineNodes.Add("base-" + neighbor.Identifier + "-" + node.Identifier, neighbGravPos);
            }

            // Set current node as visisted
            isVisited[node.Identifier] = true;

            // Increment global counter
            MazeObjectsPlaced++;
        }
        //Utilities.MergeChildrenOfParent(mazeObjects);
        return(objCenterLineNodes);
    }
 /// <summary>
 /// Updates the MazeObject for the next frame to be drawn.
 /// </summary>
 /// <param name="time">Time elapsed during gameplay.</param>
 /// <param name="frame"> The frame that is currently drawn, used for storing playSessions for evaluation</param>
 /// <returns>An updated version of the frame-parameter, with all the relevant information of this MazeObject</returns>
 internal abstract MazeFrame Update(GameTime time, MazeFrame frame);
    /// <summary>
    /// Label all nodes according to whether they are (1) on the shortest path, (2) are on a loop to the shortest path,
    /// (3) are on a path resulting in a dead end, and (4) are not connected to the maze.
    /// All labels are inclusive, i.e. the starting point of a dead end is a node labeled as on the shorted path.
    /// The shortest path is found using Dijkstra's algorithm.
    /// </summary>
    /// <returns>List of nodes with path labels.</returns>
    /// <param name="mazeFrame">List of nodes.</param>
    protected void LabelNodesWRTPath(ref MazeFrame mazeFrame)
    {
        //
        // This function labels each node according to the following booleans:
        //
        // onShortestPath - whether node is on the path found using psuedo Dijkstra's method
        // onLoop - whether node is on path that diverges from the shortest path, but returns to it eventually
        // onDeadEnd - whether the node is on a dead end, defined as a path that (1) diverges from the shortest path and doesn't return
        // notConnectedToPath - whether node has a connection on path or is superfluous visual filler
        // (junction nodes have more than one label)

        // Find shortest path from beginning to end using Dijkstra's algorithm
        // From wikipedia/other:

        /*
         * 1) Mark all nodes unvisited, mark selected initial node with a current distance of 0 and the rest with infinity.
         * 2) Set the non-visited node with the smallest current distance as the current node C.
         * 3) For each neighbour N of current node C: add the current distance of C with the weight of the edge
         *    connecting C-N. If it's smaller than the current distance of N, set it as the new current distance of N.
         * 4) Mark the current node C as visited.
         * 5) If end not touched yet, and if there are non-visited nodes, go to step 2.
         */

        // Check start/end node active status
        if (!mazeFrame.StartNode.IsActive || !mazeFrame.EndNode.IsActive)
        {
            throw new System.Exception("Start/end nodes are required to be active.");
        }

        // Prep storage of labels and initilaze as false
        int activeNodeCount = 0;

        foreach (MazeNode nd in mazeFrame.Nodes)
        {
            if (nd.IsActive)
            {
                activeNodeCount++;
            }
        }
        Dictionary <string, bool> onShortestPath     = new Dictionary <string, bool>(activeNodeCount);
        Dictionary <string, bool> onDeadEnd          = new Dictionary <string, bool>(activeNodeCount);
        Dictionary <string, bool> onLoop             = new Dictionary <string, bool>(activeNodeCount);
        Dictionary <string, bool> notConnectedToPath = new Dictionary <string, bool>(activeNodeCount);

        foreach (MazeNode nd in mazeFrame.Nodes)
        {
            if (nd.IsActive)
            {
                onShortestPath.Add(nd.Identifier, false);
                onDeadEnd.Add(nd.Identifier, false);
                onLoop.Add(nd.Identifier, false);
                notConnectedToPath.Add(nd.Identifier, false);
            }
        }

        // Find start and end node
        MazeNode startNode = mazeFrame.StartNode;
        MazeNode endNode   = mazeFrame.EndNode;

        // Create bookkeeping for visits and set all nodes to unvisitied
        Dictionary <string, bool> isVisited = new Dictionary <string, bool>(mazeFrame.Nodes.Count);

        foreach (MazeNode nd in mazeFrame.Nodes)
        {
            if (nd.IsActive)
            {
                isVisited.Add(nd.Identifier, false);
            }
            else
            {
                isVisited.Add(nd.Identifier, true);
            }
        }

        // Ignore nodes without neighbors
        foreach (MazeNode nd in mazeFrame.Nodes)
        {
            if (nd.ConnectedNeighbors.Count == 0)
            {
                isVisited[nd.Identifier] = true;
            }
        }

        // Set distance to start, and initialize at infinity
        Dictionary <string, float> distToStart = new Dictionary <string, float>(activeNodeCount);

        foreach (MazeNode nd in mazeFrame.Nodes)
        {
            if (nd.IsActive)
            {
                distToStart.Add(nd.Identifier, float.PositiveInfinity);
            }
        }

        // Start search
        MazeNode currentNode;

        distToStart[startNode.Identifier] = 0;
        int  count    = 0;
        bool endFound = false;

        while (!endFound)
        {
            // Set current unvisited node based on distance to start
            int   currentNodeInd = -1;
            float lastDist       = float.PositiveInfinity;
            for (int i = 0; i < mazeFrame.Nodes.Count; i++)
            {
                if (!isVisited[mazeFrame.Nodes[i].Identifier])
                {
                    float currDist = distToStart[mazeFrame.Nodes[i].Identifier];
                    if (currDist <= lastDist)
                    {
                        currentNodeInd = i; lastDist = currDist;
                    }
                }
            }
            currentNode = mazeFrame.Nodes[currentNodeInd];

            // Find unvisited neighbors of current node
            List <MazeNode> unvisitedNeighbors = new List <MazeNode>();
            foreach (MazeNode nb in currentNode.ConnectedNeighbors)
            {
                if (isVisited[nb.Identifier] == false)
                {
                    unvisitedNeighbors.Add(nb);
                }
            }

            // For each neighbor, set distToStart as min of (currentNode disttostart + dist to current) and neighbor disttostart
            foreach (MazeNode nb in unvisitedNeighbors)
            {
                // Distance in case of non-equidistant nodes
                //float distToCurrentNode = Vector3.Distance(currentNode.position, nb.position);
                // Distance in case of equidistant nodes
                float distToCurrentNode = 1;
                // Update neighbor
                distToStart[nb.Identifier] = Mathf.Min((distToStart[currentNode.Identifier] + distToCurrentNode), distToStart[nb.Identifier]);
            }

            // Set current node to visisted
            isVisited[currentNode.Identifier] = true;

            // end reached?
            endFound |= currentNode == endNode;

            // Safety check
            count++;
            if (count > mazeFrame.Nodes.Count)
            {
                throw new System.Exception("Finding shortest path loop did not terminate correctly.");
            }
        }

        // Store shortest path length in maze frame now that we know it
        int shortestPathLength = (int)distToStart[endNode.Identifier];

        // Backtrack along distToStart and label each node as being on the shortest path
        currentNode = endNode;
        onShortestPath[currentNode.Identifier] = true;
        count = 0;
        while (currentNode != startNode)
        {
            // Find neighbor with shortest distance to start
            MazeNode nextNodeOnPath = currentNode;
            foreach (MazeNode nb in currentNode.ConnectedNeighbors)
            {
                if (nb.IsActive && distToStart[nb.Identifier] < distToStart[nextNodeOnPath.Identifier]) // There is ALWAYS a node closer than the current one
                {
                    nextNodeOnPath = nb;
                }
            }

            // Update and continue
            currentNode = nextNodeOnPath;
            onShortestPath[currentNode.Identifier] = true;

            // Safety check
            count++;
            if (count > mazeFrame.Nodes.Count)
            {
                throw new System.Exception("Finding shortest path loop did not terminate correctly.");
            }
        }



        /* Label each node as onLoop/onDeadEnd/notConnectedToPath as follows:
         *
         * 1) Find all nodes that are adjacent to the path
         * 2) For each of these:
         *   3) Create stack to keep unlabeled nodes, and set allLabeled flag
         *   4) While !allLabeled
         *      5) Create list of unlabeled neighbors of current node that are not on the stack
         *      6) If current node has 1+ unlabeled neighbors --> explore
         *         7) Add current node to stack and set an unlabeled neighbor not on the stack as current node
         *      8) If current node has 0 unlabeled neighbors not on the stack --> label and backtrack
         *         9) If current node has only 1 connected neighbor --> onDeadEnd
         *        10) If more than one connected neighbors, and one was onLoop --> onLoop
         *        11) If more than one connected neighbors, and one of them was onShortestPath (or two if at seed node) --> onLoop (loop connection found!)
         *        12) If more than one connected neighbors, and no onLoop/onShortestPath --> onDeadEnd (backtracking from only dead ends)
         *     13) If stack is not empty, pop node and set as current node, go to 5)
         *     14) If stack is empty, all nodes are labeled, set allLabeled to true;
         *
         * 15) Set all junctions by finding all nodes adjacent to onDeadEnd/onLoop, and set them likewise.
         * 16) Set all unlabeled nodes to notConnectedToPath
         */

        // First, find unlabeled nodes that are connected to the path (1)
        List <MazeNode> unlabeledSeedNode = new List <MazeNode>();

        for (int i = 0; i < mazeFrame.Nodes.Count; i++)
        {
            if (mazeFrame.Nodes[i].IsActive && !onShortestPath[mazeFrame.Nodes[i].Identifier])
            {
                foreach (MazeNode nb in mazeFrame.Nodes[i].ConnectedNeighbors)
                {
                    if (nb.IsActive && onShortestPath[nb.Identifier])
                    {
                        unlabeledSeedNode.Add(mazeFrame.Nodes[i]); break;
                    }
                }
            }
        }

        // Start the search (2)
        for (int inode = 0; inode < unlabeledSeedNode.Count; inode++)
        {
            // set seed node
            MazeNode seedNode = unlabeledSeedNode[inode];
            if (onDeadEnd[seedNode.Identifier] || onLoop[seedNode.Identifier]) // check whether node was touched from a previous cycle below
            {
                continue;
            }

            // Create stack for to be labeled nodes
            Stack <MazeNode> nodesToLabel = new Stack <MazeNode>(); // FIXME should I initialize with conservative estimate?

            // Search from the starting node until the stack is empty (all are labeled) (4)
            count       = 0;
            currentNode = seedNode;
            bool allLabeled = false;
            while (!allLabeled)
            {
                // Parse current neighbors
                List <MazeNode> unlabeledNeighborsNotOnStack = new List <MazeNode>();
                bool            hasOnShortestPath            = false;
                bool            hasOnLoop         = false;
                int             shortestPathCount = 0;
                foreach (MazeNode nb in currentNode.ConnectedNeighbors)
                {
                    if (nb.IsActive)
                    {
                        if (onShortestPath[nb.Identifier])
                        {
                            shortestPathCount++;
                        }
                        else if (onLoop[nb.Identifier])
                        {
                            hasOnLoop = true;
                        }
                        else if (onDeadEnd[nb.Identifier])
                        {
                        }
                        else
                        {
                            if (!nodesToLabel.Contains(nb))
                            {
                                unlabeledNeighborsNotOnStack.Add(nb);
                            }
                        }
                    }
                }
                // If we're at the seed node, the first onShortestPath node is ignored (it's the hook), otherwise any one is fine
                if (currentNode == seedNode)
                {
                    hasOnShortestPath = shortestPathCount > 1;
                }
                else
                {
                    hasOnShortestPath = shortestPathCount > 0;
                }

                // Move forward or label and backtrack
                // If current node has an unlabeled node not on the stack --> explore (6)
                if (unlabeledNeighborsNotOnStack.Count > 0)
                {
                    // Add current node to stack and set first neighbor not on stack (order doesn't matter) as current node
                    foreach (MazeNode nb in unlabeledNeighborsNotOnStack)
                    {
                        nodesToLabel.Push(currentNode);
                        currentNode = nb;
                        break;
                    }
                }
                else
                // If there are no more unlabeled nodes that are not on the stack, we can label and backtrack
                {
                    int activeConnectedNeighborsCount = 0;
                    foreach (MazeNode nb in currentNode.ConnectedNeighbors)
                    {
                        if (nb.IsActive)
                        {
                            activeConnectedNeighborsCount++;
                        }
                    }
                    if (activeConnectedNeighborsCount <= 1) // easiest case
                    {
                        onDeadEnd[currentNode.Identifier] = true;
                    }
                    else if (hasOnLoop || (hasOnShortestPath && currentNode != seedNode))
                    {
                        onLoop[currentNode.Identifier] = true;
                    }
                    else
                    {
                        onDeadEnd[currentNode.Identifier] = true;
                    }

                    // Backtrack if there are still unlabeled, otherwise end
                    if (nodesToLabel.Count != 0)
                    {
                        currentNode = nodesToLabel.Pop();
                    }
                    else
                    {
                        allLabeled = true;
                    }
                }

                // Safety check
                count++;
                if (count > mazeFrame.Nodes.Count * 2)
                {
                    throw new System.Exception("Labeling nodes loop did not terminate correctly.");
                }
            }
        }

        // Set junctions
        // onDeadEnd junctions
        List <MazeNode> nodesNeighboringOnDeadEnd = new List <MazeNode>();
        List <MazeNode> nodesNeighboringOnLoop    = new List <MazeNode>();

        for (int i = 0; i < mazeFrame.Nodes.Count; i++)
        {
            if (!mazeFrame.Nodes[i].IsActive)
            {
                continue;
            }
            // Every node neighboring an onDeadEnd is a junction for a dead ending path
            foreach (MazeNode nb in mazeFrame.Nodes[i].ConnectedNeighbors)
            {
                if (nb.IsActive && onDeadEnd[nb.Identifier])
                {
                    nodesNeighboringOnDeadEnd.Add(mazeFrame.Nodes[i]); break;
                }
            }

            // Only nodes that are onShortestPath neighboring an onLoop can be an onLoop junction
            if (onShortestPath[mazeFrame.Nodes[i].Identifier])
            {
                foreach (MazeNode nb in mazeFrame.Nodes[i].ConnectedNeighbors)
                {
                    if (nb.IsActive && onLoop[nb.Identifier])
                    {
                        nodesNeighboringOnLoop.Add(mazeFrame.Nodes[i]); break;
                    }
                }
            }
        }
        foreach (MazeNode nd in nodesNeighboringOnDeadEnd)
        {
            onDeadEnd[nd.Identifier] = true;
        }
        foreach (MazeNode nd in nodesNeighboringOnLoop)
        {
            onLoop[nd.Identifier] = true;
        }

        // Find remaining nodes, and label them as notConnectedToPath
        foreach (MazeNode nd in mazeFrame.Nodes)
        {
            if (nd.IsActive && !onShortestPath[nd.Identifier] && !onLoop[nd.Identifier] && !onDeadEnd[nd.Identifier])
            {
                notConnectedToPath[nd.Identifier] = true;
            }
        }

        // Assign elements to maze frame
        int  shortestPathInd = 0;
        bool indNotFound     = true;

        while (indNotFound)
        {
            if (mazeFrame.ShortestPathInd.Contains(shortestPathInd))
            {
                shortestPathInd++;
            }
            else
            {
                indNotFound = false;
            }
        }
        mazeFrame.SetShortestPathLength(shortestPathLength, shortestPathInd);
        mazeFrame.SetOnShortestPath(onShortestPath, shortestPathInd);
        mazeFrame.SetOnDeadEnd(onDeadEnd);
        mazeFrame.SetOnLoop(onLoop);
        mazeFrame.SetNotConnectedToPath(notConnectedToPath);
    }
    /// <summary>
    /// Build connections inside maze frame using hunt and kill algorithm.
    /// </summary>
    /// <param name="mazeFrame">Maze frame object.</param>
    /// <param name="huntAfterSegmentLength">Enter hunting mode after segment has grown to this length in world units,
    /// defaults to unlimited (uses smalles Scale value.</param>
    /// <param name="huntProbability">Probability of starting hunt when it arrives, defaults to 0.50.</param>
    protected void BuildMazeFrameUsingHuntAndKill(ref MazeFrame mazeFrame, float huntAfterSegmentLength = float.PositiveInfinity, float huntProbability = 0.50f)
    {
        if (mazeRandGen == null)
        {
            mazeRandGen = new ConsistentRandom();
        }
        if (randGen == null)
        {
            randGen = new ConsistentRandom();
        }

        // Build maze using hunt and kill

        /* 1) Make the initial node the current node and mark it as visited
         * 2) While there are unvisited nodes
         *      1) If the current node has any neighbours which have not been visited
         *          1) Choose randomly one of the unvisited neighbours
         *          2) Remove the wall between the current node and the chosen node
         *          3) Make the chosen node the current node and mark it as visited and add to targets
         *      2) Find random unvisited node next to visited node
         *          1) Make it the current node and mark it as visited and add to targets
         */

        // Check start/end node active status
        if (!mazeFrame.StartNode.IsActive || !mazeFrame.EndNode.IsActive)
        {
            throw new System.Exception("Start/end nodes are required to be active.");
        }

        // Set storage of visit flags and create list for hunt to target
        int nNodesVisited = 0;
        Dictionary <string, bool> isVisited = new Dictionary <string, bool>(mazeFrame.Nodes.Count);

        foreach (MazeNode nd in mazeFrame.Nodes)
        {
            if (nd.IsActive)
            {
                isVisited.Add(nd.Identifier, false);
            }
            else
            {
                isVisited.Add(nd.Identifier, true); nNodesVisited++;
            }
        }
        List <MazeNode> targets = new List <MazeNode>(Mathf.RoundToInt(mazeFrame.Nodes.Count / 2f)); // half of full maze should be more than enough

        // Ignore nodes without neighbors
        foreach (MazeNode nd in mazeFrame.Nodes)
        {
            if (nd.AllNeighbors.Count == 0)
            {
                isVisited[nd.Identifier] = true; nNodesVisited++;
            }
        }

        // Determine huntAfterNConnections
        int huntAfterNConnections = Mathf.RoundToInt(huntAfterSegmentLength / Scale.ComponentMin());

        //Initialize near startNode
        MazeNode currentNode = mazeFrame.StartNode;

        isVisited[currentNode.Identifier] = true;
        nNodesVisited++;
        targets.Add(currentNode);
        int count            = 0;
        int nConnectionsMade = 0;

        while (nNodesVisited < mazeFrame.Nodes.Count)
        {
            // See if there are unvisited neighbors
            List <MazeNode> unvisitedNeighbors = new List <MazeNode>();
            foreach (MazeNode nb in currentNode.AllNeighbors)
            {
                if (!isVisited[nb.Identifier])
                {
                    unvisitedNeighbors.Add(nb);
                }
            }

            if (nConnectionsMade >= huntAfterNConnections)
            {
                if (mazeRandGen.NextDouble() < huntProbability)
                {
                    nConnectionsMade = 0;
                }
            }
            if (unvisitedNeighbors.Count > 0 && nConnectionsMade < huntAfterNConnections)
            {
                // Select random neighbor (2)
                MazeNode selNode = unvisitedNeighbors[mazeRandGen.Next(unvisitedNeighbors.Count)];
                // Add as connected neighbor to current node object(3) and vice versa
                currentNode.AddConnectionByReference(selNode);
                // Switch to new node(4) and record visit
                currentNode = selNode;
                isVisited[currentNode.Identifier] = true;
                nNodesVisited++;
                targets.Add(currentNode);
                nConnectionsMade++;
            }
            else
            {
                // Find random unvisited node neighboring a visited node
                List <int> targetInd = new List <int>(targets.Count);
                for (int i = 0; i < targets.Count; i++)
                {
                    targetInd.Insert(i, i);
                }
                bool             found           = false;
                Stack <MazeNode> targetsToRemove = new Stack <MazeNode>();
                int huntCount = 0;
                while (!found)
                {
                    // sample a random target node
                    int      randInd    = targetInd[mazeRandGen.Next(targetInd.Count)];
                    MazeNode currTarget = targets[randInd];
                    // See if target has unvisited neighbors
                    unvisitedNeighbors = new List <MazeNode>();
                    foreach (MazeNode nb in currTarget.AllNeighbors)
                    {
                        if (!isVisited[nb.Identifier])
                        {
                            unvisitedNeighbors.Add(nb);
                        }
                    }
                    // if it has no unvisited neighors, remove target from list and go again, otherwise, pick random unvisited neighbor
                    if (unvisitedNeighbors.Count == 0)
                    {
                        targetInd.Remove(randInd);
                        targetsToRemove.Push(currTarget);
                    }
                    else
                    {
                        // Select random neighbor and set as currentNode
                        currentNode = currTarget;
                        found       = true;
                    }
                    // Reset connections counter
                    nConnectionsMade = 0;

                    // Safety check
                    huntCount++;
                    if (huntCount > targets.Count)
                    {
                        throw new System.Exception("Hunt did not terminate correctly.");
                    }
                }
                // Remove nodes from targets that had no unvisisted neigbors
                while (targetsToRemove.Count > 0)
                {
                    targets.Remove(targetsToRemove.Pop());
                }
            }

            // Safety check
            count++;
            if (targets.Count == 0 || count > Mathf.Pow(mazeFrame.Nodes.Count, 2))
            {
                throw new System.Exception("Hunt and kill loop did not terminate correctly.");
            }
        }

        // Sanity check
        foreach (MazeNode nb in mazeFrame.Nodes)
        {
            if (!isVisited[nb.Identifier])
            {
                throw new System.Exception("Hunt and kill loop did not terminate correctly.");
            }
        }
    }
    /// <summary>
    /// /// Build connections inside maze frame using recursive backtracking algorithm.
    /// </summary>
    /// <param name="mazeFrame">Maze frame object.</param>
    protected void BuildMazeFrameUsingRecursiveBacktracking(ref MazeFrame mazeFrame)
    {
        if (mazeRandGen == null)
        {
            mazeRandGen = new ConsistentRandom();
        }
        if (randGen == null)
        {
            randGen = new ConsistentRandom();
        }

        // Build maze using recursive backtracking
        // From wikipedia:

        /* 1) Make the initial node the current node and mark it as visited
         * 2) While there are unvisited nodes
         *      1) If the current node has any neighbours which have not been visited
         *          1) Choose randomly one of the unvisited neighbours
         *          2) Push the current node to the stack
         *          3) Remove the wall between the current node and the chosen node
         *          4) Make the chosen node the current node and mark it as visited
         *      2) Else if stack is not empty
         *          1) Pop a node from the stack
         *          2) Make it the current node
         */

        // Check start/end node active status
        if (!mazeFrame.StartNode.IsActive || !mazeFrame.EndNode.IsActive)
        {
            throw new System.Exception("Start/end nodes are required to be active.");
        }

        // Set storage of visit flags and create stack for visisted nodes
        int nNodesVisited = 0;
        Dictionary <string, bool> isVisited = new Dictionary <string, bool>(mazeFrame.Nodes.Count);

        foreach (MazeNode nd in mazeFrame.Nodes)
        {
            if (nd.IsActive)
            {
                isVisited.Add(nd.Identifier, false);
            }
            else
            {
                isVisited.Add(nd.Identifier, true); nNodesVisited++;
            }
        }
        Stack <MazeNode> nodeTrack = new Stack <MazeNode>(mazeFrame.Nodes.Count); // FIXME does preallocating help performance here?

        // Ignore nodes without neighbors
        foreach (MazeNode nd in mazeFrame.Nodes)
        {
            if (nd.AllNeighbors.Count == 0)
            {
                isVisited[nd.Identifier] = true; nNodesVisited++;
            }
        }

        //Initialize near startNode
        MazeNode currentNode = mazeFrame.StartNode;

        isVisited[currentNode.Identifier] = true;
        nNodesVisited++;
        while (nNodesVisited < mazeFrame.Nodes.Count)
        {
            // See if there are unvisited neighbors
            List <MazeNode> unvisitedNeighbors = new List <MazeNode>();
            foreach (MazeNode nb in currentNode.AllNeighbors)
            {
                if (!isVisited[nb.Identifier])
                {
                    unvisitedNeighbors.Add(nb);
                }
            }

            if (unvisitedNeighbors.Count != 0)
            {
                // Add current node to stack (1)
                nodeTrack.Push(currentNode);
                // Select random neighbor (2)
                //MazeNode selNode = RandomlySelectNodeWithBiasTowardsForwards(currentNode, unvisitedNeighbors);
                //Node selNode = RandomlySelectNodeWithBiasTowardsNode(currentNode, unvisitedNeighbors, endNode.AllNeighbors[0], 2,mazeRandGen);
                MazeNode selNode = unvisitedNeighbors[mazeRandGen.Next(unvisitedNeighbors.Count)];
                // Add as connected neighbor to current node object(3) and vice versa
                currentNode.AddConnectionByReference(selNode);
                // Switch to new node(4) and record visit
                currentNode = selNode;
                isVisited[currentNode.Identifier] = true;
                nNodesVisited++;
            }
            else if ((unvisitedNeighbors.Count == 0) && (nodeTrack.Count != 0))
            {
                currentNode = nodeTrack.Pop();
            }

            // Safety check
            if ((nodeTrack.Count == 0) && (nNodesVisited > mazeFrame.Nodes.Count))
            {
                throw new System.Exception("Recursive backtracking loop did not terminate correctly.");
            }
        }

        // Sanity check
        foreach (MazeNode nb in mazeFrame.Nodes)
        {
            if (isVisited[nb.Identifier] == false)
            {
                throw new System.Exception("Recursive backtracking loop did not terminate correctly.");
            }
        }
    }
Exemple #17
0
    private IEnumerator BuildNewMaze()
    {
        float mazeFrameElementsBuilt = 0;

        mazeFrameMeshesArePresent = false;
        // Initialize maze creator
        MazeFrameCreator mazeFrameCreator = null;

        switch (mazeShape)
        {
        case 0:
        {
            mazeFrameCreator = new MazeFrameCreatorSquare3D(mazeSize, nQuadrants)
            {
                Scale = mazeScale, Jitter = mazeJitter
            }; break;
        }

        case 1:
        {
            mazeFrameCreator = new MazeFrameCreatorMeshIcosahedron(nDivisions, nQuadrants)
            {
                Scale = mazeScale, Jitter = mazeJitter
            }; break;
        }
        }
        // Set random seed selector
        System.Random randGen = new ConsistentRandom();
        // Randomize order of difficulties
        int[] randomQuadrantIndices = Utilities.RandomIndices(nQuadrants);
        // Generate maze!
        if (nQuadrants != 0 && nQuadrants != difficulties.Length)
        {
            throw new System.ArgumentException("When using quadrants, nQuadrants and nDifficulties should be equal.");
        }
        List <MazeFrame> singleMazeFrames = new List <MazeFrame>(difficulties.Length);

        for (int iDifficulty = 0; iDifficulty < difficulties.Length; iDifficulty++)
        {
            List <MazeFrame> singleFrameSections = new List <MazeFrame>(nSections);
            for (int iSections = 0; iSections < nSections; iSections++)
            {
                // Create sections
                int quadrantInd;
                //if (nQuadrants != 0) { quadrantInd = iDifficulty; }
                if (nQuadrants != 0)
                {
                    quadrantInd = randomQuadrantIndices[iDifficulty];
                }
                else
                {
                    quadrantInd = 0;
                }
                MazeFrame currSection    = null;
                int       currDifficulty = difficulties[iDifficulty];
                int       currSeedInd    = randGen.Next(seedData[quadrantInd].seeds[currDifficulty].Length);
                mazeFrameCreator.RandomSeed = seedData[quadrantInd].seeds[currDifficulty][currSeedInd];
                //mazeFrameCreator.RandomSeed = seedData[quadrantInd].seeds[iDifficulty][iSections];
                if (nQuadrants == 0)
                {
                    currSection = mazeFrameCreator.GenerateMaze();
                }
                else
                {
                    currSection = mazeFrameCreator.GenerateEmptyMazeFrame();
                    mazeFrameCreator.GenerateMaze(ref currSection, quadrantInd);
                    currSection.SetOnShortestPath(currSection.Quadrants[quadrantInd], 0); // HACKY
                }
                //currSection.KeepOnlyShortestPathConnections();
                //currSection.AddPathSegments();
                // DEBUG
                if (nQuadrants != 0)
                {
                    Debug.Log(currSection.GetNIntersectionsOnPath()[0] + "  " + currSection.GetDifficulty(quadrantInd)[0] + " - " + seedData[quadrantInd].difficulties[currDifficulty][currSeedInd]);
                }
                else
                {
                    Debug.Log(currSection.GetNIntersectionsOnPath()[0] + "  " + currSection.GetDifficulty()[0]);
                }
                // DEBUG
                singleFrameSections.Add(currSection);
                mazeFrameElementsBuilt++;
                LevelBuiltProgressPercentage = (mazeFrameElementsBuilt / (difficulties.Length * nSections)) / 2;
                yield return(null);
            }
            MazeFrame currSingleFrame;
            if (singleFrameSections.Count > 1)
            { //MazeFrame currSingleFrame = MazeFrame.Concatenate(singleFrameSections, mazeFrameCreator.Scale, mazeDirection);
                currSingleFrame = MazeFrame.CombineShrink(singleFrameSections, shrinkFactor);
            }
            else
            {
                currSingleFrame = singleFrameSections[0];
            }
            currSingleFrame.AddOffsetStartNode(Vector3.Scale(mazeDirection, mazeScale) * startOffsetFactor, true);
            currSingleFrame.AddOffsetEndNode(Vector3.Scale(mazeDirection, mazeScale) * endOffsetFactor, true);
            for (int iInc = 0; iInc < iDifficulty; iInc++)
            {
                currSingleFrame.IncrementShortestPathIndices();
            }
            singleMazeFrames.Add(currSingleFrame);
        }
        if (singleMazeFrames.Count > 1)
        {
            mazeFrame = MazeFrame.Merge(singleMazeFrames);
        }
        else
        {
            mazeFrame = singleMazeFrames[0];
        }
        //mazeFrame.ConnectUnconnectedNodes();
        mazeFrame.AddPathSegments();
        yield return(null);

        // Populate maze and get return splines and maze objects
        mazePopulator.PopulateWithSplineFittedBars(mazeFrame, ref mazeFrameSplines, ref mazeObjects, objectScaling);
        //mazePopulator.PopulateWithSplineFittedCylinders(mazeFrame, ref mazeFrameSplines, ref mazeObjects, objectScaling);

        // Wait till population is complete
        while (!mazeFrameMeshesArePresent)
        {
            if (mazePopulator.MazeObjectsPlaced == mazeFrameSplines.SplineSegments.Count)
            {
                mazeFrameMeshesArePresent = true;
            }
            yield return(null);
        }

        // Create and Initialize gravity frame
        gravityFrameController = new GravityFrameController(mazeFrame, mazeFrameSplines, gravJunctionDistFactor, gravPlaneWidth);
        yield return(null);


        // Place others
        PlacePlayerAndCamera();
        PlaceEndPortal();
        PlaceCubeOfDeath();
        PlaceMindWarpTriggers();
    }
    /// <summary>
    /// Fit splines to maze frame as a new instance of <see cref="MazeFrameSplines"/>.
    /// </summary>
    /// <param name="mazeFrame">Maze frame.</param>
    public MazeFrameSplines(MazeFrame mazeFrame)
    {
        if (mazeFrame.PathSegments == null)
        {
            throw new System.ArgumentException("Maze frame needs to have path segments in order to fit splines to it.");
        }
        SplineSegments  = new List <SplineSegment>(mazeFrame.Nodes.Count); // decent estimate
        ShortestPathInd = mazeFrame.ShortestPathInd;
        StartPoint      = mazeFrame.StartNode.Position;
        EndPoint        = mazeFrame.EndNode.Position;

        // First, create all junction splines
        float           junctionSpace = .5f;
        List <MazeNode> junctionList  = new List <MazeNode>(mazeFrame.Nodes.Count / 2);

        foreach (MazeNode node in mazeFrame.Nodes)
        {
            if (node.ConnectedNeighbors.Count >= 3)
            {
                junctionList.Add(node);
            }
        }
        foreach (MazeNode junction in junctionList)
        {
            for (int ineighb1 = 0; ineighb1 < (junction.ConnectedNeighbors.Count - 1); ineighb1++)
            {
                for (int ineighb2 = ineighb1 + 1; ineighb2 < junction.ConnectedNeighbors.Count; ineighb2++)
                {
                    // Create spline
                    MazeNode neighb1 = junction.ConnectedNeighbors[ineighb1];
                    MazeNode neighb2 = junction.ConnectedNeighbors[ineighb2];
                    if (IsNodeConnectedToOrientedUp(junction))
                    {
                        if (!IsNodePositionOrientedUp(neighb1) && !IsNodePositionOrientedUp(neighb2))
                        {
                            continue;
                        }
                    }
                    Vector3[] pArray =
                    {
                        neighb1.Position + (junction.Position - neighb1.Position) * (1 - junctionSpace),
                        junction.Position,
                        junction.Position,
                        neighb2.Position + (junction.Position - neighb2.Position) * (1 - junctionSpace)
                    };
                    Spline spline = new Spline(pArray);
                    // Set start/end normals based on node identity
                    if (IsNodePositionOrientedUp(neighb1))
                    {
                        spline.SetRMFStartNormal(Vector3.up);
                    }
                    else
                    {
                        spline.SetRMFStartNormal(mazeFrame.Center - pArray[0]);
                    }
                    if (IsNodePositionOrientedUp(neighb2))
                    {
                        spline.SetRMFEndNormal(Vector3.up);
                    }
                    else
                    {
                        spline.SetRMFEndNormal(mazeFrame.Center - pArray[3]);
                    }
                    // Create new spline segment
                    //List<int> newShortestPathInd = new List<int>(neighb1.shortestPathInd);
                    //foreach (int ind in neighb2.shortestPathInd) { newShortestPathInd.AddIfNotPresent(ind); }
                    //List<int> newShortestPathInd = new List<int>();
                    //foreach (int ind in neighb1.shortestPathInd)
                    //{ if (neighb2.shortestPathInd.Contains(ind)) { newShortestPathInd.AddIfNotPresent(ind); } }
                    //foreach (int ind in neighb2.shortestPathInd)
                    //{ if (neighb1.shortestPathInd.Contains(ind)) { newShortestPathInd.AddIfNotPresent(ind); } }
                    List <string> identifiers = new List <string>(3)
                    {
                        junction.Identifier, neighb1.Identifier, neighb2.Identifier
                    };
                    SplineSegments.Add(new SplineSegment(spline, identifiers, junction.shortestPathInd, neighb1.shortestPathInd, neighb2.shortestPathInd, true));
                }
            }
        }
        // Then, add all regular segments
        foreach (List <MazeNode> path in mazeFrame.PathSegments)
        {
            // Skip path if it only consists of two junctions
            if (path.Count == 2 && path[0].ConnectedNeighbors.Count >= 3 && path[1].ConnectedNeighbors.Count >= 3)
            {
                continue;
            }
            // Create spline
            Vector3[] pArray = new Vector3[path.Count];
            for (int i = 0; i < path.Count; i++)
            {
                pArray[i] = path[i].Position;
            }
            if (path[0].ConnectedNeighbors.Count >= 3)
            {
                pArray[0] = pArray[0] + (pArray[1] - pArray[0]) * junctionSpace;
            }
            if (path[path.Count - 1].ConnectedNeighbors.Count >= 3)
            {
                pArray[path.Count - 1] = pArray[path.Count - 1] + (pArray[path.Count - 2] - pArray[path.Count - 1]) * junctionSpace;
            }
            Spline spline = new Spline(pArray);
            // Set start/end normals based on node identity
            if (IsNodePositionOrientedUp(path[0]))
            {
                spline.SetRMFStartNormal(Vector3.up);
            }
            else
            {
                spline.SetRMFStartNormal(mazeFrame.Center - pArray[0]);
            }
            if (IsNodePositionOrientedUp(path[path.Count - 1]))
            {
                spline.SetRMFEndNormal(Vector3.up);
            }
            else
            {
                spline.SetRMFEndNormal(mazeFrame.Center - pArray[path.Count - 1]);
            }
            // Get shortestPathInd, from an in-between node in path if possible
            List <int> newShortestPathInd = new List <int>();
            if (path.Count >= 3) // use first non-junction node
            {
                newShortestPathInd = new List <int>(path[1].shortestPathInd);
            }
            else if (path.Count == 2) // use union of only two nodes
            {
                foreach (int ind in path[0].shortestPathInd)
                {
                    if (path[1].shortestPathInd.Contains(ind))
                    {
                        newShortestPathInd.AddIfNotPresent(ind);
                    }
                }
                foreach (int ind in path[1].shortestPathInd)
                {
                    if (path[0].shortestPathInd.Contains(ind))
                    {
                        newShortestPathInd.AddIfNotPresent(ind);
                    }
                }
            }
            // Create new spline segment
            List <string> identifiers = new List <string>(path.Count);
            foreach (MazeNode node in path)
            {
                identifiers.Add(node.Identifier);
            }
            SplineSegments.Add(new SplineSegment(spline, identifiers, newShortestPathInd, path[0].shortestPathInd, path[path.Count - 1].shortestPathInd, false));
        }

        // Finally, define start/end neighbors
        foreach (SplineSegment baseSeg in SplineSegments)
        {
            // For start
            Vector3 currStart = baseSeg.StartPoint;
            foreach (SplineSegment otherSeg in SplineSegments)
            {
                if (baseSeg == otherSeg)
                {
                    continue;
                }
                if (otherSeg.StartPoint.ComponentsAreApproxEqualTo(currStart))
                {
                    baseSeg.startNeighbors.AddIfNotPresent(otherSeg);
                    otherSeg.startNeighbors.AddIfNotPresent(baseSeg);
                }
                if (otherSeg.EndPoint.ComponentsAreApproxEqualTo(currStart))
                {
                    baseSeg.startNeighbors.AddIfNotPresent(otherSeg);
                    otherSeg.endNeighbors.AddIfNotPresent(baseSeg);
                }
            }
            // For end
            Vector3 currEnd = baseSeg.EndPoint;
            foreach (SplineSegment otherSeg in SplineSegments)
            {
                if (baseSeg == otherSeg)
                {
                    continue;
                }
                if (otherSeg.StartPoint.ComponentsAreApproxEqualTo(currEnd))
                {
                    baseSeg.endNeighbors.AddIfNotPresent(otherSeg);
                    otherSeg.startNeighbors.AddIfNotPresent(baseSeg);
                }
                if (otherSeg.EndPoint.ComponentsAreApproxEqualTo(currEnd))
                {
                    baseSeg.endNeighbors.AddIfNotPresent(otherSeg);
                    otherSeg.endNeighbors.AddIfNotPresent(baseSeg);
                }
            }
        }
    }
Exemple #19
0
    /// <summary>
    /// Generate  gravity frame from splines fitted to maze frame.
    /// </summary>
    /// <param name="mazeFrameSplines">Splines fitted to maze frames.</param>
    ///// <param name="closebyDist"> Distance at which nodes are considered close by.</param>
    /// <param name="junctionDist"> Distance at which nodes are considered close to junction.</param>
    private void GenerateGravityFrameFromSplines(MazeFrame mazeFrame, MazeFrameSplines mazeFrameSplines, float junctionDist, float planeWidth)
    {
        // Generate gravity frame based on maze frame fitted splines
        int nodeCount = 0;

        foreach (MazeFrameSplines.SplineSegment splineSeg in mazeFrameSplines.SplineSegments)
        {
            nodeCount += splineSeg.spline.NSamplesToUse;
        }
        gravityFrame = new List <GravityNode>(nodeCount);

        // Generate sub frame to use later
        List <List <GravityNode> > segmentSubFrame = new List <List <GravityNode> >(mazeFrameSplines.SplineSegments.Count);

        // Create nodes for each splines
        for (int iSpline = 0; iSpline < mazeFrameSplines.SplineSegments.Count; iSpline++)
        {
            segmentSubFrame.Add(new List <GravityNode>(mazeFrameSplines.SplineSegments[iSpline].spline.NSamplesToUse));
            GravityNode prevNode = null;
            for (int i = 0; i < mazeFrameSplines.SplineSegments[iSpline].spline.NSamplesToUse; i++)
            {
                Vector3   currPos     = mazeFrameSplines.SplineSegments[iSpline].spline.GetPointOnSpline(mazeFrameSplines.SplineSegments[iSpline].spline.SampleIndToT(i));
                Vector3   currTang    = mazeFrameSplines.SplineSegments[iSpline].spline.GetTangentToPointOnSpline(mazeFrameSplines.SplineSegments[iSpline].spline.SampleIndToT(i)).normalized;
                Vector3   currNorm    = mazeFrameSplines.SplineSegments[iSpline].spline.DefaultGetNormalAtT(mazeFrameSplines.SplineSegments[iSpline].spline.SampleIndToT(i)).normalized;
                Vector3   cross       = Vector3.Cross(currTang, currNorm);
                Vector3[] planePoints = new Vector3[2] {
                    currPos + (cross * (planeWidth / 2f)), currPos - (cross * (planeWidth / 2f))
                };
                GravityNode node = new GravityNode(currPos, iSpline + "-" + currPos.ToString(), planePoints);
                if (prevNode != null)
                {
                    node.neighbors.Add(prevNode);
                    prevNode.neighbors.Add(node);
                }
                // Determine if near junction
                if (mazeFrameSplines.SplineSegments[iSpline].startNeighbors.Count > 0)
                {
                    if (Vector3.Distance(currPos, mazeFrameSplines.SplineSegments[iSpline].spline.GetPointOnSpline(0)) < junctionDist)
                    {
                        node.nearJunction = true;
                    }
                }
                if (mazeFrameSplines.SplineSegments[iSpline].endNeighbors.Count > 0)
                {
                    if (Vector3.Distance(currPos, mazeFrameSplines.SplineSegments[iSpline].spline.GetPointOnSpline(1)) < junctionDist)
                    {
                        node.nearJunction = true;
                    }
                }
                // Add to frame
                gravityFrame.Add(node);
                prevNode = node;
                // Add spline segment sub-frame as well
                segmentSubFrame[iSpline].Add(node);
            }
        }
        // Create closeby list from, and connect, neighboring splines
        for (int iSeg = 0; iSeg < mazeFrameSplines.SplineSegments.Count; iSeg++)
        {
            // Add frame to its own closeby
            List <GravityNode> currSubFrame = segmentSubFrame[iSeg];
            foreach (GravityNode node in currSubFrame)
            {
                node.closeby.AddRange(currSubFrame);
            }

            // Start/end of spline
            List <List <MazeFrameSplines.SplineSegment> > segList = new List <List <MazeFrameSplines.SplineSegment> >(2)
            {
                mazeFrameSplines.SplineSegments[iSeg].startNeighbors, mazeFrameSplines.SplineSegments[iSeg].endNeighbors
            };
            for (int iStartEnd = 0; iStartEnd <= 1; iStartEnd++)
            {
                foreach (MazeFrameSplines.SplineSegment neighSeg in segList[iStartEnd])
                {
                    // Get sub frame index
                    List <GravityNode> neighSegSF = segmentSubFrame[mazeFrameSplines.SplineSegments.IndexOf(neighSeg)];

                    // Add to closeby list
                    foreach (GravityNode node in currSubFrame)
                    {
                        node.closeby.AddRange(neighSegSF);
                    }

                    // Connect start/end
                    switch (iStartEnd)
                    {
                    case 0:
                    {
                        if (Vector3.Distance(currSubFrame[0].position, neighSegSF[0].position) <
                            Vector3.Distance(currSubFrame[0].position, neighSegSF[neighSegSF.Count - 1].position))
                        {
                            currSubFrame[0].neighbors.Add(neighSegSF[0]);
                        }
                        else
                        {
                            currSubFrame[0].neighbors.Add(neighSegSF[neighSegSF.Count - 1]);
                        }
                        break;
                    }

                    case 1:
                    {
                        if (Vector3.Distance(currSubFrame[currSubFrame.Count - 1].position, neighSegSF[0].position) <
                            Vector3.Distance(currSubFrame[currSubFrame.Count - 1].position, neighSegSF[neighSegSF.Count - 1].position))
                        {
                            currSubFrame[currSubFrame.Count - 1].neighbors.Add(neighSegSF[0]);
                        }
                        else
                        {
                            currSubFrame[currSubFrame.Count - 1].neighbors.Add(neighSegSF[neighSegSF.Count - 1]);
                        }
                        break;
                    }
                    }
                }
            }
        }
    }
Exemple #20
0
        /// <summary>
        /// Updates the maze for a new frame
        /// </summary>
        /// <param name="time">Time elapsed during gameplay</param>
        /// <returns>A frame describing everything if interest for the SessionManager</returns>
        internal Frame Update(GameTime time)
        {
            MazeFrame frame = new MazeFrame();

            // Update player
            frame = player.Update(time, frame);

            if (player.moving && game.BCIManager.IsConcentrating()) // Moving players cant concentrate
            {
                game.BCIManager.StopConcentrating();
            }
            else if (!player.moving && !game.BCIManager.IsConcentrating()) // Non-moving players should start concentrating
            {
                game.BCIManager.StartConcentrating();
            }

            //Update virus
            frame = Virus.Update(time, frame);

            // Update antibodies and prepare for possible deletion.
            toDelete = new List <Antibody>();
            foreach (Antibody antibody in antibodies)
            {
                frame = antibody.Update(time, frame);
            }

            // Update all letters
            foreach (Letter letter in Letters)
            {
                frame = letter.Update(time, frame);
            }

            // If the player is moving, we might add some antibodies (if we havent reached the maximum and some small chance is present)
            if (player.moving && rand.Next(1000) < 10 && antibodies.Count < MAX_ANTIBODIES)
            {
                // Get a random position for the antibody to appear at. Should be within a specific range of the player.
                float   angle      = MathHelper.TwoPi * (float)rand.NextDouble() - MathHelper.Pi;
                float   distance   = rand.Next(100, (int)(WindowWidth / 2.0f) - 20);
                Vector2 abPosition = new Vector2(player.Position.X + ((float)Math.Sin((double)angle) * distance), player.Position.Y + ((float)Math.Cos((double)angle) * distance));
                antibodies.Add(new Antibody(this, abPosition, Keywords.AntibodyWords[rand.Next(Keywords.AntibodyWords.Length)], TimeSpan.FromSeconds(rand.Next(5, 10)).TotalMilliseconds));
            }

            // Delete all antibodies that died.
            foreach (Antibody ab in toDelete)
            {
                antibodies.Remove(ab);
            }

            // Reset the player if he died.
            if (player.died)
            {
                game.gameover(false);
            }

            // Set information to the frame (for session manager)
            frame.BCIValue    = GetBCIManager().ConcentrationLevel;
            frame.LeanForward = GetMovementManager().MovesForward();
            frame.LeanLeft    = GetMovementManager().MovesLeft();
            frame.LeanRight   = GetMovementManager().MovesRight();
            return(frame);
        }
 public void PopulateWithSplineFittedBars(MazeFrame mazeFrame, ref MazeFrameSplines mazeFrameSplines, ref GameObject mazeObjects, Vector3 resize)
 {
     PopulateWithSplineFittedShape(mazeFrame, ref mazeFrameSplines, ref mazeObjects, resize, "MazeBar");
 }
 public Dictionary <string, Vector3> PopulateWithBars(MazeFrame mazeFrame, float fracConnSpace, int nNodesPerConnectionObj)
 {
     return(PopulateWithShape(mazeFrame, fracConnSpace, nNodesPerConnectionObj, "MazeBar", "MazeBarConn"));
 }
Exemple #23
0
        /// <summary>
        /// Updates the Antibody for the next frame to be drawn.
        /// </summary>
        /// <param name="time">Time elapsed during gameplay.</param>
        /// <param name="frame"> The frame that is currently drawn, used for storing playSessions for evaluation</param>
        /// <returns>An updated version of the frame-parameter, with all the relevant information of this Antibody</returns>
        internal override MazeFrame Update(GameTime time, MazeFrame frame)
        {
            // Case Attached Antibody
            if (attached)
            {
                timeSinceLevelRaise += time.ElapsedGameTime.Milliseconds; // Timer for next level raise.
                if (timeSinceLevelRaise > MillisecondsTimer)              // Required time since last Level Raise has passed.
                {
                    RaiseStage();                                         // Raise level.
                    timeSinceLevelRaise = 0;
                }
                // If the player has only been concentrated for a short while we reset the required concentration time.
                // This is due to the fact that, after lowering a stage, the player has to concentrate even longer to lower another stage.
                // But since concentration can be lost, this could be a big punishment for players.
                // As such, we only require the minimum concentration time every time the player has lost concentration.
                if (!maze.GetBCIManager().IsConcentratedFor(TimeSpan.FromSeconds(0.1).TotalMilliseconds))
                {
                    requiredConcentration = TimeSpan.FromSeconds(CONCENTRATION_TIME_PER_STATE).TotalMilliseconds;
                }
                // If concentrated long enough the stage should be lowered.
                if (maze.GetBCIManager().IsConcentratedFor(requiredConcentration))
                {
                    LowerStage();
                }
            }
            // Case the antibody is too far from the player or the killword was said.
            else if (Vector2.Distance(Position, maze.player.Position) > 400.0f || (Vector2.Distance(Position, maze.player.Position) < 150.0f && maze.GetSpeechManager().WordWasSaid(killWord)))
            {
                maze.toDelete.Add(this); // Delete the antibody.
            }
            else // Case antibody movement
            {
                Vector2 movementDirection = Vector2.Normalize(maze.player.Position - Position); // Move in the direction of the player.
                Vector2 oldPosition       = Position;
                bool    moving            = false;
                for (float i = Speed; i > 0 && !moving; i -= 0.2f) // Move by speed, unless collision occurs which results in slower movement.
                {
                    moving      = true;
                    Position.X += movementDirection.X * i; // Move in the direction at the given speed
                    Position.Y += movementDirection.Y * i;

                    // In case of collision reset the movement.
                    Matrix abTransformMatrix =
                        Matrix.CreateTranslation(new Vector3(-maze.antibodyTextures[stage].Width / 2.0f, -maze.antibodyTextures[stage].Height / 2.0f, 0.0f)) *
                        Matrix.CreateTranslation(new Vector3(Position, 0.0f));

                    if (maze.HitsMazeWall(abTransformMatrix, (int)maze.antibodyTextures[stage].Width, (int)maze.antibodyTextures[stage].Height, maze.ABCollisionData))
                    {
                        Position = oldPosition; // Reset movement
                    }
                }
                // Antibody should become attached due to short distance to player.
                if (Vector2.Distance(Position, maze.player.Position) < 20)
                {
                    attached      = true;
                    Position      = Position - maze.player.Position;                                   // Calculate relative position to the player and store position as such.
                    relativeAngle = (float)Math.Atan2(Position.X, -Position.Y) - maze.player.rotation; // Calculate the relative angle to the player ship. Used for rotating along.
                    maze.player.LowerSpeed();
                    timeSinceLevelRaise   = 0;
                    requiredConcentration = TimeSpan.FromSeconds(CONCENTRATION_TIME_PER_STATE).TotalMilliseconds; // Set required concentration time.
                }
            }
            // Give frame all relevant information.
            frame.Antibodies.Add(new AntibodyInfo()
            {
                Position = Position, Attached = attached, Killword = killWord, State = stage
            });
            return(frame);
        }
    private IEnumerator CreateMazesFromSeeds()
    {
        // Parse difficulty
        float[,] difficultyBounds = new float[nDifficulties, 2];
        float difficultyStartPad = difficultyLimits[0];
        float difficultyEndPad   = 1 - difficultyLimits[1];
        float diffRange          = 1 - (difficultyStartPad + difficultyEndPad);
        float currDiffSize       = difficultySize * diffRange;
        float diffSpacing        = (diffRange - (nDifficulties * currDiffSize)) / (nDifficulties - 1);

        if (diffSpacing < 0)
        {
            throw new System.Exception("Overlapping difficulties.");
        }
        for (int i = 0; i < (nDifficulties); i++)
        {
            difficultyBounds[i, 0] = difficultyStartPad + (currDiffSize + diffSpacing) * i;
            difficultyBounds[i, 1] = difficultyBounds[i, 0] + currDiffSize;
        }
        string boundsDisp = difficultyBounds[0, 0] + ">x<=" + difficultyBounds[0, 1];

        for (int i = 1; i < nDifficulties; i++)
        {
            boundsDisp = boundsDisp + " | " + difficultyBounds[i, 0] + ">x<=" + difficultyBounds[i, 1];
        }
        Debug.Log(boundsDisp);

        //seedList[0] = new List<int>(nMazes); // difficulty <=.20
        //seedList[0] = new List<int>(nMazes); // difficulty >.20 <=.40
        //seedList[0] = new List<int>(nMazes); // difficulty >.40 <=.60
        //seedList[0] = new List<int>(nMazes); // difficulty >.60 <=80
        //seedList[0] = new List<int>(nMazes); // difficulty >.80 <=1

        // Set random seed
        int mainRandomSeed = System.Environment.TickCount;
        ConsistentRandom mainRandomGenenerator = new ConsistentRandom(mainRandomSeed);

        // Setup maze creators
        MazeFrameCreator mazeFrameCreator = null;

        switch (mazeShape)
        {
        case 0: { mazeFrameCreator = new MazeFrameCreatorSquare3D(mazeSize, nQuadrants); break; }

        case 1: { mazeFrameCreator = new MazeFrameCreatorMeshIcosahedron(divisions, nQuadrants); break; }
        }
        mazeFrameCreator.Scale = mazeScale; // Used in hunt and kill for determining  when to hunt

        // Create level from new seeds
        int ittMax = 1;

        if (nQuadrants != 0)
        {
            ittMax = nQuadrants;
        }
        for (int iQuadrant = 0; iQuadrant < ittMax; iQuadrant++)
        {
            // Setup seed/difficulty list
            List <List <int> > seedList = new List <List <int> >(nDifficulties);
            for (int i = 0; i < nDifficulties; i++)
            {
                seedList.Add(new List <int>(nMazes));
            }
            List <List <float> > difficultyList = new List <List <float> >(nDifficulties);
            for (int i = 0; i < nDifficulties; i++)
            {
                difficultyList.Add(new List <float>(nMazes));
            }

            bool done  = false;
            int  count = 0;
            while (!done)
            {
                // set new seed
                int mazeSeed = mainRandomGenenerator.Next();
                mazeFrameCreator.RandomSeed = mazeSeed;
                // get maze
                MazeFrame mazeFrame = mazeFrameCreator.GenerateEmptyMazeFrame();
                if (nQuadrants != 0)
                {
                    mazeFrame.SetActiveQuadrant(iQuadrant);
                }
                mazeFrameCreator.GenerateMaze(ref mazeFrame);
                // Save according to difficulty
                float difficulty;
                if (nQuadrants == 0)
                {
                    difficulty = mazeFrame.GetDifficulty()[0];
                }
                else
                {
                    difficulty = mazeFrame.GetDifficulty(iQuadrant)[0];
                }
                for (int i = 0; i < nDifficulties; i++)
                {
                    if (difficulty > difficultyBounds[i, 0] && difficulty <= difficultyBounds[i, 1] && seedList[i].Count < nMazes)
                    {
                        seedList[i].Add(mazeSeed);
                        difficultyList[i].Add(difficulty);
                    }
                }

                // Check end condition
                done = true;
                for (int i = 0; i < nDifficulties; i++)
                {
                    done = done && seedList[i].Count == nMazes;
                }

                // safety check
                count++;
                if (count == count * 100)
                {
                    throw new System.Exception("Something went wrong.");
                }

                // Display progress
                string disp;
                if (nQuadrants == 0)
                {
                    disp = count + ": " + seedList[0].Count;
                }
                else
                {
                    disp = "quadrant" + (iQuadrant + 1) + "of" + nQuadrants + " | " + count + ": " + seedList[0].Count;
                }
                for (int i = 1; i < nDifficulties; i++)
                {
                    disp = disp + ", " + seedList[i].Count;
                }
                Debug.Log(disp + "  | d = " + difficulty.ToString("0.0000"));

                // Yield
                WaitForSecondsRealtime wait = null;
                if (wait == null)
                {
                    wait = new WaitForSecondsRealtime(0.0001f);
                }
                if ((count % 10) == 0)
                {
                    yield return(wait);
                }
            }

            // Convert to SeedData object
            if (nDifficulties == 5)
            {
                seedData = new SeedData(seedList[0].ToArray(), seedList[1].ToArray(), seedList[2].ToArray(), seedList[3].ToArray(), seedList[4].ToArray(),
                                        difficultyList[0].ToArray(), difficultyList[1].ToArray(), difficultyList[2].ToArray(), difficultyList[3].ToArray(), difficultyList[4].ToArray(),
                                        mazeSize, mazeFrameCreator.GetType().FullName);
            }
            else if (nDifficulties == 4)
            {// Convert to SeedData object
                seedData = new SeedData(seedList[0].ToArray(), seedList[1].ToArray(), seedList[2].ToArray(), seedList[3].ToArray(),
                                        difficultyList[0].ToArray(), difficultyList[1].ToArray(), difficultyList[2].ToArray(), difficultyList[3].ToArray(),
                                        mazeSize, mazeFrameCreator.GetType().FullName);
            }
            else if (nDifficulties == 3)
            {// Convert to SeedData object
                seedData = new SeedData(seedList[0].ToArray(), seedList[1].ToArray(), seedList[2].ToArray(),
                                        difficultyList[0].ToArray(), difficultyList[1].ToArray(), difficultyList[2].ToArray(),
                                        mazeSize, mazeFrameCreator.GetType().FullName);
            }
            else if (nDifficulties == 2)
            {// Convert to SeedData object
                seedData = new SeedData(seedList[0].ToArray(), seedList[1].ToArray(),
                                        difficultyList[0].ToArray(), difficultyList[1].ToArray(),
                                        mazeSize, mazeFrameCreator.GetType().FullName);
            }
            else
            {
                throw new System.Exception("Requested nDifficulty not implemented due to the stupid save part.");
            }

            // Save to disk
            string creatorName = "";
            string filePath;
            switch (mazeShape)
            {
            case 0: { creatorName = mazeFrameCreator.GetType().FullName + "-" + mazeSize.ToString() + "-" + mazeScale.ToString(); break; }

            case 1: { creatorName = mazeFrameCreator.GetType().FullName + "-" + divisions.ToString() + "-" + mazeScale.ToString(); break; }
            }
            if (nQuadrants != 0)
            {
                filePath = filePathBase + "-" + creatorName + "-" + "quadrant" + (iQuadrant + 1) + "of" + nQuadrants + ".json";
            }
            else
            {
                filePath = filePathBase + "-" + creatorName + ".json";
            }
            SeedDataController.SaveSeedData(seedData, filePath);
        }
        Debug.Log("Done!");
        Debug.Break();

#if UNITY_EDITOR
        UnityEditor.EditorApplication.isPlaying = false;
#endif
    }