1 /** 2 * This module defines the notion of a $(LATTICE). A $(LATTICE) is the 3 * data structure in wich all the $(I cells) are stored. Each generation, 4 * a $(RULE) changes the cells in the $(LATTICE). The most common example 5 * of a $(LATTICE) is a infinite square grid. Each square is the $(I cell) and it 6 * can contain any data such as a number or color. All of the $(I cells) then 7 * have a x,y coordinates associated with them. But some $(LATTICE)s can have 8 * other $(I dimensions), like a 3-dimensional grid of cubes where each cube have an 9 * x,y,z coordinate. Or it can have som other shape, like a grid with hexagons 10 * instead of squares. The only limitation is that the coordinates must be integers. 11 * 12 * This module provides templates for testing whether a given object is a $(LATTICE), 13 * and what kind of $(LATTICE) it is. 14 * 15 * $(CALIB_ABSTRACT_DESC) 16 * 17 * Macros: 18 * IS_ANY = 19 * Tests if something is a $(B $0). 20 * $(DDOC_BLANKLINE) 21 * returns $(DDOC_BACKQUOTED true) if $(DDOC_BACKQUOTED T) is a $(B $0). of 22 * any $(I dimension), Storing any type of cells. 23 * $(DDOC_BLANKLINE) 24 * This template is the same as $(B is$0) but the $(I dimension) and type 25 * of cells need not be specified. Refer to $(B is$0) for more details about 26 * what a $(B $0) is. 27 * $(DDOC_BLANKLINE) 28 * $(DDOC_PARAMS T type to be tested) 29 * $(DDOC_RETURNS $(B true) if $(B T) is any $(B $0), $(B false) if not) 30 */ 31 32 module caLib_abstract.lattice; 33 34 import std.meta : Repeat; 35 import caLib_abstract.util : hasCellStateType, hasDimension, hasNeighbourhoodType; 36 import caLib_abstract.neighbourhood : isNeighbourhood; 37 38 39 40 /** 41 * Tests if something is a $(B Lattice). 42 * 43 * returns `true` if `T` is a $(B Lattice) of $(I dimension) `N`, Storing cells of type `Ct`. 44 * 45 * A $(B Lattice) is the most basic form of a $(LATTICE). 46 * It must define the functions: $(BR) 47 * `Ct get(string)(Coord)`, `Ct get(Coord)`, $(BR) 48 * `void set(string)(Coord, Ct)`, `void set(Coord, Ct)`, $(BR) 49 * `Ct[] getNeighbours(string)(Coord)`, `Ct[] getNeighbours(Coord)`, $(BR) 50 * `void iterate(string)(ubyte delegate(Ct cellState, Ct[] neighbours, Coord c))`, 51 * `void iterate(ubyte delegate(Ct cellState, Ct[] neighbours, Coord c))`, $(BR) 52 * `void nextGen(string)()`, `void nextGen()`, $(BR) 53 * Where `Coord` is an alias for a typetuple containing `int`'s, one `int` for each 54 * $(I dimension) (in a 3 dimensional $(B Lattice) `Coord` would be: `(int, int, int)`). 55 * 56 * Params: 57 * T = type to be tested 58 * Ct = type of the cells the $(B Lattice) should contain 59 * N = number of dimensions 60 * 61 * Returns: true if T is a $(B Lattice), false if not 62 */ 63 template isLattice(T, Ct, uint N) 64 { 65 alias Coord = Repeat!(N, int); 66 67 static if(hasCellStateType!T && hasDimension!T && hasNeighbourhoodType!T) 68 { 69 enum isLattice = 70 T.Dimension == N && 71 is(T.CellStateType : Ct) && 72 isNeighbourhood!(T.NeighbourhoodType, N) && 73 is(typeof(T.init.get!"_test"(Coord.init)) : Ct) && 74 is(typeof(T.init.get(Coord.init)) : Ct) && 75 is(typeof(T.init.set!"_test"(Coord.init, Ct.init)) : void) && 76 is(typeof(T.init.set(Coord.init, Ct.init)) : void) && 77 is(typeof(T.init.getNeighbours!"_test"(Coord.init)) : Ct[]) && 78 is(typeof(T.init.getNeighbours(Coord.init)) : Ct[]) && 79 is(typeof(T.init.iterate!"_test"((Ct cellState, Ct[] neighbours, Coord c) 80 { return Ct.init; })) : void) && 81 is(typeof(T.init.iterate((Ct cellState, Ct[] neighbours, Coord c) 82 { return Ct.init; })) : void) && 83 is(typeof(T.init.nextGen!"_test"()) : void) && 84 is(typeof(T.init.nextGen()) : void); 85 } 86 else 87 { 88 enum isLattice = false; 89 } 90 91 } 92 93 unittest 94 { 95 static assert( isLattice!(Lattice!(int, 1), int, 1)); 96 static assert( isLattice!(Lattice!(int, 2), int, 2)); 97 static assert( isLattice!(Lattice!(int, 3), int, 3)); 98 99 static assert(!isLattice!(Lattice!(int, 3), uint, 2)); 100 static assert(!isLattice!(Lattice!(int, 1), uint, 1)); 101 } 102 103 104 105 ///$(IS_ANY Lattice) 106 template isAnyLattice(T) 107 { 108 static if(hasCellStateType!T && hasDimension!T) 109 enum isAnyLattice = isLattice!(T, T.CellStateType, T.Dimension); 110 else 111 enum isAnyLattice = false; 112 } 113 114 unittest 115 { 116 static assert( isAnyLattice!(Lattice!(char, 1))); 117 static assert( isAnyLattice!(BoundedLattice!(float, 2))); 118 static assert(!isAnyLattice!string); 119 } 120 121 122 123 /** 124 * Tests if something is a $(B BoundedLattice). 125 * 126 * returns `true` if `T` is a $(B BoundedLattice) of $(I dimension) `N`, Storing cells of type `Ct`. 127 * False if not. 128 * 129 * A $(B BoundedLattice) is a $(LATTICE) with a fixed size. It's function `getLatticeBounds` 130 * returns a list of `uint`'s representing the length of the $(I lattice's) $(I dimensions) 131 * 132 * A $(B BoundedLattice) is a $(B Lattice) with the additional function: `void getLatticeBounds()`. 133 * 134 * Params: 135 * T = type to be tested 136 * Ct = type of the cells the $(B BoundedLattice) should contain 137 * N = number of dimensions 138 * 139 * Returns: true if T is a $(B BoundedLattice), false if not 140 */ 141 template isBoundedLattice(T, Ct, uint N) 142 { 143 alias Coord = Repeat!(N, int); 144 145 enum isBoundedLattice = 146 isLattice!(T, Ct, N) && 147 is(typeof(T.init.getLatticeBounds()) : uint[N]); 148 } 149 150 unittest 151 { 152 static assert( isLattice!(BoundedLattice!(int, 1), int, 1)); 153 static assert( isLattice!(BoundedLattice!(int, 2), int, 2)); 154 static assert( isLattice!(BoundedLattice!(int, 3), int, 3)); 155 static assert( isBoundedLattice!(BoundedLattice!(int, 1), int, 1)); 156 static assert( isBoundedLattice!(BoundedLattice!(int, 2), int, 2)); 157 static assert( isBoundedLattice!(BoundedLattice!(int, 3), int, 3)); 158 159 static assert(!isBoundedLattice!(BoundedLattice!(int, 3), uint, 2)); 160 static assert(!isBoundedLattice!(BoundedLattice!(int, 1), uint, 1)); 161 } 162 163 164 165 ///$(IS_ANY BoundedLattice) 166 template isAnyBoundedLattice(T) 167 { 168 static if(hasCellStateType!T && hasDimension!T) 169 enum isAnyBoundedLattice = isBoundedLattice!(T, T.CellStateType, T.Dimension); 170 else 171 enum isAnyBoundedLattice = false; 172 } 173 174 unittest 175 { 176 static assert(!isAnyBoundedLattice!(Lattice!(char, 1))); 177 static assert( isAnyBoundedLattice!(BoundedLattice!(float, 2))); 178 static assert(!isAnyBoundedLattice!string); 179 } 180 181 182 183 version(unittest) 184 { 185 import caLib_abstract.neighbourhood : Neighbourhood; 186 187 struct Lattice(Ct, uint N, neighbourhood=Neighbourhood!N) 188 if(isNeighbourhood!(neighbourhood, N)) 189 { 190 alias CellStateType = Ct; 191 alias NeighbourhoodType = neighbourhood; 192 enum uint Dimension = N; 193 194 alias Coord = Repeat!(N, int); 195 196 Ct get(string s)(Coord) { return Ct.init; } 197 Ct get(Coord) { return Ct.init; } 198 199 void set(string s)(Coord, Ct) {} 200 void set(Coord, Ct) {} 201 202 Ct[] getNeighbours(string s)(Coord) { return new Ct[0]; } 203 Ct[] getNeighbours(Coord) { return new Ct[0]; } 204 205 void iterate(string s)(void delegate(Coord)) {} 206 void iterate(void delegate(Coord)) {} 207 208 void nextGen(string s)() {} 209 void nextGen() {} 210 } 211 212 struct BoundedLattice(Ct, uint N) 213 { 214 Lattice!(Ct, N) lattice; 215 alias lattice this; 216 217 uint[N] getLatticeBounds() { 218 // "return uint[N]"" makes compiler think 219 // "uint[N]" is a call to "opIndex" 220 uint[N] a; return a.init; 221 } 222 } 223 }