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(); }