Created
December 12, 2022 21:43
-
-
Save Kellojo/6d4b0736cf4615133fd0ce8d58a479e3 to your computer and use it in GitHub Desktop.
A simple wave function collapse algorithm implementation useful for procedural generation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using Kellojo.Utility; | |
using System.Collections; | |
using System.Collections.Generic; | |
using UnityEngine; | |
public class WaveFunctionCollapse<T> where T : IWfcTile<T> { | |
public WfcCell<T>[,] Cells; | |
public int Entropy { | |
get { | |
var e = 0; | |
for (int x = 0; x < Cells.GetLength(0); x++) | |
for (int y = 0; y < Cells.GetLength(1); y++) | |
e += Cells[x, y].Entropy; | |
return e; | |
} | |
} | |
public WaveFunctionCollapse(WfcCell<T>[,] cells) { | |
Cells = cells; | |
} | |
public void Run() { | |
while (Entropy > 0) { | |
// find lowest entropy cell or pick random | |
var cell = GetLowestEntropyCell(); | |
// collapse that cell | |
cell.Collapse(); | |
// propagate changes | |
var stack = new Stack<WfcCell<T>>(); | |
stack.Push(cell); | |
while (stack.Peek() != null) { | |
var curCell = stack.Pop(); | |
var neighbours = GetNeighboursFor(curCell); | |
foreach(var neighbour in neighbours) { | |
if (neighbour.Constrain(curCell.GetAllowedNeighbours())) { | |
stack.Push(neighbour); | |
} | |
} | |
} | |
} | |
} | |
public WfcCell<T> GetLowestEntropyCell() { | |
var minEntropy = 0; | |
WfcCell<T> minEntropyCell = null; | |
for (int x = 0; x < Cells.GetLength(0); x++) { | |
for (int y = 0; y < Cells.GetLength(1); y++) { | |
var cell = Cells[x, y]; | |
if (cell.Entropy < minEntropy) { | |
minEntropyCell = cell; | |
} | |
} | |
} | |
return minEntropyCell; | |
} | |
public WfcCell<T> TryGetCellAt(int x, int y) { | |
if (Cells.GetLength(0) < x && Cells.GetLength(1) < y) { | |
return Cells[x, y]; | |
} | |
return null; | |
} | |
public List<WfcCell<T>> GetNeighboursFor(WfcCell<T> cell) { | |
var neighbours = new List<WfcCell<T>>(); | |
neighbours.Add(); | |
return neighbours; | |
} | |
} | |
public abstract class WfcCell<T> where T : IWfcTile<T> { | |
public List<T> PossibleStates; | |
public T ResultingState; | |
public int Entropy { | |
get { | |
return PossibleStates.Count - 1; | |
} | |
} | |
public void Collapse() { | |
ResultingState = PossibleStates.PickRandom(); | |
} | |
public bool Constrain(List<T> possibleStates) { | |
bool modified = false; | |
foreach(var state in PossibleStates) { | |
if (!possibleStates.Contains(state)) { | |
PossibleStates.Remove(state); | |
modified = true; | |
} | |
} | |
return modified; | |
} | |
public List<T> GetAllowedNeighbours() { | |
var allowed = new List<T>(); | |
PossibleStates.ForEach(state => allowed.AddRange(state.GetAllowedNeighbours())); | |
return allowed; | |
} | |
} | |
public interface IWfcTile<T> { | |
public abstract List<T> GetAllowedNeighbours(); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment