예제 #1
0
        public void TestMaskWithOverlapping()
        {
            var a = new int[, ] {
                { 1, 0 },
                { 0, 1 },
            };
            var model = OverlappingModel.Create(a, 2, false, 8);

            var mask = new bool[4 * 5];

            for (var x = 0; x < 5; x++)
            {
                for (var y = 0; y < 4; y++)
                {
                    if (x == 2 || x == 3)
                    {
                        mask[x + y * 5] = false;
                    }
                    else
                    {
                        mask[x + y * 5] = true;
                    }
                }
            }
            var topology = new GridTopology(5, 4, false).WithMask(mask);

            var propagator = new TilePropagator(model, topology);

            propagator.Select(0, 0, 0, new Tile(1));
            propagator.Select(4, 0, 0, new Tile(0));

            propagator.Run();

            Assert.AreEqual(Resolution.Decided, propagator.Status);
        }
예제 #2
0
        public void Init(TilePropagator propagator)
        {
            var tileSet = propagator.CreateTileSet(Tiles);

            var point = Point ?? GetRandomPoint(propagator, tileSet);

            propagator.Select(point.X, point.Y, point.Z, tileSet);
        }
예제 #3
0
        public Resolution Init(TilePropagator propagator)
        {
            var point = Point ?? GetRandomPoint(propagator);

            propagator.Select(point.X, point.Y, point.Z, Tile);

            return(Resolution.Undecided);
        }
예제 #4
0
        public void Check(TilePropagator propagator)
        {
            var topology = propagator.Topology;
            var indices  = topology.Width * topology.Height * topology.Depth;
            // Initialize couldBePath and mustBePath based on wave possibilities
            var couldBePath = new bool[indices];
            var mustBePath  = new bool[indices];

            for (int i = 0; i < indices; i++)
            {
                topology.GetCoord(i, out var x, out var y, out var z);
                propagator.GetBannedSelected(x, y, z, tileSet, out var isBanned, out var isSelected);
                couldBePath[i] = !isBanned;
                mustBePath[i]  = isSelected;
            }

            // Select relevant cells, i.e. those that must be connected.
            bool[] relevant;
            if (EndPoints == null)
            {
                relevant = mustBePath;
            }
            else
            {
                relevant = new bool[indices];
                if (EndPoints.Length == 0)
                {
                    return;
                }
                foreach (var endPoint in EndPoints)
                {
                    var index = topology.GetIndex(endPoint.X, endPoint.Y, endPoint.Z);
                    relevant[index] = true;
                }
            }
            var walkable = couldBePath;

            var isArticulation = PathConstraintUtils.GetArticulationPoints(graph, walkable, relevant);

            if (isArticulation == null)
            {
                propagator.SetContradiction();
                return;
            }


            // All articulation points must be paths,
            // So ban any other possibilities
            for (var i = 0; i < indices; i++)
            {
                if (isArticulation[i])
                {
                    topology.GetCoord(i, out var x, out var y, out var z);
                    propagator.Select(x, y, z, tileSet);
                }
            }
        }
예제 #5
0
        public void Check(TilePropagator propagator)
        {
            var topology = propagator.Topology;
            var width    = topology.Width;
            var height   = topology.Height;
            var depth    = topology.Depth;

            var yesCount   = countTracker.YesCount;
            var noCount    = countTracker.NoCount;
            var maybeCount = countTracker.MaybeCount;

            if (Comparison == CountComparison.AtMost || Comparison == CountComparison.Exactly)
            {
                if (yesCount > Count)
                {
                    // Already got too many, just fail
                    propagator.SetContradiction();
                    return;
                }
                if (yesCount == Count && maybeCount > 0)
                {
                    // We've reached the limit, ban any more
                    foreach (var index in topology.GetIndices())
                    {
                        var selected = selectedChangeTracker.GetQuadstate(index);
                        if (selected.IsMaybe())
                        {
                            propagator.Topology.GetCoord(index, out var x, out var y, out var z);
                            propagator.Ban(x, y, z, tileSet);
                        }
                    }
                }
            }
            if (Comparison == CountComparison.AtLeast || Comparison == CountComparison.Exactly)
            {
                if (yesCount + maybeCount < Count)
                {
                    // Already got too few, just fail
                    propagator.SetContradiction();
                    return;
                }
                if (yesCount + maybeCount == Count && maybeCount > 0)
                {
                    // We've reached the limit, select all the rest
                    foreach (var index in topology.GetIndices())
                    {
                        var selected = selectedChangeTracker.GetQuadstate(index);
                        if (selected.IsMaybe())
                        {
                            propagator.Topology.GetCoord(index, out var x, out var y, out var z);
                            propagator.Select(x, y, z, tileSet);
                        }
                    }
                }
            }
        }
예제 #6
0
 public void SelectPath(int node)
 {
     var(index, dir) = Unpack(node, GetNodesPerIndex());
     topology.GetCoord(index, out var x, out var y, out var z);
     if (dir == null)
     {
         propagator.Select(x, y, z, pathTileSet);
     }
     else
     {
         if (MustBePath[node])
         {
             return;
         }
         if (tileSetByExit.TryGetValue((Direction)dir, out var exitTiles))
         {
             propagator.Select(x, y, z, exitTiles);
         }
     }
 }
예제 #7
0
        public void TestPriority()
        {
            var t1    = new Tile(1);
            var t2    = new Tile(2);
            var t3    = new Tile(3);
            var model = new AdjacentModel(DirectionSet.Cartesian2d);

            model.AddAdjacency(t1, t1, Direction.XPlus);
            model.AddAdjacency(t1, t2, Direction.XPlus);
            model.AddAdjacency(t2, t2, Direction.XPlus);
            model.AddAdjacency(t2, t3, Direction.XPlus);
            model.AddAdjacency(t3, t3, Direction.XPlus);

            model.SetUniformFrequency();

            var topology = new GridTopology(6, 1, false).WithMask(new bool[] { true, true, true, true, true, false });

            IDictionary <Tile, PriorityAndWeight> weights = new Dictionary <Tile, PriorityAndWeight>
            {
                { t1, new PriorityAndWeight {
                      Priority = 0, Weight = 1
                  } },
                { t2, new PriorityAndWeight {
                      Priority = 1, Weight = 1
                  } },
                { t3, new PriorityAndWeight {
                      Priority = 2, Weight = 1
                  } },
            };

            var weightsArray = TopoArray.CreateByIndex(_ => weights, topology);

            var propagator = new TilePropagator(model, topology, new TilePropagatorOptions
            {
                IndexPickerType  = IndexPickerType.ArrayPriorityMinEntropy,
                WeightSetByIndex = TopoArray.CreateByIndex(_ => 0, topology),
                WeightSets       = new Dictionary <int, IDictionary <Tile, PriorityAndWeight> > {
                    { 0, weights }
                },
            });

            propagator.Select(0, 0, 0, t1);

            propagator.Run();

            Assert.AreEqual(Resolution.Decided, propagator.Status);

            var r = propagator.ToValueArray <int>();

            Assert.AreEqual(1, r.Get(0, 0));
            Assert.AreEqual(2, r.Get(1, 0));
            Assert.AreEqual(3, r.Get(2, 0));
            Assert.AreEqual(3, r.Get(3, 0));
        }
예제 #8
0
        public Resolution Init(TilePropagator propagator)
        {
            var width  = propagator.Topology.Width;
            var height = propagator.Topology.Height;
            var depth  = propagator.Topology.Depth;

            for (var x = 0; x < width; x++)
            {
                var xmin = x == 0;
                var xmax = x == width - 1;

                for (var y = 0; y < height; y++)
                {
                    var ymin = y == 0;
                    var ymax = y == height - 1;

                    for (var z = 0; z < depth; z++)
                    {
                        var zmin = z == 0;
                        var zmax = z == depth - 1;

                        var match = (Match(Sides, xmin, xmax, ymin, ymax, zmin, zmax) &&
                                     !Match(ExcludeSides, xmin, xmax, ymin, ymax, zmin, zmax)) != InvertArea;

                        if (match)
                        {
                            if (Ban)
                            {
                                var cellStatus = propagator.Ban(x, y, z, Tile);
                                if (cellStatus != Resolution.Undecided)
                                {
                                    return(cellStatus);
                                }
                            }
                            else
                            {
                                var cellStatus = propagator.Select(x, y, z, Tile);
                                if (cellStatus != Resolution.Undecided)
                                {
                                    return(cellStatus);
                                }
                            }
                        }
                    }
                }
            }
            return(Resolution.Undecided);
        }
예제 #9
0
        public void TestParityConstraint()
        {
            var w        = 10;
            var h        = 10;
            var topology = new GridTopology(10, 10, false);

            var pathModel = new PathModel(forks: false);

            var constraint = new ParityConstraint
            {
                PathSpec = new EdgedPathSpec {
                    Exits = pathModel.Exits
                },
            };

            var options = new TilePropagatorOptions
            {
                BackTrackDepth = -1,
                Constraints    = new[] { constraint },
            };

            var propagator = new TilePropagator(pathModel.Model, topology, options);

            for (var x = 0; x < w; x++)
            {
                for (var y = 0; y < h; y++)
                {
                    void Select(Tile t) => propagator.Select(x, y, 0, t);

                    if (x == 0 && y == 1)
                    {
                        Select(pathModel.Straight2);
                        continue;
                    }
                    if (x == 0 || y == 0 || x == w - 1 || y == h - 1)
                    {
                        Select(pathModel.Empty);
                    }
                }
            }

            propagator.Step();

            Assert.AreEqual(Resolution.Contradiction, propagator.Status);
        }
예제 #10
0
        public void Init(TilePropagator propagator)
        {
            var tiles = propagator.CreateTileSet(Tiles);

            var width  = propagator.Topology.Width;
            var height = propagator.Topology.Height;
            var depth  = propagator.Topology.Depth;

            for (var x = 0; x < width; x++)
            {
                var xmin = x == 0;
                var xmax = x == width - 1;

                for (var y = 0; y < height; y++)
                {
                    var ymin = y == 0;
                    var ymax = y == height - 1;

                    for (var z = 0; z < depth; z++)
                    {
                        var zmin = z == 0;
                        var zmax = z == depth - 1;

                        var match = (Match(Sides, xmin, xmax, ymin, ymax, zmin, zmax) &&
                                     !Match(ExcludeSides, xmin, xmax, ymin, ymax, zmin, zmax)) != InvertArea;

                        if (match)
                        {
                            if (Ban)
                            {
                                propagator.Ban(x, y, z, tiles);
                            }
                            else
                            {
                                propagator.Select(x, y, z, tiles);
                            }
                        }
                    }
                }
            }
        }
예제 #11
0
        public void TestDirtyIndexPicker()
        {
            var t1    = new Tile(1);
            var t2    = new Tile(2);
            var t3    = new Tile(3);
            var model = new AdjacentModel(DirectionSet.Cartesian2d);

            model.AddAdjacency(t1, t1, Direction.XPlus);
            model.AddAdjacency(t1, t2, Direction.XPlus);
            model.AddAdjacency(t2, t2, Direction.XPlus);
            model.AddAdjacency(t2, t3, Direction.XPlus);
            model.AddAdjacency(t3, t3, Direction.XPlus);
            model.AddAdjacency(t3, t2, Direction.XPlus);
            model.AddAdjacency(t2, t1, Direction.XPlus);

            model.SetUniformFrequency();

            var topology = new GridTopology(6, 1, false);

            var options = new TilePropagatorOptions
            {
                IndexPickerType = IndexPickerType.Dirty,
                TilePickerType  = TilePickerType.Ordered,
                CleanTiles      = TopoArray.FromConstant(t1, topology),
            };

            var propagator = new TilePropagator(model, topology, options);

            propagator.Select(3, 0, 0, t3);

            propagator.Run();

            var a = propagator.ToValueArray <int?>();

            Assert.AreEqual(null, a.Get(0, 0));
            Assert.AreEqual(null, a.Get(1, 0));
            Assert.AreEqual(2, a.Get(2, 0));
            Assert.AreEqual(3, a.Get(3, 0));
            Assert.AreEqual(2, a.Get(4, 0));
            Assert.AreEqual(null, a.Get(5, 0));
        }
예제 #12
0
        public void TestToTopArray()
        {
            var a = new int[, ] {
                { 1, 0 },
                { 0, 1 },
            };
            var model = OverlappingModel.Create(a, 2, false, 8);

            var propagator = new TilePropagator(model, new GridTopology(4, 4, false));

            propagator.Select(0, 0, 0, new Tile(1));
            var status = propagator.Run();

            Assert.AreEqual(Resolution.Decided, status);

            var result = propagator.ToValueArray <int>().ToArray2d();

            Assert.AreEqual(4, result.GetLength(0));
            Assert.AreEqual(4, result.GetLength(1));

            Assert.AreEqual(1, result[0, 0]);
            Assert.AreEqual(1, result[3, 3]);
        }
예제 #13
0
        public void TestUnassignableEager()
        {
            var model = new AdjacentModel(DirectionSet.Cartesian2d);
            var tile1 = new Tile(1);
            var tile2 = new Tile(2);
            var tiles = new[] { tile1, tile2 };

            model.AddAdjacency(tiles, tiles, Direction.XPlus);
            model.AddAdjacency(tiles, tiles, Direction.YPlus);
            model.SetUniformFrequency();

            var topology = new GridTopology(3, 1, false);

            var count = 3;

            var options = new TilePropagatorOptions
            {
                Constraints = new[]
                {
                    new CountConstraint
                    {
                        Tiles = new[] { tile1, }.ToHashSet(),
                Count      = count,
                Comparison = CountComparison.Exactly,
                Eager      = true,
                    }
                }
            };
            var propagator = new TilePropagator(model, topology, options);

            propagator.Select(1, 0, 0, tile2);

            propagator.Run();

            Assert.AreEqual(Resolution.Contradiction, propagator.Status);
        }
예제 #14
0
 public void SelectPath(int index)
 {
     propagator.Topology.GetCoord(index, out var x, out var y, out var z);
     propagator.Select(x, y, z, tileSet);
 }
예제 #15
0
        public void Check(TilePropagator propagator)
        {
            var topology = propagator.Topology;
            var indices  = topology.Width * topology.Height * topology.Depth;

            // TODO: This shouldn't be too hard to implement
            if (topology.Directions.Type != Topo.DirectionSetType.Cartesian2d)
            {
                throw new Exception("EdgedPathConstraint only supported for Cartesiant2d");
            }

            var nodesPerIndex = topology.Directions.Count + 1;

            // Initialize couldBePath and mustBePath based on wave possibilities
            var couldBePath = new bool[indices * nodesPerIndex];
            var mustBePath  = new bool[indices * nodesPerIndex];

            for (int i = 0; i < indices; i++)
            {
                topology.GetCoord(i, out var x, out var y, out var z);

                couldBePath[i * nodesPerIndex] = false;
                foreach (var kv in actualExits)
                {
                    var tile  = kv.Key;
                    var exits = kv.Value;

                    propagator.GetBannedSelected(x, y, z, tile, out var isBanned, out var isSelected);

                    if (!isBanned)
                    {
                        couldBePath[i * nodesPerIndex] = true;
                        foreach (var exit in exits)
                        {
                            couldBePath[i * nodesPerIndex + 1 + (int)exit] = true;
                        }
                    }
                }
                // TODO: There's probably a more efficient way to do this
                propagator.GetBannedSelected(x, y, z, pathTileSet, out var allIsBanned, out var allIsSelected);
                mustBePath[i * nodesPerIndex] = allIsSelected;
            }

            // Select relevant cells, i.e. those that must be connected.
            bool[] relevant;
            if (EndPoints == null && EndPointTiles == null)
            {
                relevant = mustBePath;
            }
            else
            {
                relevant = new bool[indices * nodesPerIndex];

                var relevantCount = 0;
                if (EndPoints != null)
                {
                    foreach (var endPoint in EndPoints)
                    {
                        var index = topology.GetIndex(endPoint.X, endPoint.Y, endPoint.Z);
                        relevant[index * nodesPerIndex] = true;
                        relevantCount++;
                    }
                }
                if (EndPointTiles != null)
                {
                    for (int i = 0; i < indices; i++)
                    {
                        topology.GetCoord(i, out var x, out var y, out var z);
                        propagator.GetBannedSelected(x, y, z, endPointTileSet, out var isBanned, out var isSelected);
                        if (isSelected)
                        {
                            relevant[i * nodesPerIndex] = true;
                            relevantCount++;
                        }
                    }
                }
                if (relevantCount == 0)
                {
                    // Nothing to do.
                    return;
                }
            }
            var walkable = couldBePath;

            var component = EndPointTiles != null ? new bool[indices] : null;

            var isArticulation = PathConstraintUtils.GetArticulationPoints(graph, walkable, relevant, component);

            if (isArticulation == null)
            {
                propagator.SetContradiction();
                return;
            }


            // All articulation points must be paths,
            // So ban any other possibilities
            for (var i = 0; i < indices; i++)
            {
                topology.GetCoord(i, out var x, out var y, out var z);
                if (isArticulation[i * nodesPerIndex])
                {
                    propagator.Select(x, y, z, pathTileSet);
                }
                for (var d = 0; d < topology.Directions.Count; d++)
                {
                    if (isArticulation[i * nodesPerIndex + 1 + d])
                    {
                        propagator.Select(x, y, z, tilesByExit[(Direction)d]);
                    }
                }
            }

            // Any EndPointTiles not in the connected component aren't safe to add
            if (EndPointTiles != null)
            {
                for (int i = 0; i < indices; i++)
                {
                    if (!component[i * nodesPerIndex])
                    {
                        topology.GetCoord(i, out var x, out var y, out var z);
                        propagator.Ban(x, y, z, endPointTileSet);
                    }
                }
            }
        }
예제 #16
0
        public void Init(TilePropagator propagator)
        {
            tileSet = propagator.CreateTileSet(Tiles);

            countTracker = new CountTracker(propagator.Topology);

            selectedChangeTracker = propagator.CreateSelectedChangeTracker(tileSet, countTracker);

            if (Eager)
            {
                // Naive implementation

                /*
                 * // Pick Count random indices
                 * var topology = propagator.Topology;
                 * var pickedIndices = new List<int>();
                 * var remainingIndices = new List<int>(topology.Indicies);
                 * for (var c = 0; c < Count; c++)
                 * {
                 *  var pickedIndexIndex = (int)(propagator.RandomDouble() * remainingIndices.Count);
                 *  pickedIndices.Add(remainingIndices[pickedIndexIndex]);
                 *  remainingIndices[pickedIndexIndex] = remainingIndices[remainingIndices.Count - 1];
                 *  remainingIndices.RemoveAt(remainingIndices.Count - 1);
                 * }
                 * // Ban or select tiles to ensure an appropriate count
                 * if(Comparison == CountComparison.AtMost || Comparison == CountComparison.Exactly)
                 * {
                 *  foreach (var i in remainingIndices)
                 *  {
                 *      topology.GetCoord(i, out var x, out var y, out var z);
                 *      propagator.Ban(x, y, z, tileSet);
                 *  }
                 * }
                 * if (Comparison == CountComparison.AtLeast || Comparison == CountComparison.Exactly)
                 * {
                 *  foreach (var i in pickedIndices)
                 *  {
                 *      topology.GetCoord(i, out var x, out var y, out var z);
                 *      propagator.Select(x, y, z, tileSet);
                 *  }
                 * }
                 */

                var topology         = propagator.Topology;
                var width            = topology.Width;
                var height           = topology.Height;
                var depth            = topology.Depth;
                var pickedIndices    = new List <int>();
                var remainingIndices = new List <int>(topology.GetIndices());

                while (true)
                {
                    var noCount   = 0;
                    var yesCount  = 0;
                    var maybeList = new List <int>();
                    for (var z = 0; z < depth; z++)
                    {
                        for (var y = 0; y < height; y++)
                        {
                            for (var x = 0; x < width; x++)
                            {
                                var index = topology.GetIndex(x, y, z);
                                if (topology.ContainsIndex(index))
                                {
                                    var selected = selectedChangeTracker.GetQuadstate(index);
                                    if (selected.IsNo())
                                    {
                                        noCount++;
                                    }
                                    if (selected.IsMaybe())
                                    {
                                        maybeList.Add(index);
                                    }
                                    if (selected.IsYes())
                                    {
                                        yesCount++;
                                    }
                                }
                            }
                        }
                    }
                    var maybeCount = maybeList.Count;

                    if (Comparison == CountComparison.AtMost)
                    {
                        if (yesCount > Count)
                        {
                            // Already got too many, just fail
                            propagator.SetContradiction();
                            return;
                        }
                        if (yesCount == Count)
                        {
                            // We've reached the limit, ban any more and exit
                            Check(propagator);
                            return;
                        }
                        var pickedIndex = maybeList[(int)(propagator.RandomDouble() * maybeList.Count)];
                        topology.GetCoord(pickedIndex, out var x, out var y, out var z);
                        propagator.Select(x, y, z, tileSet);
                    }
                    else if (Comparison == CountComparison.AtLeast || Comparison == CountComparison.Exactly)
                    {
                        if (yesCount + maybeCount < Count)
                        {
                            // Already got too few, just fail
                            propagator.SetContradiction();
                            return;
                        }
                        if (yesCount + maybeCount == Count)
                        {
                            // We've reached the limit, ban any more and exit
                            Check(propagator);
                            return;
                        }
                        var pickedIndex = maybeList[(int)(propagator.RandomDouble() * maybeList.Count)];
                        topology.GetCoord(pickedIndex, out var x, out var y, out var z);
                        propagator.Ban(x, y, z, tileSet);
                    }
                }
            }
        }
예제 #17
0
        public void VisitIndex(int index, bool[] visited)
        {
            if (visited[index])
            {
                return;
            }

            var visitedList = new List <int>();
            var parityCount = 0;

            (int, Direction?)? amigLocation = null;
            var hasMultipleAmbiguous = false;


            var stack = new Stack <int>();

            stack.Push(index);
            while (stack.Count > 0)
            {
                var i = stack.Pop();

                if (visited[i])
                {
                    continue;
                }

                visited[i] = true;
                visitedList.Add(i);

                var isOdd = oddPathTracker?.GetQuadstate(i) ?? Quadstate.No;

                // Does this tile have undefined parity in number of exits
                if (isOdd.IsMaybe())
                {
                    // Ambiguous
                    if (amigLocation != null)
                    {
                        hasMultipleAmbiguous = true;
                    }
                    else
                    {
                        amigLocation = (i, null);
                    }
                }
                if (isOdd.IsYes())
                {
                    parityCount++;
                }

                for (var d = 0; d < topology.DirectionsCount; d++)
                {
                    var direction = (Direction)d;
                    var qs        = pathView.TrackerByExit.TryGetValue(direction, out var tracker) ? tracker.GetQuadstate(i) : Quadstate.No;
                    if (qs.IsYes())
                    {
                        parityCount++;
                    }
                    if (qs.IsMaybe())
                    {
                        if (topology.TryMove(i, direction, out var i2))
                        {
                            // Need to include this cell in the region
                            if (!visited[i2])
                            {
                                stack.Push(i2);
                            }
                        }
                        else
                        {
                            // If there's no corresponding tile to balance this one
                            // then this exit is free to be path or not, altering parity

                            if (amigLocation != null)
                            {
                                hasMultipleAmbiguous = true;
                            }
                            else
                            {
                                amigLocation = (i, direction);
                            }
                        }
                    }
                }
            }

            // We've now fully explored this region

            if (hasMultipleAmbiguous)
            {
                // There's nothing we can say about this case.
                return;
            }

            if (amigLocation != null)
            {
                // There's exactly one ambiguous point, so set it to ensure even parity
                var(ambigIndex, ambigDirection) = amigLocation.Value;
                topology.GetCoord(ambigIndex, out var x, out var y, out var z);
                if (ambigDirection == null)
                {
                    if (parityCount % 2 == 0)
                    {
                        propagator.Ban(x, y, z, oddPathTilesSet);
                    }
                    else
                    {
                        propagator.Select(x, y, z, oddPathTilesSet);
                    }
                }
                else
                {
                    if (parityCount % 2 == 0)
                    {
                        propagator.Ban(x, y, z, pathView.TileSetByExit[ambigDirection.Value]);
                    }
                    else
                    {
                        propagator.Select(x, y, z, pathView.TileSetByExit[ambigDirection.Value]);
                    }
                }
            }
            else
            {
                if (parityCount % 2 == 0)
                {
                    // This is fine
                }
                else
                {
                    // This is not fine, and there's nothing ambiguous to patch things up
                    propagator.SetContradiction();
                }
            }
        }
예제 #18
0
        public virtual void Init(TilePropagator propagator)
        {
            changeTracker = propagator.CreateChangeTracker();

            var p        = propagator;
            var topology = propagator.Topology;

            // Ban any tiles which don't have a symmetry
            foreach (var i in topology.GetIndices())
            {
                if (TryMapIndex(p, i, out var i2))
                {
                    topology.GetCoord(i, out var x, out var y, out var z);

                    propagator.Select(x, y, z, propagator.TileModel.Tiles
                                      .Where(tile => TryMapTile(tile, out var _)));
                }
            }

            // Ban tiles that interact badly with their own symmetry
            foreach (var i in topology.GetIndices())
            {
                if (TryMapIndex(p, i, out var i2))
                {
                    topology.GetCoord(i, out var x, out var y, out var z);

                    if (i2 == i)
                    {
                        // index maps to itself, so only allow tiles that map to themselves
                        var allowedTiles = propagator.TileModel.Tiles
                                           .Where(tile => TryMapTile(tile, out var tile2) && tile == tile2);
                        propagator.Select(x, y, z, allowedTiles);
                        continue;
                    }


                    // TODO: Support overlapped model?
                    if (propagator.TileModel is AdjacentModel adjacentModel)
                    {
                        for (var d = 0; d < topology.DirectionsCount; d++)
                        {
                            if (topology.TryMove(i, (Direction)d, out var dest) && dest == i2)
                            {
                                // index maps adjacent to itself, so only allow tiles that can be placed adjacent to themselves
                                var allowedTiles = propagator.TileModel.Tiles
                                                   .Where(tile => TryMapTile(tile, out var tile2) && adjacentModel.IsAdjacent(tile, tile2, (Direction)d))
                                                   .ToList();
                                propagator.Select(x, y, z, allowedTiles);
                                continue;
                            }
                        }
                    }
                    if (propagator.TileModel is GraphAdjacentModel graphAdjacentModel)
                    {
                        var sentinel = new Tile(new object());
                        for (var d = 0; d < topology.DirectionsCount; d++)
                        {
                            if (topology.TryMove(i, (Direction)d, out var dest, out var _, out var edgeLabel) && dest == i2)
                            {
                                // index maps adjacent to itself, so only allow tiles that can be placed adjacent to themselves
                                var allowedTiles = propagator.TileModel.Tiles
                                                   .Where(tile => TryMapTile(tile, out var tile2) && graphAdjacentModel.IsAdjacent(tile, tile2, edgeLabel));
                                propagator.Select(x, y, z, allowedTiles);
                                continue;
                            }
                        }
                    }

                    topology.GetCoord(i2, out var x2, out var y2, out var z2);
                }
            }
        }
예제 #19
0
        private void Check(TilePropagator propagator, bool init)
        {
            var topology = propagator.Topology;
            var indices  = topology.IndexCount;
            // Initialize couldBePath and mustBePath based on wave possibilities
            var couldBePath = new bool[indices];
            var mustBePath  = new bool[indices];

            for (int i = 0; i < indices; i++)
            {
                var ts = selectedTracker.GetQuadstate(i);
                couldBePath[i] = ts.Possible();
                mustBePath[i]  = ts.IsYes();
            }

            // Select relevant cells, i.e. those that must be connected.
            var hasEndPoints = EndPoints != null || EndPointTiles != null;

            bool[] relevant;
            if (!hasEndPoints)
            {
                relevant = mustBePath;
            }
            else
            {
                relevant = new bool[indices];
                var relevantCount = 0;
                if (EndPoints != null)
                {
                    foreach (var endPoint in EndPoints)
                    {
                        var index = topology.GetIndex(endPoint.X, endPoint.Y, endPoint.Z);
                        relevant[index] = true;
                        relevantCount++;
                    }
                }
                if (EndPointTiles != null)
                {
                    for (int i = 0; i < indices; i++)
                    {
                        if (endPointSelectedTracker.IsSelected(i))
                        {
                            relevant[i] = true;
                            relevantCount++;
                        }
                    }
                }
                if (relevantCount == 0)
                {
                    // Nothing to do.
                    return;
                }
            }

            if (init)
            {
                for (int i = 0; i < indices; i++)
                {
                    if (relevant[i])
                    {
                        topology.GetCoord(i, out var x, out var y, out var z);
                        propagator.Select(x, y, z, tileSet);
                    }
                }
            }

            var walkable = couldBePath;

            var info           = PathConstraintUtils.GetArticulationPoints(graph, walkable, relevant);
            var isArticulation = info.IsArticulation;

            if (info.ComponentCount > 1)
            {
                propagator.SetContradiction("Path constraint found multiple components", this);
                return;
            }

            // All articulation points must be paths,
            // So ban any other possibilities
            for (var i = 0; i < indices; i++)
            {
                if (isArticulation[i] && !mustBePath[i])
                {
                    topology.GetCoord(i, out var x, out var y, out var z);
                    propagator.Select(x, y, z, tileSet);
                }
            }

            // Any path tiles / EndPointTiles not in the connected component aren't safe to add.
            if (info.ComponentCount > 0)
            {
                var component             = info.Component;
                var actualEndPointTileSet = hasEndPoints ? endPointTileSet : tileSet;
                if (actualEndPointTileSet != null)
                {
                    for (int i = 0; i < indices; i++)
                    {
                        if (component[i] == null)
                        {
                            topology.GetCoord(i, out var x, out var y, out var z);
                            propagator.Ban(x, y, z, actualEndPointTileSet);
                        }
                    }
                }
            }
        }
예제 #20
0
        private void Check(TilePropagator propagator, bool init)
        {
            var topology = propagator.Topology;
            var indices  = topology.Width * topology.Height * topology.Depth;

            var nodesPerIndex = topology.DirectionsCount + 1;

            // Initialize couldBePath and mustBePath based on wave possibilities
            var couldBePath    = new bool[indices * nodesPerIndex];
            var mustBePath     = new bool[indices * nodesPerIndex];
            var exitMustBePath = new bool[indices * nodesPerIndex];

            foreach (var kv in trackerByExit)
            {
                var exit    = kv.Key;
                var tracker = kv.Value;
                for (int i = 0; i < indices; i++)
                {
                    var ts = tracker.GetQuadstate(i);
                    couldBePath[i * nodesPerIndex + 1 + (int)exit] = ts.Possible();
                    // Cannot put this in mustBePath these points can be disconnected, depending on topology mask
                    exitMustBePath[i * nodesPerIndex + 1 + (int)exit] = ts.IsYes();
                }
            }
            for (int i = 0; i < indices; i++)
            {
                var pathTs = pathSelectedTracker.GetQuadstate(i);
                couldBePath[i * nodesPerIndex] = pathTs.Possible();
                mustBePath[i * nodesPerIndex]  = pathTs.IsYes();
            }
            // Select relevant cells, i.e. those that must be connected.
            var hasEndPoints = EndPoints != null || EndPointTiles != null;

            bool[] relevant;
            if (!hasEndPoints)
            {
                // Basically equivalent to EndPoints = pathTileSet
                relevant = mustBePath;
            }
            else
            {
                relevant = new bool[indices * nodesPerIndex];

                var relevantCount = 0;
                if (EndPoints != null)
                {
                    foreach (var endPoint in EndPoints)
                    {
                        var index = topology.GetIndex(endPoint.X, endPoint.Y, endPoint.Z);
                        relevant[index * nodesPerIndex] = true;
                        relevantCount++;
                    }
                }
                if (EndPointTiles != null)
                {
                    for (int i = 0; i < indices; i++)
                    {
                        if (endPointSelectedTracker.IsSelected(i))
                        {
                            relevant[i * nodesPerIndex] = true;
                            relevantCount++;
                        }
                    }
                }
                if (relevantCount == 0)
                {
                    // Nothing to do.
                    return;
                }
            }

            if (init)
            {
                for (int i = 0; i < indices; i++)
                {
                    if (relevant[i * nodesPerIndex])
                    {
                        topology.GetCoord(i, out var x, out var y, out var z);
                        propagator.Select(x, y, z, pathTileSet);
                    }
                }
            }

            var walkable = couldBePath;

            var info           = PathConstraintUtils.GetArticulationPoints(graph, walkable, relevant);
            var isArticulation = info.IsArticulation;

            if (info.ComponentCount > 1)
            {
                propagator.SetContradiction("Edged path constraint found multiple connected components.", this);
                return;
            }


            // All articulation points must be paths,
            // So ban any other possibilities
            for (var i = 0; i < indices; i++)
            {
                topology.GetCoord(i, out var x, out var y, out var z);
                if (isArticulation[i * nodesPerIndex] && !mustBePath[i * nodesPerIndex])
                {
                    propagator.Select(x, y, z, pathTileSet);
                }
                for (var d = 0; d < topology.DirectionsCount; d++)
                {
                    if (isArticulation[i * nodesPerIndex + 1 + d] && !exitMustBePath[i * nodesPerIndex + 1 + d])
                    {
                        if (tilesByExit.TryGetValue((Direction)d, out var exitTiles))
                        {
                            propagator.Select(x, y, z, exitTiles);
                        }
                    }
                }
            }

            // Any path tiles / EndPointTiles not in the connected component aren't safe to add.
            if (info.ComponentCount > 0)
            {
                var component             = info.Component;
                var actualEndPointTileSet = hasEndPoints ? endPointTileSet : pathTileSet;
                if (actualEndPointTileSet != null)
                {
                    for (int i = 0; i < indices; i++)
                    {
                        if (component[i * nodesPerIndex] == null)
                        {
                            topology.GetCoord(i, out var x, out var y, out var z);
                            propagator.Ban(x, y, z, actualEndPointTileSet);
                        }
                    }
                }
            }
        }
예제 #21
0
        public void Check(TilePropagator propagator)
        {
            var topology = propagator.Topology;
            var indices  = topology.Width * topology.Height * topology.Depth;
            // Initialize couldBePath and mustBePath based on wave possibilities
            var couldBePath = new bool[indices];
            var mustBePath  = new bool[indices];

            for (int i = 0; i < indices; i++)
            {
                var ts = selectedTracker.GetTristate(i);
                couldBePath[i] = ts.Possible();
                mustBePath[i]  = ts.IsYes();
            }

            // Select relevant cells, i.e. those that must be connected.
            bool[] relevant;
            if (EndPoints == null && EndPointTiles == null)
            {
                relevant = mustBePath;
            }
            else
            {
                relevant = new bool[indices];
                var relevantCount = 0;
                if (EndPoints != null)
                {
                    foreach (var endPoint in EndPoints)
                    {
                        var index = topology.GetIndex(endPoint.X, endPoint.Y, endPoint.Z);
                        relevant[index] = true;
                        relevantCount++;
                    }
                }
                if (EndPointTiles != null)
                {
                    for (int i = 0; i < indices; i++)
                    {
                        if (endPointSelectedTracker.IsSelected(i))
                        {
                            relevant[i] = true;
                            relevantCount++;
                        }
                    }
                }
                if (relevantCount == 0)
                {
                    // Nothing to do.
                    return;
                }
            }
            var walkable = couldBePath;

            var component = EndPointTiles != null ? new bool[indices] : null;

            var isArticulation = PathConstraintUtils.GetArticulationPoints(graph, walkable, relevant, component);

            if (isArticulation == null)
            {
                propagator.SetContradiction();
                return;
            }

            // All articulation points must be paths,
            // So ban any other possibilities
            for (var i = 0; i < indices; i++)
            {
                if (isArticulation[i] && !mustBePath[i])
                {
                    topology.GetCoord(i, out var x, out var y, out var z);
                    propagator.Select(x, y, z, tileSet);
                }
            }

            // Any EndPointTiles not in the connected component aren't safe to add
            if (EndPointTiles != null)
            {
                for (int i = 0; i < indices; i++)
                {
                    if (!component[i])
                    {
                        topology.GetCoord(i, out var x, out var y, out var z);
                        propagator.Ban(x, y, z, endPointTileSet);
                    }
                }
            }
        }
예제 #22
0
        public void Check(TilePropagator propagator)
        {
            var topology = propagator.Topology;
            var indices  = topology.Width * topology.Height * topology.Depth;

            var nodesPerIndex = topology.DirectionsCount + 1;

            // Initialize couldBePath and mustBePath based on wave possibilities
            var couldBePath    = new bool[indices * nodesPerIndex];
            var mustBePath     = new bool[indices * nodesPerIndex];
            var exitMustBePath = new bool[indices * nodesPerIndex];

            foreach (var kv in trackerByExit)
            {
                var exit    = kv.Key;
                var tracker = kv.Value;
                for (int i = 0; i < indices; i++)
                {
                    var ts = tracker.GetTristate(i);
                    couldBePath[i * nodesPerIndex + 1 + (int)exit] = ts.Possible();
                    // Cannot put this in mustBePath these points can be disconnected, depending on topology mask
                    exitMustBePath[i * nodesPerIndex + 1 + (int)exit] = ts.IsYes();
                }
            }
            for (int i = 0; i < indices; i++)
            {
                var pathTs = pathSelectedTracker.GetTristate(i);
                couldBePath[i * nodesPerIndex] = pathTs.Possible();
                mustBePath[i * nodesPerIndex]  = pathTs.IsYes();
            }
            // Select relevant cells, i.e. those that must be connected.
            bool[] relevant;
            if (EndPoints == null && EndPointTiles == null)
            {
                relevant = mustBePath;
            }
            else
            {
                relevant = new bool[indices * nodesPerIndex];

                var relevantCount = 0;
                if (EndPoints != null)
                {
                    foreach (var endPoint in EndPoints)
                    {
                        var index = topology.GetIndex(endPoint.X, endPoint.Y, endPoint.Z);
                        relevant[index * nodesPerIndex] = true;
                        relevantCount++;
                    }
                }
                if (EndPointTiles != null)
                {
                    for (int i = 0; i < indices; i++)
                    {
                        if (endPointSelectedTracker.IsSelected(i))
                        {
                            relevant[i * nodesPerIndex] = true;
                            relevantCount++;
                        }
                    }
                }
                if (relevantCount == 0)
                {
                    // Nothing to do.
                    return;
                }
            }
            var walkable = couldBePath;

            var component = EndPointTiles != null ? new bool[indices] : null;

            var isArticulation = PathConstraintUtils.GetArticulationPoints(graph, walkable, relevant, component);

            if (isArticulation == null)
            {
                propagator.SetContradiction();
                return;
            }


            // All articulation points must be paths,
            // So ban any other possibilities
            for (var i = 0; i < indices; i++)
            {
                topology.GetCoord(i, out var x, out var y, out var z);
                if (isArticulation[i * nodesPerIndex] && !mustBePath[i * nodesPerIndex])
                {
                    propagator.Select(x, y, z, pathTileSet);
                }
                for (var d = 0; d < topology.DirectionsCount; d++)
                {
                    if (isArticulation[i * nodesPerIndex + 1 + d] && !exitMustBePath[i * nodesPerIndex + 1 + d])
                    {
                        propagator.Select(x, y, z, tilesByExit[(Direction)d]);
                    }
                }
            }

            // Any EndPointTiles not in the connected component aren't safe to add
            if (EndPointTiles != null)
            {
                for (int i = 0; i < indices; i++)
                {
                    if (!component[i * nodesPerIndex])
                    {
                        topology.GetCoord(i, out var x, out var y, out var z);
                        propagator.Ban(x, y, z, endPointTileSet);
                    }
                }
            }
        }
예제 #23
0
        public void Check(TilePropagator propagator)
        {
            var topology   = propagator.Topology;
            var width      = topology.Width;
            var height     = topology.Height;
            var depth      = topology.Depth;
            var noCount    = 0;
            var yesCount   = 0;
            var maybeCount = 0;

            for (var z = 0; z < depth; z++)
            {
                for (var y = 0; y < height; y++)
                {
                    for (var x = 0; x < width; x++)
                    {
                        var index = topology.GetIndex(x, y, z);
                        if (topology.ContainsIndex(index))
                        {
                            var selected = propagator.GetSelectedTristate(x, y, z, tileSet);
                            if (selected.IsNo)
                            {
                                noCount++;
                            }
                            if (selected.IsMaybe)
                            {
                                maybeCount++;
                            }
                            if (selected.IsYes)
                            {
                                yesCount++;
                            }
                        }
                    }
                }
            }

            if (Comparison == CountComparison.AtMost || Comparison == CountComparison.Exactly)
            {
                if (yesCount > Count)
                {
                    // Already got too many, just fail
                    propagator.SetContradiction();
                    return;
                }
                if (yesCount == Count && maybeCount > 0)
                {
                    // We've reached the limit, ban any more
                    for (var z = 0; z < depth; z++)
                    {
                        for (var y = 0; y < height; y++)
                        {
                            for (var x = 0; x < width; x++)
                            {
                                var index = topology.GetIndex(x, y, z);
                                if (topology.ContainsIndex(index))
                                {
                                    var selected = propagator.GetSelectedTristate(x, y, z, tileSet);
                                    if (selected.IsMaybe)
                                    {
                                        propagator.Ban(x, y, z, tileSet);
                                    }
                                }
                            }
                        }
                    }
                }
            }
            if (Comparison == CountComparison.AtLeast || Comparison == CountComparison.Exactly)
            {
                if (yesCount + maybeCount < Count)
                {
                    // Already got too few, just fail
                    propagator.SetContradiction();
                    return;
                }
                if (yesCount + maybeCount == Count && maybeCount > 0)
                {
                    // We've reached the limit, select all the rest
                    for (var z = 0; z < depth; z++)
                    {
                        for (var y = 0; y < height; y++)
                        {
                            for (var x = 0; x < width; x++)
                            {
                                var index = topology.GetIndex(x, y, z);
                                if (topology.ContainsIndex(index))
                                {
                                    var selected = propagator.GetSelectedTristate(x, y, z, tileSet);
                                    if (selected.IsMaybe)
                                    {
                                        propagator.Select(x, y, z, tileSet);
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
예제 #24
0
        public void Init(TilePropagator propagator)
        {
            // Strictly speaking, no initialization is needed.
            // In practise, this is useful to stop WFC from blundering
            // into easy avoided contradictions.


            var topology = propagator.Topology;

            var width  = topology.Width;
            var height = topology.Height;
            var depth  = topology.Depth;

            // Ban any tiles which don't have a reflection
            // Note we don't require the topology mask to be symmetric
            // So there may be some spots they are ok
            var reflectableTileSet = propagator.CreateTileSet(propagator.TileModel.Tiles
                                                              .Where(tile => TileRotation.Rotate(tile, reflectX, out var _)));

            foreach (var i in topology.Indicies)
            {
                topology.GetCoord(i, out var x, out var y, out var z);
                var x2 = topology.Width - 1 - x;
                var i2 = topology.GetIndex(x2, y, z);
                if (topology.ContainsIndex(i2))
                {
                    propagator.Select(x, y, z, reflectableTileSet);
                }
            }

            // Ensure we don't pick a central tile that interacts badly
            // with its own reflection
            if (propagator.TileModel is AdjacentModel adjacentModel)
            {
                TilePropagatorTileSet symetricTileSet;
                if (width % 2 == 1)
                {
                    // Enforce the center strip is symetric
                    symetricTileSet = propagator.CreateTileSet(adjacentModel.Tiles
                                                               .Where(tile => TileRotation.Rotate(tile, reflectX, out var otherTile) && tile == otherTile));

                    var x = width / 2;
                    for (var z = 0; z < depth; z++)
                    {
                        for (var y = 0; y < height; y++)
                        {
                            var i = topology.GetIndex(x, y, z);
                            if (topology.ContainsIndex(i))
                            {
                                propagator.Select(x, y, z, symetricTileSet);
                            }
                        }
                    }
                }
                else
                {
                    // Enforce column left of center connect to their mirrored selves
                    symetricTileSet = propagator.CreateTileSet(adjacentModel.Tiles
                                                               .Where(tile => TileRotation.Rotate(tile, reflectX, out var otherTile) && adjacentModel.IsAdjacent(tile, otherTile, Topo.Direction.XPlus)));

                    var x  = width / 2 - 1;
                    var x2 = width / 2;
                    for (var z = 0; z < depth; z++)
                    {
                        for (var y = 0; y < height; y++)
                        {
                            var i  = topology.GetIndex(x, y, z);
                            var i2 = topology.GetIndex(x2, y, z);
                            if (topology.ContainsIndex(i) && topology.ContainsIndex(i2))
                            {
                                propagator.Select(x, y, z, symetricTileSet);
                            }
                        }
                    }
                }
            }

            // TODO: Something similar for OverlappingModel
        }
예제 #25
0
        public void TestMirrorConstraint()
        {
            var trb = new TileRotationBuilder(4, true, TileRotationTreatment.Missing);

            var tile1 = new Tile(1);
            var tile2 = new Tile(2);
            var tile3 = new Tile(3);
            var tile4 = new Tile(4);
            var tile5 = new Tile(5);

            var tiles = new[] { tile1, tile2, tile3, tile4 };

            var reflectX = new Rotation(0, true);

            trb.Add(tile1, reflectX, tile2);
            trb.Add(tile3, reflectX, tile3);
            trb.Add(tile5, reflectX, tile5);

            var model = new AdjacentModel(DirectionSet.Cartesian2d);

            model.AddAdjacency(tiles, tiles, Direction.XPlus);
            model.AddAdjacency(new[] { tile5 }, tiles, Direction.XPlus);
            model.AddAdjacency(new[] { tile5 }, tiles, Direction.XMinus);

            model.SetUniformFrequency();
            model.SetFrequency(tile5, 0.0);

            var tr = trb.Build();

            var constraints = new[] { new MirrorXConstraint {
                                          TileRotation = tr
                                      } };

            // tile1 reflects to tile 2
            {
                var t2 = new GridTopology(2, 1, false);
                var p2 = new TilePropagator(model, t2, constraints: constraints);
                p2.Select(0, 0, 0, tile1);
                var status = p2.Run();
                Assert.AreEqual(Resolution.Decided, status);
                Assert.AreEqual(tile2, p2.ToArray().Get(1, 0));
            }

            // tile3 reflects to tile3
            {
                var t2 = new GridTopology(2, 1, false);
                var p2 = new TilePropagator(model, t2, constraints: constraints);
                p2.Select(0, 0, 0, tile3);
                var status = p2.Run();
                Assert.AreEqual(Resolution.Decided, status);
                Assert.AreEqual(tile3, p2.ToArray().Get(1, 0));
            }

            // tile3 only tile that can go in a central space
            // (tile5 can go, but has zero frequency)
            // So tile3 should be selected reliably
            {
                var t2     = new GridTopology(3, 1, false);
                var p2     = new TilePropagator(model, t2, constraints: constraints);
                var status = p2.Run();
                Assert.AreEqual(Resolution.Decided, status);
                Assert.AreEqual(tile3, p2.ToArray().Get(1, 0));
            }

            // tile5 can be reflected, but cannot
            // be placed adjacent to it's own reflection
            {
                var t2 = new GridTopology(2, 1, false);
                var p2 = new TilePropagator(model, t2, constraints: constraints);
                p2.Select(0, 0, 0, tile5);
                var status = p2.Run();
                Assert.AreEqual(Resolution.Contradiction, status);
            }

            {
                var t2 = new GridTopology(4, 1, false);
                var p2 = new TilePropagator(model, t2, constraints: constraints);
                p2.Select(0, 0, 0, tile5);
                var status = p2.Run();
                Assert.AreEqual(Resolution.Decided, status);
            }
        }