| 1 |
module cellular; |
|---|
| 2 |
import qd, std.stdio, std.string, tools.base; |
|---|
| 3 |
|
|---|
| 4 |
const real K=1024, M=1024*K, G=1024*M, T=1024*G; |
|---|
| 5 |
|
|---|
| 6 |
string fc(real what, string label) { |
|---|
| 7 |
if (what>T) return format(1.0*what/T, "Tc", label); |
|---|
| 8 |
if (what>G) return format(1.0*what/G, "Gc", label); |
|---|
| 9 |
if (what>M) return format(1.0*what/M, "Mc", label); |
|---|
| 10 |
if (what>K) return format(1.0*what/K, "Kc", label); |
|---|
| 11 |
return format(what, "c", label); |
|---|
| 12 |
} |
|---|
| 13 |
|
|---|
| 14 |
import tools.array2d; |
|---|
| 15 |
|
|---|
| 16 |
void swap(T)(ref T a, ref T b) { T x=a; a=b; b=x; } |
|---|
| 17 |
|
|---|
| 18 |
size_t newline(string s) { size_t count; while (count<s.length && s[count]!='\n') ++count; return count; } |
|---|
| 19 |
|
|---|
| 20 |
/** |
|---|
| 21 |
* transform a list of rules (CTRBLI, sorted) into a tree of if statements |
|---|
| 22 |
* AT COMPILE TIME ... DUN DUN DUN |
|---|
| 23 |
* NOTE: |
|---|
| 24 |
* gdc already optimizes the if tree. |
|---|
| 25 |
* no need to calculate the symmetries manually. |
|---|
| 26 |
**/ |
|---|
| 27 |
string rules_transform(string _file) { |
|---|
| 28 |
string res; |
|---|
| 29 |
for (int symmetry=0; symmetry<4; ++symmetry) { |
|---|
| 30 |
string sup, sright, sdown, sleft; |
|---|
| 31 |
switch (symmetry) { |
|---|
| 32 |
case 0: sup="up"; sright="right"; sdown="down"; sleft="left"; break; |
|---|
| 33 |
case 1: sup="right"; sright="down"; sdown="left"; sleft="up"; break; |
|---|
| 34 |
case 2: sup="down"; sright="left"; sdown="up"; sleft="right"; break; |
|---|
| 35 |
case 3: sup="left"; sright="up"; sdown="right"; sleft="down"; break; |
|---|
| 36 |
default: assert(false); |
|---|
| 37 |
} |
|---|
| 38 |
auto file=_file; |
|---|
| 39 |
int cur_me=-1, cur_up, cur_right, cur_down, cur_left; |
|---|
| 40 |
while (file.length) { |
|---|
| 41 |
auto line=file[0..newline(file)]; |
|---|
| 42 |
file=file[newline(file)+1..$]; |
|---|
| 43 |
if (line[0]=='#') continue; |
|---|
| 44 |
auto me=line[0]-'0', up=line[1]-'0', right=line[2]-'0', |
|---|
| 45 |
down=line[3]-'0', left=line[4]-'0', to=line[5]-'0'; |
|---|
| 46 |
if (cur_me!=me) { |
|---|
| 47 |
if (cur_me!=-1) res~="} } } } }"; |
|---|
| 48 |
cur_me=me; cur_up=up; cur_right=right; cur_down=down; cur_left=left; |
|---|
| 49 |
res~="if (me=="~ctToString(me)~") { |
|---|
| 50 |
if ("~sup~"=="~ctToString(up)~") { |
|---|
| 51 |
if ("~sright~"=="~ctToString(right)~") { |
|---|
| 52 |
if ("~sdown~"=="~ctToString(down)~") { |
|---|
| 53 |
if ("~sleft~"=="~ctToString(left)~") { |
|---|
| 54 |
return "~ctToString(to)~"; "; |
|---|
| 55 |
} else { |
|---|
| 56 |
if (cur_up!=up) { |
|---|
| 57 |
cur_up=up; cur_right=right; cur_down=down; cur_left=left; |
|---|
| 58 |
res~="} } } } if ("~sup~"=="~ctToString(up)~") { |
|---|
| 59 |
if ("~sright~"=="~ctToString(right)~") { |
|---|
| 60 |
if ("~sdown~"=="~ctToString(down)~") { |
|---|
| 61 |
if ("~sleft~"=="~ctToString(left)~") { |
|---|
| 62 |
return "~ctToString(to)~"; "; |
|---|
| 63 |
} else { |
|---|
| 64 |
if (cur_right!=right) { |
|---|
| 65 |
cur_right=right; cur_down=down; cur_left=left; |
|---|
| 66 |
res~="} } } if ("~sright~"=="~ctToString(right)~") { |
|---|
| 67 |
if ("~sdown~"=="~ctToString(down)~") { |
|---|
| 68 |
if ("~sleft~"=="~ctToString(left)~") { |
|---|
| 69 |
return "~ctToString(to)~"; "; |
|---|
| 70 |
} else { |
|---|
| 71 |
if (cur_down!=down) { |
|---|
| 72 |
cur_down=down; cur_left=left; |
|---|
| 73 |
res~="} } if ("~sdown~"=="~ctToString(down)~") { |
|---|
| 74 |
if ("~sleft~"=="~ctToString(left)~") { |
|---|
| 75 |
return "~ctToString(to)~"; "; |
|---|
| 76 |
} else { |
|---|
| 77 |
if (cur_left!=left) { |
|---|
| 78 |
cur_left=left; |
|---|
| 79 |
res~="} if ("~sleft~"=="~ctToString(left)~") { |
|---|
| 80 |
return "~ctToString(to)~"; "; |
|---|
| 81 |
} else assert(false, "Duplicate state"); |
|---|
| 82 |
} |
|---|
| 83 |
} |
|---|
| 84 |
} |
|---|
| 85 |
} |
|---|
| 86 |
} |
|---|
| 87 |
res~="} } } } }"; |
|---|
| 88 |
} |
|---|
| 89 |
return res; |
|---|
| 90 |
} |
|---|
| 91 |
|
|---|
| 92 |
import living_space; |
|---|
| 93 |
final class ChouReggia : Space!(Neumann) { |
|---|
| 94 |
this(int b, int o, bool w, int[][] s=null) { super(b, o, w, s); } |
|---|
| 95 |
final ubyte eval(ubyte me, ubyte up, ubyte right, ubyte down, ubyte left) { |
|---|
| 96 |
mixin(rules_transform(import("ChouReggia.r"))); |
|---|
| 97 |
return me; |
|---|
| 98 |
} |
|---|
| 99 |
} |
|---|
| 100 |
|
|---|
| 101 |
final class Byl : Space!(Neumann) { |
|---|
| 102 |
this(int b, int o, bool w, int[][] s=null) { super(b, o, w, s); } |
|---|
| 103 |
final ubyte eval(ubyte me, ubyte up, ubyte right, ubyte down, ubyte left) { |
|---|
| 104 |
mixin(rules_transform(import("byl.r"))); |
|---|
| 105 |
return me; |
|---|
| 106 |
} |
|---|
| 107 |
} |
|---|
| 108 |
|
|---|
| 109 |
final class Evoloop : Space!(Neumann) { |
|---|
| 110 |
this(int b, int o, bool w, int[][] s=null) { super(b, o, w, s); } |
|---|
| 111 |
final ubyte eval(ubyte me, ubyte up, ubyte right, ubyte down, ubyte left) { |
|---|
| 112 |
bool see(ubyte which) { |
|---|
| 113 |
return (up == which || right == which || down == which || left == which); |
|---|
| 114 |
} |
|---|
| 115 |
mixin(rules_transform(import("evoloop.r"))); |
|---|
| 116 |
if (me==8) return 0; |
|---|
| 117 |
if (see(8)) { |
|---|
| 118 |
if ((me==0) || (me==1)) { |
|---|
| 119 |
if (see(2) || see(3) || see(4) || see(5) || see(6) || see(7)) return 8; |
|---|
| 120 |
else return me; |
|---|
| 121 |
} |
|---|
| 122 |
if ((me==2) || (me==3) || (me==5)) return 0; |
|---|
| 123 |
if ((me==4) || (me==6) || (me==7)) return 1; |
|---|
| 124 |
} |
|---|
| 125 |
if (!me) return 0; |
|---|
| 126 |
return 8; |
|---|
| 127 |
} |
|---|
| 128 |
} |
|---|
| 129 |
|
|---|
| 130 |
ubyte urand(ubyte and=255) { return cast(ubyte)(rand()&and); } |
|---|
| 131 |
|
|---|
| 132 |
import std.random; |
|---|
| 133 |
final class Life : Space!(Moore) { |
|---|
| 134 |
bool[] sustain, births; |
|---|
| 135 |
this(int b, int o, bool w, int[] sust, int[] birth, int[][] s=null) { |
|---|
| 136 |
sustain=new bool[9]; foreach (entry; sust) sustain[entry]=true; |
|---|
| 137 |
births=new bool[9]; foreach (entry; birth) births[entry]=true; |
|---|
| 138 |
super(b, o, w, s); |
|---|
| 139 |
cols=[Black, White, Black~White~Black, White~Black~White]; |
|---|
| 140 |
if (!s) foreach (ref cell; state) cell=urand(1); |
|---|
| 141 |
} |
|---|
| 142 |
final ubyte eval(ubyte a, ubyte b, ubyte c, ubyte d, ubyte e, ubyte f, ubyte g, ubyte h, ubyte i) { |
|---|
| 143 |
int sum=(a&1)+(b&1)+(c&1) |
|---|
| 144 |
+(d&1) +(f&1) |
|---|
| 145 |
+(g&1)+(h&1)+(i&1); |
|---|
| 146 |
if (e&1) { |
|---|
| 147 |
if (sustain[sum]) return 3; |
|---|
| 148 |
return 2; |
|---|
| 149 |
} else { |
|---|
| 150 |
if (births[sum]) return 1; // Any dead cell with exactly three live neighbours comes to life. |
|---|
| 151 |
return 0; |
|---|
| 152 |
} |
|---|
| 153 |
} |
|---|
| 154 |
} |
|---|
| 155 |
|
|---|
| 156 |
template rule(int a, int b, int c, int d, int e, string WHUT) { |
|---|
| 157 |
const string rule=Replace!("if (me==$a) { |
|---|
| 158 |
if ( |
|---|
| 159 |
(($b==-1)?true:( up ==$b)) && (($c==-1)?true:( left ==$c)) && (($d==-1)?true:( down ==$d)) && (($e==-1)?true:( right ==$e)) || |
|---|
| 160 |
(($b==-1)?true:( left ==$b)) && (($c==-1)?true:( down ==$c)) && (($d==-1)?true:( right ==$d)) && (($e==-1)?true:( up ==$e)) || |
|---|
| 161 |
(($b==-1)?true:( down ==$b)) && (($c==-1)?true:( right ==$c)) && (($d==-1)?true:( up ==$d)) && (($e==-1)?true:( left ==$e)) || |
|---|
| 162 |
(($b==-1)?true:( right ==$b)) && (($c==-1)?true:( up ==$c)) && (($d==-1)?true:( left ==$d)) && (($e==-1)?true:( down ==$e)) |
|---|
| 163 |
) $W; |
|---|
| 164 |
}", "$a", ctToString(a), "$b", ctToString(b), "$c", ctToString(c), "$d", ctToString(d), "$e", ctToString(e), "$W", WHUT); |
|---|
| 165 |
} |
|---|
| 166 |
|
|---|
| 167 |
const string WireEval=" |
|---|
| 168 |
final ubyte eval(ubyte a, ubyte b, ubyte c, ubyte d, ubyte e, ubyte f, ubyte g, ubyte h, ubyte i) { |
|---|
| 169 |
switch (e) { |
|---|
| 170 |
case 0: return 0; break; |
|---|
| 171 |
case 2: return 3; break; |
|---|
| 172 |
case 3: return 1; break; |
|---|
| 173 |
case 1: |
|---|
| 174 |
ubyte heads; |
|---|
| 175 |
if (a==2) ++heads; if (b==2) ++heads; if (c==2) ++heads; |
|---|
| 176 |
if (d==2) ++heads; if (f==2) ++heads; |
|---|
| 177 |
if (g==2) ++heads; if (h==2) ++heads; if (i==2) ++heads; |
|---|
| 178 |
if ((heads==1) || (heads==2)) return 2; |
|---|
| 179 |
return 1; |
|---|
| 180 |
break; |
|---|
| 181 |
} |
|---|
| 182 |
}"; |
|---|
| 183 |
final class Wireworld : Space!(Moore, WireEval) { |
|---|
| 184 |
this(int b, int o, int[][] s=null) { super(b, o, true, s); cols=[Black, Yellow, Blue~White~Blue, Red~White~Red]; } |
|---|
| 185 |
} |
|---|
| 186 |
|
|---|
| 187 |
import std.c.time, tools.functional; |
|---|
| 188 |
static import std.file; |
|---|
| 189 |
|
|---|
| 190 |
void main() { |
|---|
| 191 |
screen(640, 960); |
|---|
| 192 |
flip=false; |
|---|
| 193 |
// cannot reproduce sexyloop |
|---|
| 194 |
// ask authors? |
|---|
| 195 |
auto g = new Evoloop(1, 0, true, (string text) { |
|---|
| 196 |
auto lines = text.split("\n"); |
|---|
| 197 |
auto res = new int[][lines.length]; |
|---|
| 198 |
int maxlen; |
|---|
| 199 |
foreach (line; lines) if (line.length > maxlen) maxlen = line.length; |
|---|
| 200 |
foreach (i, line; lines) { |
|---|
| 201 |
res[i] = new int[maxlen]; |
|---|
| 202 |
foreach (k, ch; line) |
|---|
| 203 |
if (ch == ' ') res[i][k] = 0; |
|---|
| 204 |
else res[i][k] = ch - '0'; |
|---|
| 205 |
} |
|---|
| 206 |
return res; |
|---|
| 207 |
}(" |
|---|
| 208 |
222222222222222 |
|---|
| 209 |
27017017017011115 |
|---|
| 210 |
21222222222222212 |
|---|
| 211 |
202 212 |
|---|
| 212 |
272 212 |
|---|
| 213 |
212 212 |
|---|
| 214 |
202 212 |
|---|
| 215 |
272 212 |
|---|
| 216 |
212 212 |
|---|
| 217 |
202 212 |
|---|
| 218 |
272 272 |
|---|
| 219 |
212 202 22222 |
|---|
| 220 |
202 212 2111112 |
|---|
| 221 |
272 272 2122212 |
|---|
| 222 |
21222222222222202 212 212 |
|---|
| 223 |
20710710710410412 212 272 |
|---|
| 224 |
222222222222222 2122202 |
|---|
| 225 |
2111112 |
|---|
| 226 |
22222 |
|---|
| 227 |
")); |
|---|
| 228 |
/*auto g=new Life(2, 0, [2, 3], [3], [ |
|---|
| 229 |
[1,1,1,0,1], |
|---|
| 230 |
[1,0,0,0,0], |
|---|
| 231 |
[0,0,0,1,1], |
|---|
| 232 |
[0,1,1,0,1], |
|---|
| 233 |
[1,0,1,0,1] |
|---|
| 234 |
]);*/ |
|---|
| 235 |
/*auto g=new Life(4, 0, true, [2, 3], [3], [ |
|---|
| 236 |
" x ", |
|---|
| 237 |
" x x ", |
|---|
| 238 |
" xx xx xx", |
|---|
| 239 |
" x x xx x ", |
|---|
| 240 |
"xx x x xx ", |
|---|
| 241 |
"xx x x xx x x ", |
|---|
| 242 |
" x x x ", |
|---|
| 243 |
" x x ", |
|---|
| 244 |
" xx " |
|---|
| 245 |
] /map/ (string s) { return s /map/ ex!("f -> (f==' ')?0:1"); });*/ |
|---|
| 246 |
/*auto g=new Life(2, 0, true, [2, 3], [3], [ |
|---|
| 247 |
" x ", |
|---|
| 248 |
" x", |
|---|
| 249 |
" xxx" |
|---|
| 250 |
] /Map!("return $ /Map!(\"return ($==' ')?0:1\")"));*/ |
|---|
| 251 |
//auto g=new Life(2, 0, [2, 5], [3, 4]); |
|---|
| 252 |
/*auto g=new Wireworld(4, 1, " |
|---|
| 253 |
->oooooooooooooooo oooooooo |
|---|
| 254 |
o ooooooo oooooooo |
|---|
| 255 |
ooooooooooooooo o o o |
|---|
| 256 |
o o o oo o oooooooo |
|---|
| 257 |
ooooooooooooooo o o o o o oooooooo |
|---|
| 258 |
o o o o ooo |
|---|
| 259 |
ooooooooooooooooo ooo o o |
|---|
| 260 |
o ooo |
|---|
| 261 |
o oooooooooooo |
|---|
| 262 |
o o |
|---|
| 263 |
ooo |
|---|
| 264 |
o |
|---|
| 265 |
o |
|---|
| 266 |
->ooooo o |
|---|
| 267 |
o ooooooooooooooooo |
|---|
| 268 |
ooooooo |
|---|
| 269 |
".split("\n") /Map!(" return $ /Map!(\"if (($=='>') || ($=='<')) return 2; if ($=='-') return 3; if ($=='o') return 1; return 0\")"));*/ |
|---|
| 270 |
/*auto wf=cast(char[])std.file.read("primes.wi"); |
|---|
| 271 |
//auto wf=cast(char[])std.file.read("test.wi"); |
|---|
| 272 |
screen(640, 960); |
|---|
| 273 |
auto g=new Wireworld(1, 0, wf.split("\n") /map/ (string s) { |
|---|
| 274 |
return s /map/ (char c) { |
|---|
| 275 |
if (c=='~') return 3; else |
|---|
| 276 |
if (c=='@') return 2; else |
|---|
| 277 |
if (c=='#') return 1; else |
|---|
| 278 |
return 0; |
|---|
| 279 |
}; |
|---|
| 280 |
});*/ |
|---|
| 281 |
//auto g=new Byl(1, 0, true, [[0, 2, 2], [2, 3, 1, 2], [2, 3, 4, 2], [0, 2, 5]]); |
|---|
| 282 |
//auto g=new Test(4, 1, true, [[1]]); |
|---|
| 283 |
//auto g=new Life(5, 0, true, [0], [2], [[1, 1], [1, 1]]); |
|---|
| 284 |
real sum=0; long secs, isum; |
|---|
| 285 |
auto now=time(null); long ips; |
|---|
| 286 |
bool fastmode=false, step=false, returnPressed=false; |
|---|
| 287 |
int stepsize=1; |
|---|
| 288 |
int delay=1; |
|---|
| 289 |
bool mouseBtn[5]; |
|---|
| 290 |
|
|---|
| 291 |
void changeStepsize(int n) { auto on=stepsize; stepsize=n; writefln("Stepsize: ", on, " -> ", n); } |
|---|
| 292 |
void changeDelay(int n) { auto on=delay; delay=n; writefln("Delay: ", on, " -> ", n); } |
|---|
| 293 |
void handleEvents() { |
|---|
| 294 |
events((ushort key, bool pressed) { |
|---|
| 295 |
if (!pressed) return; |
|---|
| 296 |
switch (key) { |
|---|
| 297 |
case 13: step=true; fastmode=false; break; |
|---|
| 298 |
case ' ': fastmode=!fastmode; break; |
|---|
| 299 |
case 93, 270: /*fastmode=true;*/ if (delay>1) changeDelay(delay/2); else changeStepsize(stepsize*2); break; |
|---|
| 300 |
case 47, 269: /*fastmode=true;*/ if (stepsize>1) changeStepsize(stepsize/2); else changeDelay(delay*2); break; |
|---|
| 301 |
default: break; |
|---|
| 302 |
} |
|---|
| 303 |
}, (int x, int y, ubyte button, int pressed) { |
|---|
| 304 |
if (pressed==1) mouseBtn[button-1]=true; |
|---|
| 305 |
if (pressed==-1) mouseBtn[button-1]=false; |
|---|
| 306 |
if (mouseBtn[0]) { |
|---|
| 307 |
x/=g.boxsize; y/=g.boxsize; |
|---|
| 308 |
logln("State: ", g.state[x, y]); |
|---|
| 309 |
} |
|---|
| 310 |
}); |
|---|
| 311 |
} |
|---|
| 312 |
ulong cps, iterations; bool worked; |
|---|
| 313 |
while (true) { |
|---|
| 314 |
if(now != time(null)) { |
|---|
| 315 |
sum += cps; |
|---|
| 316 |
if (worked) { worked = false; secs ++; } |
|---|
| 317 |
isum += ips; |
|---|
| 318 |
writefln(fc(cps, "ps"), ", avg ", fc(sum/secs, "ps"), "; ", iterations, " iterations done, ", ips, " iterations per second, ", 1f*isum / secs, " average"); |
|---|
| 319 |
now=time(null); ips=cps=0; |
|---|
| 320 |
} |
|---|
| 321 |
if (!(iterations&(stepsize-1))) { |
|---|
| 322 |
g.render; |
|---|
| 323 |
flip; |
|---|
| 324 |
handleEvents; |
|---|
| 325 |
} |
|---|
| 326 |
if (fastmode || step) { |
|---|
| 327 |
++ips; ++iterations; |
|---|
| 328 |
worked = true; |
|---|
| 329 |
g.step; |
|---|
| 330 |
cps ++; |
|---|
| 331 |
step=false; |
|---|
| 332 |
} |
|---|
| 333 |
handleEvents; |
|---|
| 334 |
} |
|---|
| 335 |
} |
|---|