fyrox_autotile/wave.rs
1// Copyright (c) 2019-present Dmitry Stepanov and Fyrox Engine contributors.
2//
3// Permission is hereby granted, free of charge, to any person obtaining a copy
4// of this software and associated documentation files (the "Software"), to deal
5// in the Software without restriction, including without limitation the rights
6// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7// copies of the Software, and to permit persons to whom the Software is
8// furnished to do so, subject to the following conditions:
9//
10// The above copyright notice and this permission notice shall be included in all
11// copies or substantial portions of the Software.
12//
13// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19// SOFTWARE.
20
21//! Wave function collapse algorithm based upon [fast-wfc](https://github.com/math-fehr/fast-wfc).
22
23use std::{
24 error::Error,
25 fmt::Display,
26 ops::{Deref, DerefMut},
27};
28
29use super::*;
30
31type Entropy = f64;
32
33/// A `WavePosition` is like an `OffsetPosition` but it also has a counter type
34/// that the wave propagator can use to keep count of how many possible matching
35/// patterns are available from each `Offset`.
36pub trait WavePosition: OffsetPosition {
37 /// The type that holds count of patterns in each direction.
38 type Counter: WaveOffsetCounter<Offset = Self::Offset>;
39}
40
41impl WavePosition for Vector2<i32> {
42 type Counter = Vector2OffsetCounter;
43}
44
45/// A trait for counting types that store a `usize` for each of the offsets from
46/// some `OffsetPosition` type. During the wave function collapse process, these
47/// counts can be reduced as adjacent patterns possibilities are removed so
48/// that the algorithm can keep track of how many ways a pattern is possible.
49/// For a pattern to be possible, it must match some possible pattern in all
50/// offsets, so if any of these counts become 0 the algorithm will know to
51/// remove the corresponding pattern.
52pub trait WaveOffsetCounter: Default + Clone {
53 /// The type that will be the index into the array of `usize` counts.
54 type Offset;
55 /// The count for the given offset.
56 fn count(&self, offset: &Self::Offset) -> usize;
57 /// The count for the given offset.
58 fn count_mut(&mut self, offset: &Self::Offset) -> &mut usize;
59}
60
61/// A [`WaveOffsetCounter`] for `Vector3` offsets, containing 4 `usize` values,
62/// one for each adjacent cell of a pattern in 2D.
63#[derive(Debug, Clone, Default)]
64pub struct Vector2OffsetCounter([usize; 4]);
65
66impl WaveOffsetCounter for Vector2OffsetCounter {
67 type Offset = Vector2Offset;
68
69 fn count(&self, offset: &Vector2Offset) -> usize {
70 self.0[offset.0]
71 }
72
73 fn count_mut(&mut self, offset: &Vector2Offset) -> &mut usize {
74 &mut self.0[offset.0]
75 }
76}
77
78impl WavePosition for Vector3<i32> {
79 type Counter = Vector3OffsetCounter;
80}
81
82/// A [`WaveOffsetCounter`] for `Vector3` offsets, containing 6 `usize` values,
83/// one for each adjacent cell of a pattern in 3D.
84#[derive(Debug, Clone, Default)]
85pub struct Vector3OffsetCounter([usize; 6]);
86
87impl WaveOffsetCounter for Vector3OffsetCounter {
88 type Offset = Vector3Offset;
89
90 fn count(&self, offset: &Vector3Offset) -> usize {
91 self.0[offset.0]
92 }
93
94 fn count_mut(&mut self, offset: &Vector3Offset) -> &mut usize {
95 &mut self.0[offset.0]
96 }
97}
98
99/// Trait for an object that specifies the rules by which wave function collapse
100/// tiles are chosen.
101pub trait WfcConstrain {
102 /// The offset type that is used to specify the ways in which one pattern
103 /// may be adjacent to another pattern.
104 type Offset;
105 /// The type of patterns that wave function collapse will choose.
106 type Pattern: Eq + Hash + Clone;
107 /// Iterator for all the patterns that wave function collapse is allowed to choose.
108 /// Note that any pattern with a probability of 0.0 must not be included.
109 fn all_patterns(&self) -> impl Iterator<Item = &Self::Pattern>;
110 /// The probability of choosing a given pattern when there are no restrictions on patterns
111 /// in any of the adjacent cells. This should be between 0.0 and 1.0.
112 fn probability_of(&self, pattern: &Self::Pattern) -> f32;
113 /// True if the `from` pattern may be chosen when the `to` pattern has already been chosen
114 /// for the cell at position `offset` relative to `from`.
115 fn is_legal(&self, from: &Self::Pattern, offset: &Self::Offset, to: &Self::Pattern) -> bool;
116}
117
118/// An implementation for `WfcConstrain` that stores the information for each pattern
119/// in a hash table and also keeps track of a which values each pattern may represent,
120/// so that patterns may be translated into tiles after wave function collapse is complete.
121pub struct HashWfcConstraint<Pat, V> {
122 pattern_map: FxHashMap<Pat, WfcPatternConstraint<V>>,
123}
124
125/// The data for a pattern, with probability of the pattern
126/// and probability of each tile that matches the pattern.
127struct WfcPatternConstraint<V> {
128 /// The probability of the pattern being chosen, between 0.0 and 1.0.
129 probability: f32,
130 /// The set of values that may be randomly chosen to represent the pattern
131 /// when patterns are converted into tiles.
132 value_set: ProbabilitySet<V>,
133}
134
135impl<V> Default for WfcPatternConstraint<V> {
136 fn default() -> Self {
137 Self {
138 probability: 0.0,
139 value_set: ProbabilitySet::default(),
140 }
141 }
142}
143
144impl<Pat, V> Default for HashWfcConstraint<Pat, V> {
145 fn default() -> Self {
146 Self {
147 pattern_map: FxHashMap::default(),
148 }
149 }
150}
151
152impl<Pat, V> HashWfcConstraint<Pat, V>
153where
154 Pat: Eq + Hash,
155{
156 /// True if this constraint contains no patterns.
157 pub fn is_empty(&self) -> bool {
158 self.pattern_map.is_empty()
159 }
160 /// Remove all data, reseting this object to empty so it is ready
161 /// to be reused with new pattern data.
162 pub fn clear(&mut self) {
163 self.pattern_map.clear();
164 }
165 /// Add a new value to the data with the given pattern and frequency.
166 /// The frequency does not need to be between 0.0 and 1.0.
167 /// Frequencies of 0.0 or less will be silently ignored, and
168 /// frequencies will be automatically normalized into probabilities
169 /// when [`finalize`](Self::finalize) is called.
170 pub fn add(&mut self, pattern: Pat, frequency: f32, value: V)
171 where
172 Pat: Debug,
173 V: Debug,
174 {
175 if frequency <= 0.0 {
176 return;
177 }
178 self.pattern_map
179 .entry(pattern)
180 .or_default()
181 .value_set
182 .add(frequency, value);
183 }
184 /// Calculate the probability of each pattern based on the frequencies
185 /// of all values that were added with [`add`](Self::add).
186 /// The sum of the frequencies is calculated and then each frequency is
187 /// divided by the sum to normalize the frequencies into probabilities between
188 /// 0.0 and 1.0.
189 pub fn finalize(&mut self) {
190 let sum: f32 = self
191 .pattern_map
192 .values()
193 .map(|v| v.value_set.total_frequency())
194 .sum();
195 if sum > 0.0 {
196 for v in self.pattern_map.values_mut() {
197 v.probability = v.value_set.total_frequency() / sum;
198 }
199 }
200 }
201 /// Calculate the probability of each pattern based on the frequencies
202 /// of all values that were added with [`add`](Self::add).
203 /// Divide the frequency of each pattern by the total number of patterns
204 /// in that pattern's terrain so that terrains with many patterns are not
205 /// given an advantage over terrains with few patterns.
206 /// The `terrain` function is used to determine the terrain of each pattern.
207 /// The sum of the frequencies is calculated and then each frequency is
208 /// divided by the sum to normalize the frequencies into probabilities between
209 /// 0.0 and 1.0.
210 pub fn finalize_with_terrain_normalization<Ter, F>(&mut self, terrain: F)
211 where
212 Ter: Hash + Eq,
213 F: Fn(&Pat) -> Ter,
214 {
215 let mut terrain_count = FxHashMap::<Ter, usize>::default();
216 for p in self.pattern_map.keys() {
217 *terrain_count.entry(terrain(p)).or_default() += 1;
218 }
219 let sum: f32 = self
220 .pattern_map
221 .iter()
222 .map(|(p, v)| {
223 let count = terrain_count.get(&terrain(p)).copied().unwrap_or_default() as f32;
224 if count > 0.0 {
225 v.value_set.total_frequency() / count
226 } else {
227 1.0
228 }
229 })
230 .sum();
231 if sum > 0.0 {
232 for v in self.pattern_map.values_mut() {
233 v.probability = v.value_set.total_frequency() / sum;
234 }
235 }
236 }
237 /// A random value that matches the given pattern, based upon previous calls to
238 /// [`add`](Self::add). Often there may be more than one tile that matches a particular
239 /// pattern, and so this method helps with the final step after wave function collapse:
240 /// converting the chosen patterns into actual tiles.
241 pub fn get_random<R: Rng + ?Sized>(&self, rng: &mut R, pattern: &Pat) -> Option<&V> {
242 self.pattern_map.get(pattern)?.value_set.get_random(rng)
243 }
244}
245
246impl<Pat, V> WfcConstrain for HashWfcConstraint<Pat, V>
247where
248 Pat: Eq + Hash + Clone + TilePattern,
249{
250 type Offset = Pat::Offset;
251 type Pattern = Pat;
252
253 fn all_patterns(&self) -> impl Iterator<Item = &Self::Pattern> {
254 self.pattern_map.keys()
255 }
256
257 fn probability_of(&self, pattern: &Self::Pattern) -> f32 {
258 self.pattern_map
259 .get(pattern)
260 .map(|s| s.probability)
261 .unwrap_or_default()
262 }
263
264 fn is_legal(&self, from: &Self::Pattern, offset: &Self::Offset, to: &Self::Pattern) -> bool {
265 from.is_legal(offset, to)
266 }
267}
268
269#[derive(Debug, Clone)]
270struct Wave<Pos: WavePosition, Pat>(FxHashMap<Pos, WaveCell<Pos::Counter, Pat>>);
271
272impl<K: WavePosition, P> Default for Wave<K, P> {
273 fn default() -> Self {
274 Self(FxHashMap::default())
275 }
276}
277
278impl<Pos: WavePosition, Pat> Deref for Wave<Pos, Pat> {
279 type Target = FxHashMap<Pos, WaveCell<Pos::Counter, Pat>>;
280
281 fn deref(&self) -> &Self::Target {
282 &self.0
283 }
284}
285
286impl<Pos: WavePosition, Pat> DerefMut for Wave<Pos, Pat> {
287 fn deref_mut(&mut self) -> &mut Self::Target {
288 &mut self.0
289 }
290}
291
292#[derive(Debug, Clone)]
293struct WaveCell<Count, Pat> {
294 /// The possible patterns, each with a count of the possible patterns in other
295 /// cells that make this pattern possible.
296 pattern_possibilities: FxHashMap<Pat, Count>,
297 /// The sum of p(P) * log(p(P)) for all possible patterns P in the cell.
298 plogp_sum: Entropy,
299 /// The sum of p(P) for all possible patterns P in the cell.
300 sum: Entropy,
301 /// The log of `sum`.
302 log_sum: Entropy,
303 /// The entropy of the cell
304 entropy: Entropy,
305}
306
307impl<Count, Pat> Default for WaveCell<Count, Pat> {
308 fn default() -> Self {
309 Self {
310 pattern_possibilities: FxHashMap::default(),
311 plogp_sum: 0.0,
312 sum: 0.0,
313 log_sum: 0.0,
314 entropy: 0.0,
315 }
316 }
317}
318
319impl<Count, Pat> WaveCell<Count, Pat> {
320 pub fn single_pattern(&self) -> Option<&Pat> {
321 let mut keys = self.pattern_possibilities.keys();
322 let pat = keys.next();
323 if keys.next().is_none() {
324 pat
325 } else {
326 None
327 }
328 }
329}
330
331/// Statistical information derived from a [`WfcConstrain`] object.
332/// A `WaveLimits` object is only valid for a particular `WfcConstrain` object
333/// so long as the output of the `WfcConstrain` object's methods do not change.
334#[derive(Debug, Clone)]
335struct WaveLimits<Count, Pat> {
336 max_cell: WaveCell<Count, Pat>,
337 plogp_map: FxHashMap<Pat, Entropy>,
338 maximum_noise: Entropy,
339}
340
341impl<Count, Pat> Default for WaveLimits<Count, Pat> {
342 fn default() -> Self {
343 Self {
344 max_cell: WaveCell::default(),
345 plogp_map: FxHashMap::default(),
346 maximum_noise: 0.0,
347 }
348 }
349}
350
351impl<Count: WaveOffsetCounter, Pat: Clone + Hash + Eq> WaveLimits<Count, Pat> {
352 /// Use the data in a [`WfcConstrain`] object to initialize the limits
353 /// for a new wave function collapse using the given constraint.
354 pub fn fill_from<
355 Pos: WavePosition<Counter = Count, Offset = Count::Offset>,
356 Con: WfcConstrain<Pattern = Pat, Offset = Count::Offset>,
357 >(
358 &mut self,
359 constraint: &Con,
360 ) {
361 self.plogp_map.clear();
362 let mut plogp_sum = 0.0;
363 let mut sum = 0.0;
364 let mut min_abs_plogp = Entropy::INFINITY;
365 self.max_cell.pattern_possibilities.clear();
366 for pattern in constraint.all_patterns() {
367 let p = constraint.probability_of(pattern) as Entropy;
368 let plogp = p * p.ln();
369 self.plogp_map.insert(pattern.clone(), plogp);
370 let abs_plogp = plogp.abs();
371 if abs_plogp < min_abs_plogp {
372 min_abs_plogp = abs_plogp;
373 }
374 plogp_sum += plogp;
375 sum += p;
376 let mut count = Count::default();
377 for offset in Pos::all_offsets() {
378 *count.count_mut(&offset) = constraint
379 .all_patterns()
380 .filter(|p| constraint.is_legal(p, &offset, pattern))
381 .count();
382 }
383 self.max_cell
384 .pattern_possibilities
385 .insert(pattern.clone(), count);
386 }
387 let log_sum = sum.log10();
388 self.max_cell.plogp_sum = plogp_sum;
389 self.max_cell.sum = sum;
390 self.max_cell.log_sum = log_sum;
391 self.max_cell.entropy = log_sum - plogp_sum / sum;
392 self.maximum_noise = min_abs_plogp / 2.0;
393 }
394}
395
396/// Wave function collapse is a multi-step that may require a significant amount of time,
397/// depending on how may cells need to be filled. `WfcControlFlow` allows wave propagation
398/// methods to specify in their return value whether they have completed their task or whether
399/// further work is required. This allows the user to spread the wave function collapse
400/// across multiple method calls so it can be mixed with other work or even aborted.
401#[derive(Debug, Clone, Copy, Eq, PartialEq)]
402pub enum WfcControlFlow {
403 /// More work is required.
404 Continue,
405 /// The method has finished whatever it was trying to do.
406 Finish,
407}
408
409/// Wave function collapse failed to find a solution. Due to the randomization of the algorithm,
410/// a failed attempt does not necessarily indicate a problem, but may instead be due to chance.
411/// Another attempt my succeed where a previous one failed.
412#[derive(Debug)]
413pub struct WfcFailure;
414
415impl Error for WfcFailure {}
416
417impl Display for WfcFailure {
418 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
419 f.write_str("Wave function collapse failed to find a solution.")
420 }
421}
422
423/// The propagator is responsible for the wave function collapse algorithm and
424/// it contains all the necessary data except for the [`WfcConstrain`] object
425/// that contains the rules for which patterns may be chosen.
426///
427/// The contraint object is not owned by the propagator because the wave function
428/// collapse algorithm may be spread out across many steps and it may be necessary
429/// for the constraint object to not live as long as the propagator, and be
430/// reconstructed at each step.
431///
432/// Every constraint object at each step should always fully agree with the constraint
433/// objects at all previous steps, or else the algorithm may produce undesired results.
434#[derive(Debug, Default, Clone)]
435pub struct WfcPropagator<Pos: WavePosition, Pat> {
436 limits: WaveLimits<Pos::Counter, Pat>,
437 wave: Wave<Pos, Pat>,
438 propagating: Vec<(Pos, Pat)>,
439 pending: Vec<(Pos, Pat)>,
440 backtrack_cells: Vec<WaveCell<Pos::Counter, Pat>>,
441 backtrack_map: FxHashMap<Pos, WaveCell<Pos::Counter, Pat>>,
442}
443
444impl<Pos: WavePosition + Debug, Pat: Clone + Hash + Eq + Debug> WfcPropagator<Pos, Pat> {
445 /// This propagator contains no cells.
446 pub fn is_empty(&self) -> bool {
447 self.wave.is_empty()
448 }
449 /// An iterator over the positions of every cell of the wave.
450 pub fn positions(&self) -> impl Iterator<Item = &Pos> {
451 self.wave.keys()
452 }
453 /// True if the wave has a cell at the given position.
454 pub fn contains_cell(&self, position: &Pos) -> bool {
455 self.wave.contains_key(position)
456 }
457 /// Iterator over the cells that have been given a pattern by the wave function collapse.
458 pub fn assigned_patterns(&self) -> impl Iterator<Item = (&Pos, &Pat)> {
459 self.wave
460 .iter()
461 .filter_map(|(p, c)| Some((p, c.single_pattern()?)))
462 }
463 /// Use the data in a [`WfcConstrain`] object to initialize the propagator
464 /// for a new wave function collapse using the given constraint.
465 /// After calling this method, the next step is to call [`add_cell`](Self::add_cell) for each
466 /// cell that needs to be filled by wave function collapse.
467 pub fn fill_from<Con: WfcConstrain<Pattern = Pat, Offset = <Pos as OffsetPosition>::Offset>>(
468 &mut self,
469 constraint: &Con,
470 ) {
471 self.limits.fill_from::<Pos, Con>(constraint);
472 self.wave.clear();
473 self.propagating.clear();
474 }
475 /// Create a new cell at the given position, assuming that wave function collapse has not yet begun
476 /// and all the surrounding cells have no restrictions.
477 /// Calling this after [`restrict_edge`](Self::restrict_edge), [`observe_random_cell`](Self::observe_random_cell),
478 /// or [`observe_all`](Self::observe_all) may produce undesirable consequences.
479 /// After all the cells have been added, the next step is to optionally call `restrict_edge` to constrain which patterns
480 /// the may be put into the border cells.
481 pub fn add_cell(&mut self, position: Pos) {
482 let _ = self.wave.insert(position, self.limits.max_cell.clone());
483 }
484 fn save_cell(&mut self, position: Pos) {
485 if let Entry::Vacant(entry) = self.backtrack_map.entry(position.clone()) {
486 let Some(cell) = self.wave.get(&position) else {
487 return;
488 };
489 let mut backtrack_cell = self.backtrack_cells.pop().unwrap_or_default();
490 backtrack_cell.clone_from(cell);
491 entry.insert(backtrack_cell);
492 }
493 }
494 fn clear_backtrack(&mut self) {
495 for (_, backtrack_cell) in self.backtrack_map.drain() {
496 self.backtrack_cells.push(backtrack_cell);
497 }
498 }
499 fn backtrack(&mut self) {
500 for (pos, backtrack_cell) in self.backtrack_map.drain() {
501 let Some(cell) = self.wave.get_mut(&pos) else {
502 continue;
503 };
504 cell.clone_from(&backtrack_cell);
505 self.backtrack_cells.push(backtrack_cell);
506 }
507 }
508 /// Randomly choose a low-entropy cell. This is used by [`observe_random_cell`](Self::observe_random_cell)
509 /// to decide which cell will be observed.
510 pub fn find_min_entropy<R: Rng + ?Sized>(&self, rng: &mut R) -> Option<Pos>
511 where
512 Pos::Counter: Debug,
513 {
514 let mut min_pos = None;
515 let mut min_entropy = Entropy::INFINITY;
516 for (position, cell) in self.wave.iter() {
517 if cell.pattern_possibilities.len() <= 1 || cell.entropy >= min_entropy {
518 continue;
519 }
520 let noise = rng.gen_range(0.0..=self.limits.maximum_noise);
521 let entropy = cell.entropy + noise;
522 if entropy < min_entropy {
523 min_entropy = entropy;
524 min_pos = Some(position.clone());
525 }
526 }
527 min_pos
528 }
529 /// Choose a random pattern from among the potential patterns for the given cell based
530 /// on probabilities given in `constraint`. This is called by
531 /// [`observe_random_cell`](Self::observe_random_cell) to decide which pattern to assign.
532 pub fn choose_random_pattern<R, Con>(
533 &self,
534 position: &Pos,
535 rng: &mut R,
536 constraint: &Con,
537 ) -> Option<Pat>
538 where
539 R: Rng + ?Sized,
540 Con: WfcConstrain<Pattern = Pat, Offset = Pos::Offset>,
541 {
542 let cell = self.wave.get(position)?;
543 let mut target = rng.gen_range(0.0..cell.sum);
544 for pattern in cell.pattern_possibilities.keys() {
545 let p = constraint.probability_of(pattern) as Entropy;
546 target -= p;
547 if target <= 0.0 {
548 return Some(pattern.clone());
549 }
550 }
551 cell.pattern_possibilities.keys().next().cloned()
552 }
553 /// Constrain the cells around the given position based on the assumption that the given
554 /// position has the given pattern. Calling this method twice on the same position may put
555 /// the surrounding cells into an invalid state, even if both calls have the same pattern.
556 /// This should be called after all cells have been added using [`add_cell`](Self::add_cell),
557 /// and there should be no cell at the given position.
558 ///
559 /// The next step is to call [`observe_random_cell`](Self::observe_random_cell) or
560 /// [`observe_all`](Self::observe_all) to begin collapsing the wave function.
561 pub fn restrict_edge<Con>(
562 &mut self,
563 position: &Pos,
564 pattern: &Pat,
565 constraint: &Con,
566 ) -> Result<(), WfcFailure>
567 where
568 Con: WfcConstrain<Pattern = Pat, Offset = Pos::Offset>,
569 Pos: Debug,
570 {
571 for offset in Pos::all_offsets() {
572 let other_pos = position.clone() + offset.clone();
573 let Some(other_cell) = self.wave.get_mut(&other_pos) else {
574 continue;
575 };
576 other_cell
577 .pattern_possibilities
578 .retain(|other_pattern, _counter| {
579 if !constraint.is_legal(pattern, &offset, other_pattern) {
580 self.pending
581 .push((other_pos.clone(), other_pattern.clone()));
582 false
583 } else {
584 true
585 }
586 });
587 if other_cell.pattern_possibilities.is_empty() {
588 return Err(WfcFailure);
589 }
590 }
591 Ok(())
592 }
593 fn set_cell<Con>(
594 &mut self,
595 position: &Pos,
596 pattern: &Pat,
597 constraint: &Con,
598 ) -> Result<(), WfcFailure>
599 where
600 Con: WfcConstrain<Pattern = Pat, Offset = Pos::Offset>,
601 {
602 let cell = self.wave.get_mut(position).expect("Missing wave cell");
603 let mut possibilities = std::mem::take(&mut cell.pattern_possibilities);
604 let (pattern, count) = possibilities
605 .remove_entry(pattern)
606 .expect("Missing pattern");
607 for (p, _) in possibilities.drain() {
608 self.after_restrict(position, &p, constraint)?;
609 }
610 possibilities.insert(pattern, count);
611 let cell = self.wave.get_mut(position).unwrap();
612 cell.pattern_possibilities = possibilities;
613 self.verify(position, constraint)
614 }
615 /// Completely collapse the wave function. This method repeatedly observes a random cell and
616 /// propagates each observation until all cells have been observed or the wave function collapse
617 /// fails due to not being able to find a valid pattern for some cell.
618 ///
619 /// If the number of cells is large, this method may be slow. Use [`observe_random_cell`](Self::observe_random_cell)
620 /// to progress the collapse one cell at a time and thereby have more control over the process
621 /// and allow the possibility of aborting.
622 pub fn observe_all<R, Con>(&mut self, rng: &mut R, constraint: &Con) -> Result<(), WfcFailure>
623 where
624 R: Rng + ?Sized,
625 Con: WfcConstrain<Pattern = Pat, Offset = Pos::Offset>,
626 Pos: Debug,
627 Pos::Counter: Debug,
628 {
629 while self.observe_random_cell(rng, constraint)? == WfcControlFlow::Continue {}
630 Ok(())
631 }
632 /// Observing a cell means choosing a random pattern for that cell from all the potential patterns
633 /// for that particular cell. Each cell keeps its own independent list of possible patterns,
634 /// and after a cell has been observed the possibilities for the surrounding cells may need to be
635 /// restricted. The restriction of the surrounding cells is called *propagation* and it should be
636 /// performed after each observation by calling [`propagate`](Self::propagate) or
637 /// [`propagate_until_finished`](Self::propagate_until_finished).
638 ///
639 /// If propagation is not complete when this method is called, then `propagate_until_finished` is
640 /// automatically called before the cell is observed.
641 ///
642 /// [`WfcControlFlow::Continue`] is returned if a cell was successfully observed, meaning that propagation
643 /// and more observations may be required to complete the collapse.
644 /// [`WfcControlFlow::Finish`] is returned if no cell could be observed because the pattern for all
645 /// cells has already been determined and the wave function collapse is complete.
646 pub fn observe_random_cell<R, Con>(
647 &mut self,
648 rng: &mut R,
649 constraint: &Con,
650 ) -> Result<WfcControlFlow, WfcFailure>
651 where
652 R: Rng + ?Sized,
653 Con: WfcConstrain<Pattern = Pat, Offset = Pos::Offset>,
654 Pos: Debug,
655 Pos::Counter: Debug,
656 {
657 self.propagate_until_finished(constraint)?;
658 let Some(position) = self.find_min_entropy(rng) else {
659 return Ok(WfcControlFlow::Finish);
660 };
661 let pattern = self
662 .choose_random_pattern(&position, rng, constraint)
663 .unwrap();
664 self.save_cell(position.clone());
665 for offset in Pos::all_offsets() {
666 let p = position.clone() + offset;
667 self.save_cell(p);
668 }
669 match self.set_cell(&position, &pattern, constraint) {
670 Ok(()) => {
671 self.clear_backtrack();
672 self.propagating.append(&mut self.pending);
673 }
674 Err(_) => {
675 self.backtrack();
676 self.pending.clear();
677 self.restrict(&position, &pattern, constraint)?;
678 self.propagating.append(&mut self.pending);
679 }
680 }
681 Ok(WfcControlFlow::Continue)
682 }
683 /// Repeatedly call [`propagate`](Self::propagate) until it returns [`WfcControlFlow::Finish`],
684 /// thus ensuring that all the cells are prepared for the next observation. This is called
685 /// automatically by [`observe_random_cell`](Self::observe_random_cell).
686 pub fn propagate_until_finished<Con>(&mut self, constraint: &Con) -> Result<(), WfcFailure>
687 where
688 Con: WfcConstrain<Pattern = Pat, Offset = Pos::Offset>,
689 {
690 while self.propagate(constraint)? == WfcControlFlow::Continue {}
691 Ok(())
692 }
693 /// Propagate the restrictions from the most recent calls to [`observe_random_cell`](Self::observe_random_cell)
694 /// or [`restrict_edge`](Self::restrict_edge) by one step, so that appropriate restrictions are spread across
695 /// the cells of the wave. This method should be repeatedly called until it returns [`WfcControlFlow::Finish`]
696 /// before another cell is observed so that the observation is based upon an accurate list of possible patterns
697 /// for the cell.
698 pub fn propagate<Con>(&mut self, constraint: &Con) -> Result<WfcControlFlow, WfcFailure>
699 where
700 Con: WfcConstrain<Pattern = Pat, Offset = Pos::Offset>,
701 {
702 let Some((position, pattern)) = self.propagating.pop() else {
703 return Ok(WfcControlFlow::Finish);
704 };
705 self.restrict(&position, &pattern, constraint)?;
706 self.propagating.append(&mut self.pending);
707 Ok(WfcControlFlow::Continue)
708 }
709 /// The given pattern is no longer valid at the given position. Update the surrouding cells
710 /// to reflect this restriction and if any of the surrounding cells need to be restricted,
711 /// add that restriction to the propagation list for future consideration.
712 /// Return false if the restriction was impossible due to leaving the cell with no remaining
713 /// possibilities.
714 fn restrict<Con>(
715 &mut self,
716 position: &Pos,
717 pattern: &Pat,
718 constraint: &Con,
719 ) -> Result<(), WfcFailure>
720 where
721 Con: WfcConstrain<Pattern = Pat, Offset = Pos::Offset>,
722 {
723 let Some(cell) = self.wave.get_mut(position) else {
724 return Ok(());
725 };
726 match cell.pattern_possibilities.entry(pattern.clone()) {
727 Entry::Occupied(entry) => drop(entry.remove()),
728 Entry::Vacant(_) => return Ok(()),
729 }
730 match cell.pattern_possibilities.len() {
731 0 => return Err(WfcFailure),
732 1 => self.verify(position, constraint)?,
733 _ => (),
734 }
735 let Some(cell) = self.wave.get_mut(position) else {
736 return Ok(());
737 };
738 if let Some(plogp) = self.limits.plogp_map.get(pattern) {
739 let p = constraint.probability_of(pattern) as Entropy;
740 cell.plogp_sum -= plogp;
741 cell.sum -= p;
742 cell.log_sum = cell.sum.ln();
743 cell.entropy = cell.log_sum - cell.plogp_sum / cell.sum;
744 self.after_restrict(position, pattern, constraint)
745 } else {
746 Ok(())
747 }
748 }
749 fn after_restrict<Con>(
750 &mut self,
751 position: &Pos,
752 pattern: &Pat,
753 constraint: &Con,
754 ) -> Result<(), WfcFailure>
755 where
756 Con: WfcConstrain<Pattern = Pat, Offset = Pos::Offset>,
757 Pos: Debug,
758 {
759 for offset in Pos::all_offsets() {
760 let other_pos = position.clone() + offset.clone();
761 let Some(other_cell) = self.wave.get_mut(&other_pos) else {
762 continue;
763 };
764 other_cell
765 .pattern_possibilities
766 .retain(|other_pattern, counter| {
767 if constraint.is_legal(pattern, &offset, other_pattern) {
768 let c = counter.count_mut(&offset);
769 *c -= 1;
770 if *c == 0 {
771 self.pending
772 .push((other_pos.clone(), other_pattern.clone()));
773 }
774 *c > 0
775 } else {
776 true
777 }
778 });
779 if other_cell.pattern_possibilities.is_empty() {
780 return Err(WfcFailure);
781 }
782 }
783 Ok(())
784 }
785 fn verify<Con>(&self, position: &Pos, constraint: &Con) -> Result<(), WfcFailure>
786 where
787 Con: WfcConstrain<Pattern = Pat, Offset = Pos::Offset>,
788 {
789 let Some(cell) = self.wave.get(position) else {
790 return Ok(());
791 };
792 let Some(pat) = cell.single_pattern() else {
793 return Ok(());
794 };
795 for offset in Pos::all_offsets() {
796 let other_pos = position.clone() + offset.clone();
797 let Some(other_cell) = self.wave.get(&other_pos) else {
798 continue;
799 };
800 if !other_cell
801 .pattern_possibilities
802 .keys()
803 .any(|p| constraint.is_legal(pat, &offset, p))
804 {
805 return Err(WfcFailure);
806 }
807 }
808 Ok(())
809 }
810}