1 /** 2 * This module defines the notion of a $(NEIGHBOURHOOD). A $(NEIGHBOURHOOD) 3 * defines what cells in the lattice that are "neighbours". Most ca $(RULE)s 4 * change the state of a $(I cell) depending on the $(I cells) $(I neighbours). 5 * 6 * It provides templates for testing whether a given object is a $(NEIGHBOURHOOD), 7 * and what kind of $(NEIGHBOURHOOD) it is. 8 * 9 * $(CALIB_ABSTRACT_DESC) 10 * 11 * Macros: 12 * IS_ANY = 13 * Tests if something is a $(B $0). 14 * $(DDOC_BLANKLINE) 15 * returns $(DDOC_BACKQUOTED true) if $(DDOC_BACKQUOTED T) is a $(B $0). of 16 * any dimension. 17 * $(DDOC_BLANKLINE) 18 * This template is the same as $(B is$0) but the dimension need not be 19 * specified. Refer to $(B is$0) for more details about what a $(B $0) is. 20 * $(DDOC_BLANKLINE) 21 * $(DDOC_PARAMS T type to be tested) 22 * $(DDOC_RETURNS $(B true) if $(B T) is any $(B $0), $(B false) if not) 23 */ 24 25 module caLib_abstract.neighbourhood; 26 27 import std.meta : Repeat; 28 import caLib_abstract.util : hasDimension, isDimensionsCompatible; 29 30 31 32 /** 33 * Tests if something is a $(B Neighbourhood). 34 * 35 * returns `true` if `T` is a $(B Neighbourhood) of dimension `N`. 36 * 37 * A $(B Neighbourhood) is the most basic form of a $(NEIGHBOURHOOD). 38 * It's `getNeighboursCoordinates` function takes a $(I cells) position 39 * and returns a list of coordinate pairs for it's neighbours positions. 40 * 41 * It must define the primitive: $(BR)`int[N][] getNeighboursCoordinates(Coord)`. $(BR) 42 * Where N is the dimension of the $(B Neighbourhood) and `Coord` is an 43 * alias for a typetuple containing `int`'s, one `int` for each 44 * dimension (in a 3 dimensional $(B Neighbourhood) `Coord` would be: `(int, int, int)`). 45 * 46 * Params: 47 * T = type to be tested 48 * N = number of dimensions 49 * 50 * Returns: true if T is a $(B Neighbourhood), false if not 51 */ 52 53 template isNeighbourhood(T, uint N) 54 { 55 alias Coord = Repeat!(N, int); 56 57 static if(hasDimension!T) 58 { 59 enum isNeighbourhood = 60 isDimensionsCompatible!(T.Dimension, N) && 61 is(typeof(T.init.getNeighboursCoordinates(Coord.init)) : int[N][]); 62 } 63 else 64 { 65 enum isNeighbourhood = false; 66 } 67 } 68 69 /// 70 unittest 71 { 72 struct A { 73 enum uint Dimension = 2; 74 int[2][] getNeighboursCoordinates(int x, int y) { 75 return [[x+1, y+1], [x+1, y], [x+1, y-1]]; 76 } 77 } 78 79 static assert( isNeighbourhood!(A, 2)); 80 static assert(!isNeighbourhood!(string, 1)); 81 } 82 83 unittest 84 { 85 static assert( isNeighbourhood!(Neighbourhood!(1), 1)); 86 static assert( isNeighbourhood!(Neighbourhood!(2), 2)); 87 static assert( isNeighbourhood!(Neighbourhood!(3), 3)); 88 89 static assert(!isNeighbourhood!(Neighbourhood!(3), 2)); 90 } 91 92 93 94 ///$(IS_ANY Neighbourhood) 95 template isAnyNeighbourhood(T) 96 { 97 static if(hasDimension!T) 98 enum isAnyNeighbourhood = isNeighbourhood!(T, T.Dimension); 99 else 100 enum isAnyNeighbourhood = false; 101 } 102 103 /// 104 unittest 105 { 106 struct Foo { 107 enum uint Dimension = 2; 108 109 int[Dimension][] getNeighboursCoordinates(int x, int y) { 110 return [[x+1, y+1], [x+1, y], [x+1, y-1]]; 111 } 112 } 113 114 static assert(! isNeighbourhood!(Foo, 3)); 115 static assert( isAnyNeighbourhood!(Foo )); 116 } 117 118 unittest 119 { 120 static assert( isAnyNeighbourhood!(Neighbourhood!(1))); 121 static assert( isAnyNeighbourhood!(StaticNeighbourhood!(2))); 122 static assert( isAnyNeighbourhood!(ShiftingNeighbourhood!(3))); 123 static assert(!isAnyNeighbourhood!string); 124 } 125 126 127 128 /** 129 * Tests if something is a $(B StaticNeighbourhood). 130 * 131 * returns `true` if `T` is a $(B StaticNeighbourhood) of dimension `N`. 132 * 133 * A $(B StaticNeighbourhood) is a $(NEIGHBOURHOOD) that never changes. 134 * If a particular $(I cell) has a particular $(I neighbour) it will always have 135 * that $(I neighbour). Also, if a $(I cell) where to have a neighbour "to the right" 136 * all other $(I cells) will also have a neighbour "to the right". 137 * A $(B StaticNeighbourhood) dosen't really add any additional functionality. 138 * All it does is having the a uint enum NeighboursAmount. 139 * 140 * Params: 141 * T = type to be tested 142 * N = number of dimensions 143 * 144 * Returns: true if T is a $(B StaticNeighbourhood), false if not 145 */ 146 template isStaticNeighbourhood(T, uint N) 147 { 148 alias Coord = Repeat!(N, int); 149 150 enum isStaticNeighbourhood = 151 isNeighbourhood!(T, N) && 152 is(typeof(T.NeighboursAmount) : uint) && 153 is(typeof(T.init.NeighboursAmount) : uint); 154 } 155 156 /// 157 unittest 158 { 159 struct A { 160 enum uint Dimension = 2; 161 enum uint NeighboursAmount = 1; 162 enum isStatic; 163 int[2][] getNeighboursCoordinates(int x, int y) { 164 return [[x+1, y+1]]; 165 } 166 } 167 168 static assert( isNeighbourhood!(A, 2)); 169 static assert( isStaticNeighbourhood!(A, 2)); 170 static assert(!isStaticNeighbourhood!(string, 1)); 171 } 172 173 unittest 174 { 175 static assert( isStaticNeighbourhood!(StaticNeighbourhood!(1), 1)); 176 static assert( isStaticNeighbourhood!(StaticNeighbourhood!(2), 2)); 177 static assert( isStaticNeighbourhood!(StaticNeighbourhood!(3), 3)); 178 179 static assert(!isStaticNeighbourhood!(StaticNeighbourhood!(3), 2)); 180 static assert(!isStaticNeighbourhood!(Neighbourhood!(1), 1)); 181 } 182 183 184 185 ///$(IS_ANY StaticNeighbourhood) 186 template isAnyStaticNeighbourhood(T) 187 { 188 static if(hasDimension!T) 189 enum isAnyStaticNeighbourhood = isStaticNeighbourhood!(T, T.Dimension); 190 else 191 enum isAnyStaticNeighbourhood = false; 192 } 193 194 /// 195 unittest 196 { 197 struct Foo { 198 enum uint Dimension = 2; 199 enum uint NeighboursAmount = 1; 200 int[2][] getNeighboursCoordinates(int x, int y) { 201 return [[x+1, y+1]]; 202 } 203 } 204 static assert(! isStaticNeighbourhood!(Foo, 3)); 205 static assert( isAnyStaticNeighbourhood!(Foo )); 206 } 207 208 unittest 209 { 210 static assert(!isAnyStaticNeighbourhood!(Neighbourhood!(1))); 211 static assert( isAnyStaticNeighbourhood!(StaticNeighbourhood!(2))); 212 static assert(!isAnyStaticNeighbourhood!(ShiftingNeighbourhood!(3))); 213 static assert(!isAnyStaticNeighbourhood!string); 214 } 215 216 217 218 /** 219 * Tests if something is a $(B ShiftingNeighbourhood). 220 * 221 * returns `true` if `T` is a $(B ShiftingNeighbourhood) of dimension `N`. 222 * 223 * A $(B ShiftingNeighbourhood) is a $(NEIGHBOURHOOD) that can change each 224 * generation. It's `shift` function is meant to be called by the $(I ca)'s 225 * $(LATTICE) every time a generation changes. 226 * 227 * A $(B ShiftingNeighbourhood) is a $(B neighbourhood) with the additional 228 * primitive `void shift()` 229 * 230 * Params: 231 * T = type to be tested 232 * N = number of dimensions 233 * 234 * Returns: true if T is a $(B ShiftingNeighbourhood), false if not 235 */ 236 template isShiftingNeighbourhood(T, uint N) 237 { 238 enum isShiftingNeighbourhood = 239 isNeighbourhood!(T, N) && 240 is(typeof(T.init.shift()) : void); 241 } 242 243 /// 244 unittest 245 { 246 struct Foo { 247 enum uint Dimension = 2; 248 249 private bool state = false; 250 251 int[2][] getNeighboursCoordinates(int x, int y) { 252 if(state) { 253 return [[x+1, y+1]]; 254 } else { 255 return [[x-1, y-1]]; 256 } 257 } 258 259 void shift() { state = !state; } 260 } 261 262 static assert( isNeighbourhood!(Foo, 2)); 263 static assert( isShiftingNeighbourhood!(Foo, 2)); 264 static assert(!isShiftingNeighbourhood!(string, 1)); 265 } 266 267 unittest 268 { 269 static assert( isShiftingNeighbourhood!(ShiftingNeighbourhood!(1), 1)); 270 static assert( isShiftingNeighbourhood!(ShiftingNeighbourhood!(2), 2)); 271 static assert( isShiftingNeighbourhood!(ShiftingNeighbourhood!(3), 3)); 272 273 static assert(!isShiftingNeighbourhood!(ShiftingNeighbourhood!(3), 2)); 274 static assert(!isShiftingNeighbourhood!(Neighbourhood!(1), 1)); 275 } 276 277 278 279 ///$(IS_ANY ShiftingNeighbourhood) 280 template isAnyShiftingNeighbourhood(T) 281 { 282 static if(hasDimension!T) 283 enum isAnyShiftingNeighbourhood = isShiftingNeighbourhood!(T, T.Dimension); 284 else 285 enum isAnyShiftingNeighbourhood = false; 286 } 287 288 /// 289 unittest 290 { 291 struct Foo { 292 enum uint Dimension = 2; 293 294 private bool state = false; 295 296 int[2][] getNeighboursCoordinates(int x, int y) { 297 if(state) { 298 return [[x+1, y+1]]; 299 } else { 300 return [[x-1, y-1]]; 301 } 302 } 303 304 void shift() { state = !state; } 305 } 306 307 static assert(! isShiftingNeighbourhood!(Foo, 3)); 308 static assert( isAnyShiftingNeighbourhood!(Foo )); 309 } 310 311 unittest 312 { 313 static assert( isAnyShiftingNeighbourhood!(ShiftingNeighbourhood!(1))); 314 static assert( isAnyShiftingNeighbourhood!(ShiftingNeighbourhood!(2))); 315 static assert( isAnyShiftingNeighbourhood!(ShiftingNeighbourhood!(3))); 316 static assert(!isAnyShiftingNeighbourhood!(Neighbourhood!(1))); 317 static assert(!isAnyShiftingNeighbourhood!string); 318 } 319 320 321 322 /+ 323 + This template is undocumented since it is unclear wheater it should exist or not 324 +/ 325 template isBlockNeighbourhood(T, uint N) 326 { 327 alias Coord = Repeat!(N, int); 328 329 enum isBlockNeighbourhood = 330 isNeighbourhood!(T, N) && 331 is(typeof(T.init.getBlockCoordinates(Coord.init)) : int[N][]); 332 } 333 334 unittest 335 { 336 static assert( isBlockNeighbourhood!(BlockNeighbourhood!(1), 1)); 337 static assert( isBlockNeighbourhood!(BlockNeighbourhood!(2), 2)); 338 static assert( isBlockNeighbourhood!(BlockNeighbourhood!(3), 3)); 339 340 static assert(!isBlockNeighbourhood!(BlockNeighbourhood!(3), 2)); 341 } 342 343 344 345 version(unittest) 346 { 347 struct Neighbourhood(uint N) 348 { 349 enum uint Dimension = N; 350 351 alias Coord = Repeat!(N, int); 352 353 int[N][] getNeighboursCoordinates(Coord) { int[N][]a; return a.init; } 354 } 355 356 struct StaticNeighbourhood(uint N) 357 { 358 Neighbourhood!N neighbourhood; 359 alias neighbourhood this; 360 361 enum uint NeighboursAmount = 0; 362 } 363 364 struct ShiftingNeighbourhood(uint N) 365 { 366 Neighbourhood!N neighbourhood; 367 alias neighbourhood this; 368 369 void shift() {} 370 } 371 372 struct BlockNeighbourhood(uint N) 373 { 374 alias Coord = Repeat!(N, int); 375 376 Neighbourhood!N neighbourhood; 377 alias neighbourhood this; 378 379 int[N][] getBlockCoordinates(Coord) { int[N][]a; return a.init; } 380 } 381 }