1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
//! A polymorphic world.

use crate::{
    cells::{Coord, State},
    config::Config,
    rules::{Life, LifeGen, NtLife, NtLifeGen},
    search::{Backjump, LifeSrc, Status},
    world::World,
};
use from_variants::FromVariants;

#[cfg(feature = "serde")]
use crate::{error::Error, save::WorldSer};

#[cfg(doc)]
use crate::cells::ALIVE;

/// A polymorphic [`World`].
#[non_exhaustive]
#[derive(FromVariants)]
pub enum PolyWorld {
    /// A [`World`] with [`Life`] rule and [`LifeSrc`] algorithm.
    Life(World<Life, LifeSrc>),
    /// A [`World`] with [`LifeGen`] rule and [`LifeSrc`] algorithm.
    LifeGen(World<LifeGen, LifeSrc>),
    /// A [`World`] with [`NtLife`] rule and [`LifeSrc`] algorithm.
    NtLife(World<NtLife, LifeSrc>),
    /// A [`World`] with [`NtLifeGen`] rule and [`LifeSrc`] algorithm.
    NtLifeGen(World<NtLifeGen, LifeSrc>),
    /// A [`World`] with [`Life`] rule and [`Backjump`] algorithm.
    LifeBackjump(World<Life, Backjump<Life>>),
    /// A [`World`] with [`NtLife`] rule and [`Backjump`] algorithm.
    NtLifeBackjump(World<NtLife, Backjump<NtLife>>),
}

macro_rules! dispatch {
    ($self: expr, $world: ident => $action: expr) => {
        match $self {
            PolyWorld::Life($world) => $action,
            PolyWorld::LifeGen($world) => $action,
            PolyWorld::NtLife($world) => $action,
            PolyWorld::NtLifeGen($world) => $action,
            PolyWorld::LifeBackjump($world) => $action,
            PolyWorld::NtLifeBackjump($world) => $action,
        }
    };
}

impl PolyWorld {
    /// The search function.
    ///
    /// Returns [`Status::Found`] if a result is found,
    /// [`Status::None`] if such pattern does not exist,
    /// [`Status::Searching`] if the number of steps exceeds `max_step`
    /// and no results are found.
    #[inline]
    pub fn search(&mut self, max_step: Option<u64>) -> Status {
        dispatch!(self, world => world.search(max_step))
    }

    /// Gets the state of a cell. Returns `Err(())` if there is no such cell.
    #[inline]
    pub fn get_cell_state(&self, coord: Coord) -> Option<State> {
        dispatch!(self, world => world.get_cell_state(coord))
    }

    /// World configuration.
    #[inline]
    pub const fn config(&self) -> &Config {
        dispatch!(self, world => world.config())
    }

    /// Whether the rule is a Generations rule.
    #[inline]
    pub const fn is_gen_rule(&self) -> bool {
        dispatch!(self, world => world.is_gen_rule())
    }

    /// Whether the rule contains `B0`.
    ///
    /// In other words, whether a cell would become [`ALIVE`] in the next
    /// generation, if all its neighbors in this generation are dead.
    #[inline]
    pub fn is_b0_rule(&self) -> bool {
        dispatch!(self, world => world.is_b0_rule())
    }

    /// Number of known living cells in some generation.
    ///
    /// For Generations rules, dying cells are not counted.
    #[inline]
    pub fn cell_count_gen(&self, t: i32) -> u32 {
        dispatch!(self, world => world.cell_count_gen(t))
    }

    /// Minimum number of known living cells in all generation.
    ///
    /// For Generations rules, dying cells are not counted.
    #[inline]
    pub fn cell_count(&self) -> u32 {
        dispatch!(self, world => world.cell_count())
    }

    /// Number of conflicts during the search.
    #[inline]
    pub const fn conflicts(&self) -> u64 {
        dispatch!(self, world => world.conflicts())
    }

    /// Set the max cell counts.
    ///
    /// Currently this is the only parameter that you can change
    /// during the search.
    #[inline]
    pub fn set_max_cell_count(&mut self, max_cell_count: Option<u32>) {
        dispatch!(self, world => world.set_max_cell_count(max_cell_count));
    }

    /// Displays the whole world in some generation,
    /// in a mix of [Plaintext](https://conwaylife.com/wiki/Plaintext) and
    /// [RLE](https://conwaylife.com/wiki/Rle) format.
    ///
    /// * **Dead** cells are represented by `.`;
    /// * **Living** cells are represented by `o` for rules with 2 states,
    ///   `A` for rules with more states;
    /// * **Dying** cells are represented by uppercase letters starting from `B`;
    /// * **Unknown** cells are represented by `?`;
    /// * Each line is ended with `$`;
    /// * The whole pattern is ended with `!`.
    #[inline]
    pub fn rle_gen(&self, t: i32) -> String {
        dispatch!(self, world => world.rle_gen(t))
    }

    /// Displays the whole world in some generation in
    /// [Plaintext](https://conwaylife.com/wiki/Plaintext) format.
    ///
    /// Do not use this for Generations rules.
    ///
    /// * **Dead** cells are represented by `.`;
    /// * **Living** and **Dying** cells are represented by `o`;
    /// * **Unknown** cells are represented by `?`.
    #[inline]
    pub fn plaintext_gen(&self, t: i32) -> String {
        dispatch!(self, world => world.plaintext_gen(t))
    }

    /// Saves the world as a [`WorldSer`],
    /// which can be easily serialized.
    #[cfg(feature = "serde")]
    #[cfg_attr(any(docs_rs, github_io), doc(cfg(feature = "serde")))]
    #[inline]
    pub fn ser(&self) -> WorldSer {
        dispatch!(self, world => world.ser())
    }

    /// Restores the world from the [`WorldSer`].
    #[cfg(feature = "serde")]
    #[cfg_attr(any(docs_rs, github_io), doc(cfg(feature = "serde")))]
    #[inline]
    pub fn deser(&mut self, ser: &WorldSer) -> Result<(), Error> {
        dispatch!(self, world => world.deser(ser))
    }
}