Issues implementing the Wave Collapse Function algorithm in Python
Data Structures & Algorithms practice on Codemia
Step through 300 algorithm problems with animated visualisers that show the data structure changing as the code runs.
Introduction
Wave Function Collapse, usually shortened to WFC, is easy to describe and surprisingly easy to break in code. The algorithm is not just "pick a tile randomly." It is a constraint-satisfaction system where every local choice must be propagated through neighboring cells until the grid becomes consistent again.
Model the Grid as Domains
Each cell starts with a domain: the set of tiles that are still allowed there. The algorithm repeatedly:
- chooses one unresolved cell
- collapses it to a single tile
- propagates that decision to neighbors
The most important data structure is not the final grid. It is the current domain of each cell.
A tiny Python representation might look like this:
If you lose track of domain updates, the implementation becomes incorrect long before it becomes slow.
Propagation Is Where Most Bugs Live
The usual failure mode is incomplete propagation. After one cell changes, you must reconsider affected neighbors, and then neighbors of those neighbors, until nothing else changes.
This loop is the heart of WFC. If propagation stops too early, the grid may appear valid temporarily and then fail much later for confusing reasons.
Choosing the Next Cell
Most WFC implementations pick the cell with the lowest entropy, meaning the smallest remaining domain greater than one. That is a good heuristic because it resolves the most constrained cells first.
The exact entropy formula can include tile weights, but that is secondary. A correct propagation engine with a simple heuristic is better than a fancy entropy formula on top of broken domain updates.
Contradictions Need a Strategy
Sooner or later, some collapse sequence produces an empty domain for a cell. That means the current branch is invalid.
You need one of these strategies:
- restart from scratch
- backtrack to an earlier choice
- relax tile constraints
Many Python implementations start with random restarts because they are much easier than full backtracking. That is a reasonable first version, but it can become expensive on larger grids or stricter tile sets.
Python Performance Problems
WFC performs many tiny set operations and neighbor checks, so Python overhead appears quickly. The first useful optimizations are usually:
- store tile IDs as small integers instead of strings
- precompute adjacency lookups
- use a queue instead of rescanning the full grid
- consider bitmasks for domains if the tile set is modest
For a learning implementation, plain Python sets are fine. For large procedural maps, compact representations pay off fast.
Keep the Rules Verifiable
A subtle source of bugs is bad adjacency data rather than bad algorithm code. If tile A allows tile B on the right, make sure tile B also allows tile A on the left when the design expects symmetry.
Before you generate a full map, test the rule table separately with tiny grids and assertions. It is far easier to debug one inconsistent rule than a full generator that sometimes collapses and sometimes implodes.
Common Pitfalls
- Treating propagation as a one-step neighbor update instead of a repeated fixed-point process.
- Spending too much effort on entropy math before domain propagation is correct.
- Ignoring contradictions instead of restarting or backtracking.
- Using slow rule lookups that turn every neighbor check into a bottleneck.
- Debugging the generator without first validating the tile adjacency rules.
Summary
- WFC is primarily a constraint-propagation algorithm.
- The key state is each cell's current domain of possible tiles.
- Correct propagation matters more than sophisticated entropy formulas.
- Contradictions are normal and require restart or backtracking logic.
- In Python, data representation and rule lookup speed determine how far the implementation scales.
Related reading
- Issues with understanding Dining table optimal seating algorithm
- Iterate through binary search tree to find all leaves
- iterated conditional mode E step EM
- Iterating over a Binary Tree with O1 Auxiliary Space
- Issues using Spyder Python to connect to a remote machine
- Items in JSON object are out of order using json.dumps?
- Iterating over every two elements in a list
- Iterating through dictionary with ForEach

DSA Fundamentals
Master algorithmic patterns and data structures through hands-on LeetCode-style problems - from arrays and hashing to dynamic programming and advanced graphs.
View the courseTrack what you have practised
A free account saves your progress, solutions and study plan across every problem on Codemia.
Data Structures & Algorithms practice on Codemia
Step through 300 algorithm problems with animated visualisers that show the data structure changing as the code runs.