1 module caLib.rules.BinaryRule; 2 3 import std.math : pow; 4 import std.meta : Repeat; 5 import std.bigint; 6 import std.conv : to; 7 import std.array : appender; 8 import caLib_abstract.lattice : isAnyLattice; 9 import caLib_abstract.neighbourhood : isAnyStaticNeighbourhood; 10 11 12 13 auto create_BinaryRule(Lt)(Lt* lattice, BigInt ruleNumber) 14 { 15 return new BinaryRule!Lt(lattice, ruleNumber); 16 } 17 18 19 20 auto create_BinaryRule(Lt)(Lt* lattice, ubyte[] ruleSet) 21 in 22 { assert(BinaryRule!Lt.configurations == ruleSet.length); } 23 body 24 { 25 return new BinaryRule!Lt(lattice, ruleSet); 26 } 27 28 29 30 struct BinaryRule(Lt) 31 if(isAnyLattice!Lt && is(Lt.CellStateType : ubyte) 32 && isAnyStaticNeighbourhood!(Lt.NeighbourhoodType) && Lt.NeighbourhoodType.NeighboursAmount+1 < uint.max) 33 { 34 35 private: 36 37 Lt* lattice; 38 39 BigInt ruleNumber; 40 ubyte[] ruleSet; 41 42 alias Coord = Repeat!(Lt.Dimension, int); 43 44 public: 45 46 enum uint configurations = pow(2, Lt.NeighbourhoodType.NeighboursAmount+1); 47 BigInt maxRuleNumber; 48 49 this(Lt* lattice, BigInt ruleNumber) 50 in 51 { assert(ruleNumber <= calculateMaxRuleNumber, "rule number is to big"); } 52 body 53 { 54 this(lattice, createRuleSetFromNumber(ruleNumber)); 55 } 56 57 58 59 this(Lt* lattice, ubyte[] ruleSet) 60 in 61 { assert(BinaryRule!Lt.configurations == ruleSet.length); } 62 body 63 { 64 this.lattice = lattice; 65 66 this.ruleSet = ruleSet; 67 this.ruleNumber = createNumberFromRuleSet(ruleSet); 68 69 maxRuleNumber = calculateMaxRuleNumber(); 70 } 71 72 73 74 void applyRule() 75 { 76 lattice.iterate((ubyte cellState, ubyte[] neighbours, Coord c) 77 { 78 uint n = cellState << neighbours.length; 79 foreach(i; 0 .. neighbours.length) 80 { 81 n += neighbours[i] << i; 82 } 83 84 return ruleSet[n]; 85 }); 86 87 lattice.nextGen(); 88 } 89 90 91 92 BigInt getRuleNumber() { return ruleNumber; } 93 ubyte[] getRuleSet() { return ruleSet.dup; } 94 95 96 97 static BigInt calculateMaxRuleNumber() 98 { 99 BigInt n = BigInt("1"); 100 foreach(i; 0 .. configurations) { n *= 2; } 101 return n-1; 102 } 103 104 105 106 private: 107 108 static ubyte[] createRuleSetFromNumber(BigInt ruleNumber) 109 out(result) 110 { 111 assert(BinaryRule!Lt.configurations == result.length); 112 } 113 body 114 { 115 string binaryRuleString = toBinaryString(ruleNumber); 116 117 ubyte[] ruleSet = new ubyte[BinaryRule!Lt.configurations]; 118 foreach(i; 0 .. binaryRuleString.length) 119 { 120 ruleSet[i] = to!ubyte( 121 binaryRuleString[binaryRuleString.length-1-i] - '0'); 122 } 123 124 return ruleSet; 125 } 126 127 128 129 static BigInt createNumberFromRuleSet(const ubyte[] ruleSet) 130 in 131 { 132 assert(BinaryRule!Lt.configurations == ruleSet.length); 133 } 134 body 135 { 136 BigInt n = BigInt("0"); 137 foreach(i; 0 .. configurations) 138 { 139 if(ruleSet[i] == 1) 140 { 141 BigInt k = BigInt("1"); 142 foreach(j; 0 .. i) 143 { 144 k *= 2; 145 } 146 n += k; 147 } 148 } 149 return n; 150 } 151 152 153 154 static string toBinaryString(const BigInt num) 155 { 156 string hex = num.toHex(); 157 auto bin = appender!string(); 158 159 foreach(s; hex) 160 { 161 if(s != '_') 162 bin.put(hexToBinary(s)); 163 } 164 165 return bin.data; 166 } 167 168 169 170 static string hexToBinary(char hex) 171 { 172 string bin = 173 [ 174 '0':"0000", '1':"0001", '2':"0010", '3':"0011", '4':"0100", 175 '5':"0101", '6':"0110", '7':"0111", '8':"1000", '9':"1001", 176 'A':"1010", 'B':"1011", 'C':"1100", 'D':"1101", 'E':"1110", 177 'F':"1111" 178 ].get(hex,null); 179 180 assert(bin != null); 181 return bin; 182 } 183 } 184 185 186 187 version(unittest) 188 { 189 import caLib_abstract.rule : isRule; 190 import caLib_abstract.lattice : Lattice; 191 import caLib_abstract.neighbourhood : StaticNeighbourhood; 192 } 193 194 unittest 195 { 196 alias lattice = Lattice!(ubyte, 2, StaticNeighbourhood!2); 197 static assert(isRule!(BinaryRule!lattice)); 198 }