Skip to content

Commit be33390

Browse files
committed
make the ground attribute boolean, small improvements
1 parent 9654013 commit be33390

19 files changed

Lines changed: 38 additions & 53 deletions

.github/FUNDING.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
github: [mxgmn]

Helper.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ public static int Random(this double[] weights, double r)
2525
partialSum += weights[i];
2626
if (partialSum >= threshold) return i;
2727
}
28-
return -1;
28+
return 0;
2929
}
3030

3131
public static long ToPower(this int a, int n)

Model.cs

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ abstract class Model
2020
int stacksize, observedSoFar;
2121

2222
protected int MX, MY, T, N;
23-
protected bool periodic;
23+
protected bool periodic, ground;
2424

2525
protected double[] weights;
2626
double[] weightLogWeights, distribution;
@@ -81,7 +81,7 @@ public bool Run(int seed, int limit)
8181
if (wave == null) Init();
8282

8383
Clear();
84-
Random random = new Random(seed);
84+
Random random = new(seed);
8585

8686
for (int l = 0; l < limit || limit < 0; l++)
8787
{
@@ -102,7 +102,7 @@ public bool Run(int seed, int limit)
102102
return true;
103103
}
104104

105-
protected int NextUnobservedNode(Random random)
105+
int NextUnobservedNode(Random random)
106106
{
107107
if (heuristic == Heuristic.Scanline)
108108
{
@@ -146,7 +146,7 @@ void Observe(int node, Random random)
146146
for (int t = 0; t < T; t++) if (w[t] != (t == r)) Ban(node, t);
147147
}
148148

149-
protected bool Propagate()
149+
bool Propagate()
150150
{
151151
while (stacksize > 0)
152152
{
@@ -185,7 +185,7 @@ protected bool Propagate()
185185
return sumsOfOnes[0] > 0;
186186
}
187187

188-
protected void Ban(int i, int t)
188+
void Ban(int i, int t)
189189
{
190190
wave[i][t] = false;
191191

@@ -202,7 +202,7 @@ protected void Ban(int i, int t)
202202
entropies[i] = Math.Log(sum) - sumsOfWeightLogWeights[i] / sum;
203203
}
204204

205-
protected virtual void Clear()
205+
void Clear()
206206
{
207207
for (int i = 0; i < wave.Length; i++)
208208
{
@@ -219,6 +219,16 @@ protected virtual void Clear()
219219
observed[i] = -1;
220220
}
221221
observedSoFar = 0;
222+
223+
if (ground)
224+
{
225+
for (int x = 0; x < MX; x++)
226+
{
227+
for (int t = 0; t < T - 1; t++) Ban(x + (MY - 1) * MX, t);
228+
for (int y = 0; y < MY - 1; y++) Ban(x + y * MX, T - 1);
229+
}
230+
Propagate();
231+
}
222232
}
223233

224234
public abstract System.Drawing.Bitmap Graphics();

OverlappingModel.cs

Lines changed: 4 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,8 @@ class OverlappingModel : Model
1515
{
1616
byte[][] patterns;
1717
List<Color> colors;
18-
int ground;
1918

20-
public OverlappingModel(string name, int N, int width, int height, bool periodicInput, bool periodic, int symmetry, int ground, Heuristic heuristic)
19+
public OverlappingModel(string name, int N, int width, int height, bool periodicInput, bool periodic, int symmetry, bool ground, Heuristic heuristic)
2120
: base(width, height, N, periodic, heuristic)
2221
{
2322
var bitmap = new Bitmap($"samples/{name}.png");
@@ -116,7 +115,7 @@ byte[] patternFromIndex(long ind)
116115
}
117116

118117
T = weights.Count;
119-
this.ground = (ground + T) % T;
118+
this.ground = ground;
120119
patterns = new byte[T][];
121120
base.weights = new double[T];
122121

@@ -141,7 +140,7 @@ bool agrees(byte[] p1, byte[] p2, int dx, int dy)
141140
propagator[d] = new int[T][];
142141
for (int t = 0; t < T; t++)
143142
{
144-
List<int> list = new List<int>();
143+
List<int> list = new();
145144
for (int t2 = 0; t2 < T; t2++) if (agrees(patterns[t], patterns[t2], dx[d], dy[d])) list.Add(t2);
146145
propagator[d][t] = new int[list.Count];
147146
for (int c = 0; c < list.Count; c++) propagator[d][t][c] = list[c];
@@ -151,7 +150,7 @@ bool agrees(byte[] p1, byte[] p2, int dx, int dy)
151150

152151
public override Bitmap Graphics()
153152
{
154-
Bitmap result = new Bitmap(MX, MY);
153+
Bitmap result = new(MX, MY);
155154
int[] bitmapData = new int[result.Height * result.Width];
156155

157156
if (observed[0] >= 0)
@@ -204,20 +203,4 @@ public override Bitmap Graphics()
204203

205204
return result;
206205
}
207-
208-
protected override void Clear()
209-
{
210-
base.Clear();
211-
212-
if (ground != 0)
213-
{
214-
for (int x = 0; x < MX; x++)
215-
{
216-
for (int t = 0; t < T; t++) if (t != ground) Ban(x + (MY - 1) * MX, t);
217-
for (int y = 0; y < MY - 1; y++) Ban(x + y * MX, ground);
218-
}
219-
220-
Propagate();
221-
}
222-
}
223206
}

Program.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ static void Main()
1616
{
1717
Stopwatch sw = Stopwatch.StartNew();
1818

19-
Random random = new Random();
19+
Random random = new();
2020
XDocument xdoc = XDocument.Load("samples.xml");
2121

2222
foreach (XElement xelem in xdoc.Root.Elements("overlapping", "simpletiled"))
@@ -38,7 +38,7 @@ static void Main()
3838
int N = xelem.Get("N", 3);
3939
bool periodicInput = xelem.Get("periodicInput", true);
4040
int symmetry = xelem.Get("symmetry", 8);
41-
int ground = xelem.Get("ground", 0);
41+
bool ground = xelem.Get("ground", false);
4242

4343
model = new OverlappingModel(name, N, width, height, periodicInput, periodic, symmetry, ground, heuristic);
4444
}

README.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ dihedral group D4 are isomorphic for tiles and their corresponding letters). Wit
6161

6262
Note that the unrestrained knot tileset (with all 5 tiles being allowed) is not interesting for WFC, because you can't run into a situation where you can't place a tile. We call tilesets with this property "easy". Without special heuristics easy tilesets don't produce interesting global arrangements, because correlations of tiles in easy tilesets quickly fall off with a distance. Many easy tilesets can be found on [Guy Walker's website](http://cr31.co.uk/stagecast/wang/tiles_e.html). Consider the "Dual" 2-edge tileset there. How can it generate knots (without t-junctions, not easy) while being easy? The answer is, it can only generate a narrow class of knots, it can't produce an arbitrary knot.
6363

64-
Note also that Circuit, Summer and Rooms tilesets are non-Wang. That is, their adjacency data cannot be induced from edge coloring. For example, in Circuit two Corners cannot be adjacent, yet they can be connected with a Connection tile, and diagonal tracks cannot change direction.
64+
Note also that Circuit, Summer and Rooms tilesets are non-Wang. That is, their adjacency data cannot be induced from edge labels. For example, in Circuit two Corners cannot be adjacent, yet they can be connected with a Connection tile, and diagonal tracks cannot change direction.
6565

6666
## Higher dimensions
6767
WFC algorithm in higher dimensions works completely the same way as in dimension 2, though performance becomes an issue. These voxel models were generated with N=2 overlapping tiled model using 5x5x5 and 5x5x2 blocks and additional heuristics (height, density, curvature, ...).
@@ -82,7 +82,7 @@ Here is WFC autocompleting a level started by a human:
8282

8383
[ConvChain](https://github.com/mxgmn/ConvChain) algorithm satisfies the strong version of the condition (C2): the limit distribution of NxN patterns in the outputs it is producing is exactly the same as the distributions of patterns in the input. However, ConvChain doesn't satisfy (C1): it often produces noticeable defects. It makes sense to run ConvChain first to get a well-sampled configuration and then run WFC to correct local defects. This is similar to a common strategy in optimization: first run a Monte-Carlo method to find a point close to a global optimum and then run a gradient descent from that point for greater accuracy.
8484

85-
P. F. Harrison's [texture synthesis](https://github.com/mxgmn/SynTex) algorithm is significantly faster than WFC, but it has trouble with long correlations (for example, it's difficult for this algorithm to synthesize brick wall textures with correctly aligned bricks). But this is exactly where WFC shines, and Harrison's algorithm supports constraints. It makes sense first to generate a perfect brick wall blueprint with WFC and then run a constrained texture synthesis algorithm on that blueprint.
85+
P. F. Harrison's [texture synthesis](https://github.com/mxgmn/TextureSynthesis) algorithm is significantly faster than WFC, but it has trouble with long correlations (for example, it's difficult for this algorithm to synthesize brick wall textures with correctly aligned bricks). But this is exactly where WFC shines, and Harrison's algorithm supports constraints. It makes sense first to generate a perfect brick wall blueprint with WFC and then run a constrained texture synthesis algorithm on that blueprint.
8686

8787
## Comments
8888
Why the minimal entropy heuristic? I noticed that when humans draw something they often follow the [minimal entropy heuristic](images/lowest-entropy-heuristic.gif) themselves. That's why the algorithm is so enjoyable to watch.
@@ -97,7 +97,7 @@ One of the dimensions can be time. In particular, d-dimensional WFC captures the
9797

9898
## Used work
9999
1. Alexei A. Efros and Thomas K. Leung, [Texture Synthesis by Non-parametric Sampling](https://www2.eecs.berkeley.edu/Research/Projects/CS/vision/papers/efros-iccv99.pdf), 1999. WaveFunctionCollapse is a [texture synthesis](https://en.wikipedia.org/wiki/Texture_synthesis) algorithm. Compared to the earlier texture synthesis algorithms, WFC guarantees that the output contains only those NxN patterns that are present in the input. This makes WFC perfect for level generation in games and pixel art, and less suited for large full-color textures.
100-
2. Paul C. Merrell, [Model Synthesis](http://graphics.stanford.edu/~pmerrell/thesis.pdf), 2009. Merrell derives adjacency constraints between tiles from an example model and generates a new larger model with the AC-3 algorithm. We generalize his approach to work with NxN patterns of tiles instead of individual tiles. This allows to use a single image as the input to the algorithm. By varying N, we can make the output look more like the input or less. We introduce the [lowest entropy heuristic](images/lowest-entropy-heuristic.gif) that removes the [directional bias](images/directional-bias.png) in generated results, is defined for irregular grids and is better suited for [pre-constrained problems](images/constrained.gif). We implement a tile symmetry system to reduce the sizes of inputs. We visualize partially observed states, either with [color averaging](images/wfc.gif) or [per-voxel voting](https://twitter.com/ExUtumno/status/900395635412787202). Merrell also introduced a method of incrementally modifying the model in parts to reduce the failure rate (which we don't use here). Recently the author created a [page](https://paulmerrell.org/model-synthesis/) for model synthesis and published [code](https://github.com/merrell42/model-synthesis).
100+
2. Paul C. Merrell, [Model Synthesis](http://graphics.stanford.edu/~pmerrell/thesis.pdf), 2009. Merrell derives adjacency constraints between tiles from an example model and generates a new larger model with the AC-3 algorithm. We generalize his approach to work with NxN overlapping patterns of tiles instead of individual tiles. This allows to use a single image as the input to the algorithm. By varying N, we can make the output look more like the input or less. We introduce the [lowest entropy heuristic](images/lowest-entropy-heuristic.gif) that removes the [directional bias](images/directional-bias.png) in generated results, is defined for irregular grids and is better suited for [pre-constrained problems](images/constrained.gif). We implement a tile symmetry system to reduce the sizes of inputs. We visualize partially observed states, either with [color averaging](images/wfc.gif) or [per-voxel voting](https://twitter.com/ExUtumno/status/900395635412787202). Merrell also introduced a method of incrementally modifying the model in parts to reduce the failure rate (which we don't use here). Recently the author created a [page](https://paulmerrell.org/model-synthesis/) for model synthesis and published [code](https://github.com/merrell42/model-synthesis).
101101
3. Alan K. Mackworth, [Consistency in Networks of Relations](https://www.cs.ubc.ca/~mack/Publications/AI77.pdf), 1977. WFC translates a texture synthesis problem into a constraint satisfaction problem. Currently it uses the [AC-4 algorithm](http://www.cs.utah.edu/~tch/CS4300/resources/AC4.pdf) by Roger Mohr and Thomas C. Henderson, 1986.
102102
4. Paul F. Harrison, [Image Texture Tools](http://logarithmic.net/pfh-files/thesis/dissertation.pdf), 2005. WFC was also influenced by the declarative texture synthesis chapter of Paul Harrison's dissertation. The author defines adjacency data of tiles by labeling their borders and uses backtracking search to fill the tilemap. A [demonstration of the algorithm](https://logarithmic.net/ghost.xhtml) is available on the web.
103103

@@ -159,7 +159,7 @@ that the resulting observed zone is navigable at each step.
159159
* Stephen Sherratt wrote a [detailed explanation](https://gridbugs.org/wave-function-collapse/) of the overlapping model and made a [Rust library](https://github.com/stevebob/wfc). For the 7DRL Challenge 2019 he made a roguelike [Get Well Soon](https://gridbugs.org/get-well-soon/) that [uses](https://gridbugs.org/7drl2019-day1/) WFC to generate levels.
160160
* Florian Drux created a [generalization](https://github.com/lamelizard/GraphWaveFunctionCollapse/blob/master/thesis.pdf) that works on graphs with arbitrary local structure and [implemented](https://github.com/lamelizard/GraphWaveFunctionCollapse) it in Python.
161161
* Bob Burrough [discovered](https://twitter.com/ExUtumno/status/1119996185199116289) a percolation-like phase transition in one of the tilesets that manifests in spiking contradiction rate.
162-
* Oskar Stålberg combined WFC with marching cubes on irregular grids and made a town building toy [Townscaper](https://store.steampowered.com/app/1291340/Townscaper/) based on it: [1](https://twitter.com/OskSta/status/1164926304640229376), [2](https://twitter.com/OskSta/status/1168168400155267072), [3](https://twitter.com/OskSta/status/1181464374839521280), [4](https://twitter.com/OskSta/status/1189109278361165825), [5](https://twitter.com/OskSta/status/1189902695303458816), [6](https://www.youtube.com/watch?v=1hqt8JkYRdI). Oskar gave a number of talks and interviews about mixed initiative town generation in Townscaper: [EPC2021](https://www.youtube.com/watch?v=NOJYZYqY6_M), [SGC21](https://www.youtube.com/watch?v=Uxeo9c-PX-w), [Konsoll 2021](https://www.youtube.com/watch?v=5xrRTOikBBg), [AI and Games](https://www.youtube.com/watch?v=_1fvJ5sHh6A).
162+
* Oskar Stålberg combined WFC with marching cubes on irregular grids and made a town building toy [Townscaper](https://store.steampowered.com/app/1291340/Townscaper/) based on it: [1](https://twitter.com/OskSta/status/1164926304640229376), [2](https://twitter.com/OskSta/status/1168168400155267072), [3](https://twitter.com/OskSta/status/1181464374839521280), [4](https://twitter.com/OskSta/status/1189109278361165825), [5](https://twitter.com/OskSta/status/1189902695303458816), [6](https://www.youtube.com/watch?v=1hqt8JkYRdI). Oskar gave a number of talks and interviews about the mixed initiative town generation in Townscaper: [EPC2021](https://www.youtube.com/watch?v=NOJYZYqY6_M), [SGC21](https://www.youtube.com/watch?v=Uxeo9c-PX-w), [Konsoll 2021](https://www.youtube.com/watch?v=5xrRTOikBBg), [AI and Games](https://www.youtube.com/watch?v=_1fvJ5sHh6A).
163163
* In his Rust roguelike tutorial, [Herbert Wolverson](https://github.com/thebracket) wrote a [chapter](http://bfnightly.bracketproductions.com/rustbook/chapter_33.html) about implementing the WFC algorithm from scratch.
164164
* At the [Game Developers Conference 2019](https://www.youtube.com/watch?v=AdCgi9E90jw) and the [Roguelike Celebration 2019](https://www.youtube.com/watch?v=fnFj3dOKcIQ), [Brian Bucklew](https://github.com/unormal) gave talks about WFC and how Freehold Games uses it to generate levels in [Caves of Qud](https://store.steampowered.com/app/333640/Caves_of_Qud/). The talks discuss problems with overfitting and homogeny, level connectedness and combining WFC with constructive procgen methods.
165165
* [Boris the Brave](https://github.com/boristhebrave) published a [commercial Unity asset](https://assetstore.unity.com/packages/tools/modeling/tessera-procedural-tile-based-generator-155425) based on the tiled model.
@@ -174,7 +174,7 @@ that the resulting observed zone is navigable at each step.
174174
* [Ivan Donchevskii](https://github.com/yvvan) published a [commercial Unreal Engine plugin](https://www.unrealengine.com/marketplace/en-US/product/procedural-environment-generator-wfc) based on the tiled model.
175175
* [Ján Pernecký](https://github.com/janper) and [Ján Tóth](https://github.com/yanchith) published a [Grasshopper plugin](https://github.com/subdgtl/Monoceros) that extends the tiled model.
176176
* Krystian Samp made a [single-file overlapping WFC library in C](https://github.com/krychu/wfc).
177-
* [Gerald Krystian](https://github.com/amarcolina) made an [interactive tool](https://amarcolina.github.io/WFC-Explorer/) that explores the tiled model where tile adjacencies are induced from edge colorings.
177+
* [Gerald Krystian](https://github.com/amarcolina) made an [interactive tool](https://amarcolina.github.io/WFC-Explorer/) that explores the tiled model where tile adjacencies are induced from edge labels.
178178
* DeepMind open-ended learning team [used](https://storage.googleapis.com/deepmind-media/papers/Open-Ended%20Learning%20Leads%20to%20Generally%20Capable%20Agents/open-ended-learning-paper.pdf) WFC to generate arenas for reinforcement learning agents.
179179
* Oskar Stålberg [made](https://twitter.com/OskSta/status/1447483550257799171) an island generator that combines triangle and quad tiles and uses a custom observation heuristic that doesn't produce local minimums.
180180
* [Boris the Brave](https://github.com/boristhebrave) [applied](https://www.boristhebrave.com/2021/11/08/infinite-modifying-in-blocks/) [Paul Merrell's](https://github.com/merrell42) modifying in blocks technique to the lazy generation of unbounded tile configurations. Marian Kleineberg has [implemented](https://twitter.com/marian42_/status/1490060483944140804) this method into his [infinite city generator](https://github.com/marian42/wavefunctioncollapse).

SimpleTiledModel.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -125,14 +125,14 @@ Color[] tile(Func<int, int, Color> f)
125125
{
126126
for (int t = 0; t < cardinality; t++)
127127
{
128-
Bitmap bitmap = new Bitmap($"samples/{name}/{tilename} {t}.png");
128+
Bitmap bitmap = new($"samples/{name}/{tilename} {t}.png");
129129
tiles.Add(tile((x, y) => bitmap.GetPixel(x, y)));
130130
tilenames.Add($"{tilename} {t}");
131131
}
132132
}
133133
else
134134
{
135-
Bitmap bitmap = new Bitmap($"samples/{name}/{tilename}.png");
135+
Bitmap bitmap = new($"samples/{name}/{tilename}.png");
136136
tiles.Add(tile((x, y) => bitmap.GetPixel(x, y)));
137137
tilenames.Add($"{tilename} 0");
138138

@@ -209,7 +209,7 @@ Color[] tile(Func<int, int, Color> f)
209209

210210
public override Bitmap Graphics()
211211
{
212-
Bitmap result = new Bitmap(MX * tilesize, MY * tilesize);
212+
Bitmap result = new(MX * tilesize, MY * tilesize);
213213
int[] bitmapData = new int[result.Height * result.Width];
214214

215215
if (observed[0] >= 0)

WaveFunctionCollapse.csproj

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,9 @@
22

33
<PropertyGroup>
44
<OutputType>Exe</OutputType>
5-
<TargetFramework>net5.0</TargetFramework>
5+
<TargetFramework>net6.0</TargetFramework>
66
</PropertyGroup>
77

8-
<ItemGroup>
9-
<Content Include="samples.xml">
10-
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
11-
</Content>
12-
<Content Include="samples\**">
13-
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
14-
</Content>
15-
</ItemGroup>
16-
178
<ItemGroup>
189
<PackageReference Include="System.Drawing.Common" Version="4.5.1" />
1910
</ItemGroup>

images/castle-3d.png

-4.22 KB
Loading

images/castle.png

-273 Bytes
Loading

0 commit comments

Comments
 (0)