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