Example #1
0
    private void Propagate(int x, int y, int z)
    {
        //Reset the map that keeps track of the changes.
        ReInitMapOfChanges();

        //Queue the first element.
        var nodesToVisit = new Queue <Coord3D>();

        nodesToVisit.Enqueue(new Coord3D(x, y, z));

        //Perform a Breadth-First grid traversal.
        while (nodesToVisit.Any())
        {
            var current = nodesToVisit.Dequeue();
            mapOfChanges.SetValue(true, current.X, current.Y, current.Z);

            //Get the list of the allowed neighbours of the current node
            var nghbrsMaps = outputMatrix[current.X, current.Y, current.Z].Select(possibleElement => NeighboursMap[possibleElement]).ToList();

            var allowedNghbrs = nghbrsMaps.SelectMany(dict => dict)
                                .ToLookup(pair => pair.Key, pair => pair.Value)
                                .ToDictionary(group => group.Key, group => group.SelectMany(list => list).ToList());


            //For every possible direction check if the node has already been affected by the propagation.
            //If it hasn't queue it up and mark it as visited, otherwise move on.
            foreach (var direction in Directions)
            {
                var nodeToBeChanged = current.Add(direction.X, direction.Y, direction.Z);

                if (outputMatrix.OutOfBounds(nodeToBeChanged) && !Periodic)
                {
                    continue;
                }

                if (outputMatrix.OutOfBounds(nodeToBeChanged) && Periodic)
                {
                    nodeToBeChanged = new Coord3D(Mod(nodeToBeChanged.X, outputMatrix.GetLength(0)),
                                                  Mod(nodeToBeChanged.Y, outputMatrix.GetLength(1)),
                                                  Mod(nodeToBeChanged.Z, outputMatrix.GetLength(2)));

                    if (mapOfChanges.OutOfBounds(nodeToBeChanged))
                    {
                        continue;
                    }
                }

                //Count the states before the propagation.
                var statesBefore = outputMatrix[nodeToBeChanged.X, nodeToBeChanged.Y, nodeToBeChanged.Z].Count;

                //Eliminate neighbours that are not allowed from the output matrix
                var allowedNghbrsInDirection = allowedNghbrs[direction].Distinct().ToList();
                outputMatrix[nodeToBeChanged.X, nodeToBeChanged.Y, nodeToBeChanged.Z]
                .RemoveAll(neighbour => !allowedNghbrsInDirection.Contains(neighbour));

                //Count the states after, if nbBefore != nbAfter queue it up.
                var statesAfter = outputMatrix[nodeToBeChanged.X, nodeToBeChanged.Y, nodeToBeChanged.Z].Count;

                //Check for contradictions
                // TODO Add a backtrack recovery system to remedy the contradictions.
                if (outputMatrix[nodeToBeChanged.X, nodeToBeChanged.Y, nodeToBeChanged.Z].Count == 0)
                {
                    try {
                        ChosenPoints.Add(new Coord3D(x, y, z));
                        RollbackState();
                        TotalRollbacks++;
                        return;
                    }
                    catch (InvalidOperationException e) {
                        contradiction = true;
                        return;
                    }
                }

                //Queue it up in order to spread the info to its neighbours and mark it as visited.
                if (statesBefore != statesAfter)
                {
                    if (!nodesToVisit.Contains(nodeToBeChanged))
                    {
                        nodesToVisit.Enqueue(nodeToBeChanged);
                    }
                }
            }
        }

        ChosenPoints.Clear();
        if (TotalRollbacks > TOTAL_ROLLBACKS_ALLOWED)
        {
            contradiction = true;
            return;
        }

        generationFinished = CheckIfFinished();
    }