1 module caLib.simulations.HppLatticeGasSimulation;
2 
3 import std.algorithm.searching : countUntil;
4 import caLib.lattices.TwoDimDenseLattice;
5 import caLib.renderers.TwoDimBasicRenderer;
6 import caLib_util.structs : Simulation, create_Simulation;
7 import caLib_util.structs : Color;
8 
9 public import caLib_util.graphics : Window;
10 
11 import std.stdio;
12 
13 
14 
15 auto create_HppLatticeGasSimulation(int width, int height,
16 	ubyte delegate(int x, int y) initialCondition, Window window)
17 {
18 	auto lattice = create_TwoDimDenseLattice(width, height,
19 		new DummyNeighbourhood(), ubyte(0), initialCondition);
20 
21 	auto rule = new HppLatticeGasRule(lattice);
22 
23 	auto renderer = create_TwoDimBasicRenderer(lattice, new HppLatticeGasPalette(), window);
24 
25 	return create_Simulation(lattice, rule, renderer);
26 }
27 
28 
29 
30 struct HppLatticeGasRule
31 {
32 
33 private:
34 
35 	alias Lt = TwoDimDenseLattice!(ubyte, DummyNeighbourhood);
36 
37 	Lt* lattice;
38 	int latticeWidth;
39 	int latticeHeight;
40 
41 	static immutable ubyte[] ruleSet =
42 	(){
43 		ubyte[] ruleSet;
44 
45 		ruleSet.length = 16;
46 		foreach(ubyte i; 0 .. 16)
47 		{
48 			ruleSet[i] = i;
49 
50 			if(i == 3)
51 				ruleSet[i] = 12;
52 			if(i == 12)
53 				ruleSet[i] = 3;
54 		}
55 
56 		ruleSet.length = 32;
57 		foreach(ubyte i; 16 .. 32)
58 		{
59 			ruleSet[i] =
60 				(i << 1 & 2) +
61 				(i >> 1 & 1) +
62 				(i << 1 & 8) +
63 				(i >> 1 & 4) +
64 				16;
65 		}
66 
67 		return ruleSet;
68 	}();
69 
70 public:
71 
72 	this(Lt* lattice)
73 	{
74 		this.lattice = lattice;
75 		this.latticeWidth = lattice.getLatticeBounds[0];
76 		this.latticeHeight = lattice.getLatticeBounds[1];
77 	}
78 
79 	void applyRule()
80 	{
81 		foreach(y; 0 .. latticeHeight)
82 		{
83 			foreach(x; 0 .. latticeWidth)
84 			{
85 				ubyte afterTransportation =
86 					(lattice.get!"torus"(x+1, y) & 1) +
87 					(lattice.get!"torus"(x-1, y) & 2) +
88 					(lattice.get!"torus"(x, y+1) & 4) +
89 					(lattice.get!"torus"(x, y-1) & 8) +
90 					(lattice.get!"bounded-assumeInBounds"(x,y) & 16);
91 
92 				ubyte afterCollision = ruleSet[afterTransportation];
93 
94 				lattice.set!"bounded-assumeInBounds"(x, y, afterCollision);
95 			}
96 		}
97 		lattice.nextGen();
98 	}
99 
100 	void applyRuleReverse() {}
101 }
102 
103 
104 
105 private struct HppLatticeGasPalette
106 {
107 	alias CellStateType = ubyte;
108 	alias DisplayValueType = Color;
109 
110 	Color getDisplayValue(string behaviour)(ubyte cellState)
111 	{
112 		uint intensity = cast(ubyte)(
113 			(cellState >> 0 & 1) + (cellState >> 1 & 1) +
114 		    (cellState >> 2 & 1) + (cellState >> 3 & 1)) * 255/4;
115 		
116 		return Color(intensity + (intensity << 8) + (intensity << 16));
117 	}
118 
119 	Color getDisplayValue(ubyte cellState)
120 	{
121 		return getDisplayValue!""(cellState);
122 	}
123 }
124 
125 
126 
127 private struct DummyNeighbourhood
128 {
129 	static immutable int Dimension = 2;
130 	int[2][] getNeighboursCoordinates(int x, int y) { return []; }
131 }
132 
133 
134 
135 version(unittest)
136 {
137 	import caLib_abstract.neighbourhood : isNeighbourhood;
138 	import caLib_abstract.rule : isReversibleRule;
139 }
140 
141 
142 
143 unittest
144 {
145 	static assert(isNeighbourhood!(DummyNeighbourhood, 2));
146 	static assert(isReversibleRule!(HppLatticeGasRule));
147 }