| 1 |
import qd; |
|---|
| 2 |
import std.stdio, std.math, std.stdio: format; |
|---|
| 3 |
import tools.base; |
|---|
| 4 |
|
|---|
| 5 |
bool even(int i) { return (i%2)==0; } |
|---|
| 6 |
bool odd(int i) { return (i%2)!=0; } |
|---|
| 7 |
|
|---|
| 8 |
bool has(T)(T[] array, T what) { |
|---|
| 9 |
foreach (elem; array) if (elem==what) return true; |
|---|
| 10 |
return false; |
|---|
| 11 |
} |
|---|
| 12 |
|
|---|
| 13 |
size_t count(T)(T[] array, T what) { |
|---|
| 14 |
size_t res; foreach (elem; array) if (elem==what) res++; |
|---|
| 15 |
return res; |
|---|
| 16 |
} |
|---|
| 17 |
|
|---|
| 18 |
void swap(T)(ref T a, ref T b) { T c=a; a=b; b=c; } |
|---|
| 19 |
|
|---|
| 20 |
void merge(T)(ref T[] a, T[] b) { foreach (elem; b) if (!a.has(elem)) a~=elem; } |
|---|
| 21 |
|
|---|
| 22 |
template Tuple(T...) { alias T Tuple; } |
|---|
| 23 |
|
|---|
| 24 |
void swipe(T)(ref T target, T replacement, void delegate() dg) { |
|---|
| 25 |
auto backup=target; |
|---|
| 26 |
target=replacement; |
|---|
| 27 |
scope(exit) target=backup; |
|---|
| 28 |
dg(); |
|---|
| 29 |
} |
|---|
| 30 |
|
|---|
| 31 |
class SuperGoBoard(C, C free) { |
|---|
| 32 |
private C[] fields; |
|---|
| 33 |
C turn; |
|---|
| 34 |
this(C start) { turn=start; } |
|---|
| 35 |
struct connects { |
|---|
| 36 |
int[2] fields; |
|---|
| 37 |
bool opIn_r(int id) { |
|---|
| 38 |
foreach (field; fields) if (field==id) return true; |
|---|
| 39 |
return false; |
|---|
| 40 |
} |
|---|
| 41 |
static connects opCall(int a, int b) { connects res=void; res.fields[]=[a, b]; return res; } |
|---|
| 42 |
} |
|---|
| 43 |
private connects[] conns; |
|---|
| 44 |
private { |
|---|
| 45 |
bool connected(int id1, int id2) { |
|---|
| 46 |
foreach (conn; conns) if ((id1 in conn) && (id2 in conn)) return true; |
|---|
| 47 |
return false; |
|---|
| 48 |
} |
|---|
| 49 |
void checkRange(int id) { |
|---|
| 50 |
if (id<0 || id!<fields.length) throw new Exception("ID out of range"); |
|---|
| 51 |
} |
|---|
| 52 |
} |
|---|
| 53 |
SuperGoBoard deepdup() { |
|---|
| 54 |
auto res=new SuperGoBoard(turn); |
|---|
| 55 |
foreach (id, value; this.tupleof) |
|---|
| 56 |
static if (is(typeof(value.dup))) res.tupleof[id]=value.dup; |
|---|
| 57 |
else res.tupleof[id]=value; |
|---|
| 58 |
res.conns=conns.dup; |
|---|
| 59 |
return res; |
|---|
| 60 |
} |
|---|
| 61 |
C opIndex(int id) { |
|---|
| 62 |
checkRange(id); |
|---|
| 63 |
return fields[id]; |
|---|
| 64 |
} |
|---|
| 65 |
void opIndexAssign(C col, int id) { |
|---|
| 66 |
checkRange(id); |
|---|
| 67 |
fields[id]=col; |
|---|
| 68 |
} |
|---|
| 69 |
int[] adj(int id) { |
|---|
| 70 |
int[] res; for (int i=0; i<fields.length; ++i) if ((i!=id) && connected(i, id)) res~=i; |
|---|
| 71 |
return res; |
|---|
| 72 |
} |
|---|
| 73 |
private void tryRemove(C exempt=free) { |
|---|
| 74 |
auto processed=new bool[fields.length]; |
|---|
| 75 |
size_t current=0; |
|---|
| 76 |
while (current<fields.length) { |
|---|
| 77 |
while (current<fields.length && (processed[current]||fields[current]==free||fields[current]==exempt)) current++; |
|---|
| 78 |
if (current==fields.length) break; |
|---|
| 79 |
auto curGrp=group(current); scope(exit) foreach (entry; curGrp) processed[entry]=true; |
|---|
| 80 |
if (!freedoms(curGrp)) foreach (entry; curGrp) fields[entry]=free; |
|---|
| 81 |
} |
|---|
| 82 |
} |
|---|
| 83 |
void rule() { |
|---|
| 84 |
tryRemove(turn); |
|---|
| 85 |
tryRemove; |
|---|
| 86 |
} |
|---|
| 87 |
int[] adj(int[] ids) { |
|---|
| 88 |
int[] res; |
|---|
| 89 |
foreach (id; ids) res.merge(adj(id)); |
|---|
| 90 |
return res; |
|---|
| 91 |
} |
|---|
| 92 |
void connect(int a, int b) { |
|---|
| 93 |
if (connected(a, b)) throw new Exception(format("Can't connect (", a, ", ", b, ") twice!")); |
|---|
| 94 |
conns~=connects(a, b); |
|---|
| 95 |
} |
|---|
| 96 |
int[] group(int id) { |
|---|
| 97 |
int[] res; |
|---|
| 98 |
auto col=fields[id]; |
|---|
| 99 |
void recurse(int id) { |
|---|
| 100 |
res~=id; |
|---|
| 101 |
foreach (next; adj(id)) if ((fields[next]==col) && !res.has(next)) recurse(next); |
|---|
| 102 |
} |
|---|
| 103 |
recurse(id); |
|---|
| 104 |
return res; |
|---|
| 105 |
} |
|---|
| 106 |
int freedoms(int[] ids) { |
|---|
| 107 |
int res; |
|---|
| 108 |
foreach (id; adj(ids)) if (fields[id]==free) res++; |
|---|
| 109 |
return res; |
|---|
| 110 |
} |
|---|
| 111 |
bool canSet(int id, C col) { |
|---|
| 112 |
checkRange(id); |
|---|
| 113 |
if (fields[id]!=free) return false; |
|---|
| 114 |
foreach (ac; adj(id)) if (fields[ac]==free) return true; |
|---|
| 115 |
auto test=deepdup(); |
|---|
| 116 |
test.fields[id]=col; |
|---|
| 117 |
test.rule; |
|---|
| 118 |
return test.fields[id]==col; |
|---|
| 119 |
} |
|---|
| 120 |
int opCatAssign(C c) { fields~=c; return fields.length-1; } |
|---|
| 121 |
} |
|---|
| 122 |
|
|---|
| 123 |
enum Color { None, Black, White } |
|---|
| 124 |
|
|---|
| 125 |
struct AnonStruct(string s) { |
|---|
| 126 |
mixin(s); |
|---|
| 127 |
static AnonStruct opCall(typeof(AnonStruct.tupleof) t) { |
|---|
| 128 |
AnonStruct res=void; |
|---|
| 129 |
foreach (id, value; t) res.tupleof[id]=value; |
|---|
| 130 |
return res; |
|---|
| 131 |
} |
|---|
| 132 |
} |
|---|
| 133 |
|
|---|
| 134 |
void drawDigit(int px, int py, ubyte digit) { |
|---|
| 135 |
void draw(bool a, bool b, bool c, bool d, bool e, bool f, bool g) { |
|---|
| 136 |
if (a) line(px-2, py-5, px+2, py-5); |
|---|
| 137 |
if (b) line(px-2, py-5, px-2, py); |
|---|
| 138 |
if (c) line(px+2, py-5, px+2, py); |
|---|
| 139 |
if (d) line(px-2, py, px+2, py); |
|---|
| 140 |
if (e) line(px-2, py, px-2, py+5); |
|---|
| 141 |
if (f) line(px+2, py, px+2, py+5); |
|---|
| 142 |
if (g) line(px+2, py+5, px-2, py+5); |
|---|
| 143 |
} |
|---|
| 144 |
const y=true; |
|---|
| 145 |
const n=false; |
|---|
| 146 |
switch (digit) { |
|---|
| 147 |
case 0: draw(y, y, y, n, y, y, y); break; |
|---|
| 148 |
case 1: draw(n, n, y, n, n, y, n); break; |
|---|
| 149 |
case 2: draw(y, n, y, y, y, n, y); break; |
|---|
| 150 |
case 3: draw(y, n, y, y, n, y, y); break; |
|---|
| 151 |
case 4: draw(n, y, y, y, n, y, n); break; |
|---|
| 152 |
case 5: draw(y, y, n, y, n, y, y); break; |
|---|
| 153 |
case 6: draw(y, y, n, y, y, y, y); break; |
|---|
| 154 |
case 7: draw(y, y, y, n, n, y, n); break; |
|---|
| 155 |
case 8: draw(y, y, y, y, y, y, y); break; |
|---|
| 156 |
case 9: draw(y, y, y, y, n, y, y); break; |
|---|
| 157 |
default: break; |
|---|
| 158 |
} |
|---|
| 159 |
} |
|---|
| 160 |
|
|---|
| 161 |
void drawNumber(int x, int y, int num) { |
|---|
| 162 |
const width=7; |
|---|
| 163 |
ubyte[] digits; do { digits~=num%10; num/=10; } while(num); |
|---|
| 164 |
foreach (id, digit; digits) drawDigit(-id*width+x+(digits.length-1)*width/2, y, digit); |
|---|
| 165 |
} |
|---|
| 166 |
|
|---|
| 167 |
void main() { |
|---|
| 168 |
int size=3; |
|---|
| 169 |
screen(640, 480); |
|---|
| 170 |
flip=false; |
|---|
| 171 |
alias AnonStruct!("int x, y; ") vec2; |
|---|
| 172 |
vec2[int] position; |
|---|
| 173 |
auto board=new SuperGoBoard!(Color, Color.None)(Color.White); |
|---|
| 174 |
int[int][int] columns; |
|---|
| 175 |
const offs=0.5; |
|---|
| 176 |
int width=cast(int)(screen.width/(size*2+1+offs)); |
|---|
| 177 |
int height=cast(int)(screen.height/(size*4+3)); |
|---|
| 178 |
for (int i=0; i<=size; i++) { |
|---|
| 179 |
for (int dir=-1; dir<=1; dir+=2) { |
|---|
| 180 |
auto cid=dir*(i+1); |
|---|
| 181 |
writefln(even(i)?1:0); |
|---|
| 182 |
for (int j=-size*2-1+i; j<=size*2+1-i; ++j) { |
|---|
| 183 |
writefln("j = ", j); |
|---|
| 184 |
//int offset=(screen.width/8)/(size+1); |
|---|
| 185 |
int offset=cast(int)((width*offs)/4); |
|---|
| 186 |
if (even(i) != even(j)) offset=-offset; |
|---|
| 187 |
//if (odd(i)) offset=-offset; |
|---|
| 188 |
auto newPos=board~=Color.None; |
|---|
| 189 |
columns[cid][j]=newPos; |
|---|
| 190 |
bool function(int) cond; |
|---|
| 191 |
if (even(i)) cond=&odd; |
|---|
| 192 |
else cond=&even; |
|---|
| 193 |
if (i) { |
|---|
| 194 |
if (cond(j)) board.connect(newPos, columns[dir*i][j]); |
|---|
| 195 |
if (cond(j) && (j + 2) in columns[dir*i]) |
|---|
| 196 |
board.connect(newPos, columns[dir*i][j+2]); |
|---|
| 197 |
// connect outsides |
|---|
| 198 |
/*if (j==i-size-1) board.connect(newPos, columns[dir*i][j-1]); |
|---|
| 199 |
if (j==size-i+1) board.connect(newPos, columns[dir*i][j+1]);*/ |
|---|
| 200 |
} |
|---|
| 201 |
position[newPos]=vec2(screen.width/2+dir*(width/2+width*i+offset), screen.height/2+j*height); |
|---|
| 202 |
} |
|---|
| 203 |
writefln(columns[cid]); |
|---|
| 204 |
foreach (id, entry; columns[cid]) if (id+1 in columns[cid]) board.connect(entry, columns[cid][id+1]); |
|---|
| 205 |
} |
|---|
| 206 |
} |
|---|
| 207 |
bool function(int) cond; |
|---|
| 208 |
foreach (id, entry; columns[1]) if (odd(id)) board.connect(entry, columns[-1][id]); |
|---|
| 209 |
while(true) { |
|---|
| 210 |
void delegate() click=null; |
|---|
| 211 |
circle(20, 20, 10, Fill=(board.turn==Color.White)?White:Black, Red); |
|---|
| 212 |
foreach (id, conn; board.conns) { |
|---|
| 213 |
auto p1=position[conn.fields[0]]; |
|---|
| 214 |
auto p2=position[conn.fields[1]]; |
|---|
| 215 |
line(p1.x, p1.y, p2.x, p2.y); |
|---|
| 216 |
} |
|---|
| 217 |
foreach (id, color; board.fields) with (position[id]) { |
|---|
| 218 |
auto fill=([Color.None: Blue, Color.Black: Black, Color.White: White][color]); |
|---|
| 219 |
auto overlay=fill; |
|---|
| 220 |
if (distance(x, y, mouse.x, mouse.y)<10) { |
|---|
| 221 |
if (!board.canSet(id, board.turn)) overlay=Red; |
|---|
| 222 |
else { |
|---|
| 223 |
overlay=(board.turn==Color.Black?Black:White); |
|---|
| 224 |
click=stuple(id, board) /apply/ (int id, typeof(board) board) { |
|---|
| 225 |
with (board) if (canSet(id, turn)) { |
|---|
| 226 |
board[id]=turn; |
|---|
| 227 |
rule; |
|---|
| 228 |
turn=(turn==Color.White)?Color.Black:Color.White; |
|---|
| 229 |
} |
|---|
| 230 |
}; |
|---|
| 231 |
} |
|---|
| 232 |
} |
|---|
| 233 |
fill=fill~(fill~overlay); |
|---|
| 234 |
circle(x, y, 10, Red, Fill=fill); |
|---|
| 235 |
if (board[id]) drawNumber(x, y, board.freedoms(board.group(id))); |
|---|
| 236 |
// drawNumber(x, y, id); |
|---|
| 237 |
} |
|---|
| 238 |
|
|---|
| 239 |
flip; events; |
|---|
| 240 |
if (key.pressed(27)) return; |
|---|
| 241 |
if (mouse.clicked(Button.Left) && click) click(); |
|---|
| 242 |
} |
|---|
| 243 |
} |
|---|