Browse Source

drawing seems to work?

dhasenan 5 years ago
parent
commit
53647391dc
9 changed files with 211 additions and 96 deletions
  1. 2
    0
      .gitignore
  2. 10
    2
      source/app.d
  3. 1
    0
      source/cosmic/ai.d
  4. 46
    0
      source/cosmic/base.d
  5. 81
    93
      source/cosmic/blocks.d
  6. 70
    0
      source/cosmic/drawing.d
  7. 1
    0
      source/cosmic/random.d
  8. 0
    1
      source/cosmicai/ai.d
  9. 0
    0
      source/cosmicai/random.d

+ 2
- 0
.gitignore View File

@@ -13,3 +13,5 @@ cosmicai-test-*
13 13
 *.obj
14 14
 *.lst
15 15
 *.swp
16
+cosmicai
17
+*.svg

+ 10
- 2
source/app.d View File

@@ -1,10 +1,18 @@
1 1
 module app;
2 2
 
3
-import comsicai.blocks;
3
+import cosmic.base;
4
+import cosmic.blocks;
5
+import cosmic.drawing;
4 6
 import std.stdio;
5 7
 
6 8
 void main()
7 9
 {
8 10
     auto board = new Board;
9
-    board
11
+    foreach (i, type; BlockType.playable)
12
+    {
13
+        board[Pos(cast(int)i, 0)].type = type;
14
+    }
15
+    auto svg = new SvgSink();
16
+    board.draw(svg);
17
+    svg.write("board.svg");
10 18
 }

+ 1
- 0
source/cosmic/ai.d View File

@@ -0,0 +1 @@
1
+module cosmic.ai;

+ 46
- 0
source/cosmic/base.d View File

@@ -0,0 +1,46 @@
1
+module cosmic.base;
2
+
3
+enum WIDTH = 21;
4
+enum HEIGHT = 11;
5
+
6
+struct Pos
7
+{
8
+    int x, y;
9
+    int opCmp(Pos other) inout
10
+    {
11
+        if (x != other.x)
12
+        {
13
+            return x - other.x;
14
+        }
15
+        return y - other.y;
16
+    }
17
+
18
+    Pos opBinary(string op)(inout Pos other) inout if (op == "+" || op == "-")
19
+    {
20
+        return Pos(
21
+                mixin("this.x " ~ op ~ " other.x"),
22
+                mixin("this.y " ~ op ~ " other.y"));
23
+    }
24
+}
25
+
26
+struct Point
27
+{
28
+    double x, y;
29
+
30
+    Point rotate(real radians) inout
31
+    {
32
+        import std.math : atan2, cos, sin;
33
+        auto angle = atan2(y, x);
34
+        angle += radians;
35
+        auto magnitude = (x^^2 + y^^2) ^^ 0.5;
36
+        return Point(cos(angle) * magnitude, sin(angle) * magnitude);
37
+    }
38
+
39
+    Point opBinary(string op)(inout Point other) inout if (op == "+" || op == "-")
40
+    {
41
+        return Point(
42
+                mixin("this.x " ~ op ~ " other.x"),
43
+                mixin("this.y " ~ op ~ " other.y"));
44
+    }
45
+}
46
+

source/cosmicai/blocks.d → source/cosmic/blocks.d View File

@@ -1,12 +1,13 @@
1
-module cosmicai.blocks;
1
+module cosmic.blocks;
2
+
3
+import cosmic.base;
4
+import cosmic.drawing;
2 5
 
3 6
 import std.math, std.format;
4 7
 
5 8
 final class Board
6 9
 {
7
-    enum WIDTH = 21;
8
-    enum HEIGHT = 11;
9
-    BlockType[HEIGHT][WIDTH] grid;
10
+    Block[HEIGHT][WIDTH] grid;
10 11
     Pos startA, startB;
11 12
 
12 13
     this()
@@ -15,7 +16,7 @@ final class Board
15 16
         {
16 17
             foreach (y; 0..HEIGHT)
17 18
             {
18
-                grid[x][y] = Block(null, false, false, Pos(x, y));
19
+                grid[x][y] = Block(null, Access.none, Pos(x, y));
19 20
             }
20 21
         }
21 22
 
@@ -52,7 +53,7 @@ final class Board
52 53
 
53 54
         while (!queue.empty)
54 55
         {
55
-            auto p = queue.removeAny;
56
+            auto p = this[queue.removeAny];
56 57
             assert(p.type !is null);
57 58
             foreach (offset; p.type.downstreamOffsets)
58 59
             {
@@ -63,7 +64,7 @@ final class Board
63 64
                 }
64 65
                 seen[t] = true;
65 66
                 auto bt = this[t];
66
-                if (bt.type !is null && bt.access & access == Access.none)
67
+                if (bt.type !is null && (bt.access & access) == Access.none)
67 68
                 {
68 69
                     queue.insert(t);
69 70
                 }
@@ -75,6 +76,41 @@ final class Board
75 76
     {
76 77
         return grid[pos.x][pos.y];
77 78
     }
79
+
80
+    void draw(SvgSink sink)
81
+    {
82
+        foreach (x; 0..WIDTH)
83
+        {
84
+            foreach (y; 0..HEIGHT)
85
+            {
86
+                this[Pos(x, y)].draw(sink);
87
+            }
88
+        }
89
+    }
90
+}
91
+
92
+@("Access for a fresh board")
93
+unittest
94
+{
95
+    auto board = new Board;
96
+    assert(board[board.startA].access == Access.a);
97
+    assert(board[board.startB].access == Access.b);
98
+
99
+    // Some random points around that should be empty
100
+    assert(board[Pos(1, 1)].access == Access.none);
101
+    assert(board[Pos(20, 10)].access == Access.none);
102
+    assert(board[Pos(0, 0)].access == Access.none);
103
+
104
+    // The neighborhood near the start positions
105
+    foreach (dx; -1 .. 2)
106
+    {
107
+        foreach (dy; -1 .. 2)
108
+        {
109
+            assert(board[Pos(board.startA.x + dx, board.startA.y + dy)].access == Access.a);
110
+            assert(board[Pos(board.startB.x + dx, board.startB.y + dy)].access == Access.b);
111
+        }
112
+    }
113
+    assert(board[Pos(0, 0)].access == Access.none);
78 114
 }
79 115
 
80 116
 enum Access
@@ -91,42 +127,30 @@ struct Block
91 127
     Access access;
92 128
     Pos pos;
93 129
 
94
-    void draw(SvgSink sink, Board board)
130
+    void draw(SvgSink sink)
95 131
     {
96 132
         string color;
97 133
         final switch (access) with (Access)
98 134
         {
99 135
             case none:
100
-                color = board.colorBg;
136
+                color = sink.colorBg;
101 137
                 break;
102 138
             case both:
103
-                color = board.colorBoth;
139
+                color = sink.colorBoth;
104 140
                 break;
105 141
             case a:
106
-                color = board.colorA;
142
+                color = sink.colorA;
107 143
                 break;
108 144
             case b:
109
-                color = board.colorB;
145
+                color = sink.colorB;
110 146
                 break;
111 147
         }
112 148
         auto center = sink.center(pos);
113 149
         auto scale = Point(sink.halfScale, sink.halfScale);
114
-        auto left = center - scale, right = center + scale;
115
-        sink.put(format(`<rect x1=%s y1=%s x2=%s y2=%s fill="%s" stroke="#CAD0D1" stroke-width="%s" />`,
116
-                    left.x, left.y, right.x, right.y, color, sink.thinStroke));
117
-    }
118
-}
119
-
120
-struct Pos
121
-{
122
-    int x, y;
123
-    int opCmp(Pos other)
124
-    {
125
-        if (x != other.x)
126
-        {
127
-            return x - other.x;
128
-        }
129
-        return y - other.y;
150
+        auto left = center - Point(sink.halfScale, sink.halfScale);
151
+        sink.put(format(`<rect x="%s" y="%s" width="%s" height="%s" fill="%s" stroke="#CAD0D1" stroke-width="%s" />`,
152
+                    left.x, left.y, sink.scale, sink.scale, color, sink.thinStroke));
153
+        if (type !is null) type.draw(sink, pos);
130 154
     }
131 155
 }
132 156
 
@@ -143,8 +167,9 @@ abstract class BlockType
143 167
     static BlockType oplus;
144 168
     static BlockType star;
145 169
     static BlockType ostar;
170
+    static BlockType source;
146 171
 
147
-    static BlockType[] all;
172
+    static BlockType[] playable;
148 173
 
149 174
     static this()
150 175
     {
@@ -154,8 +179,9 @@ abstract class BlockType
154 179
         oplus = new BlockOPlus();
155 180
         star = new BlockStar();
156 181
         ostar = new BlockOStar();
182
+        source = new BlockSource;
157 183
 
158
-        all = [
184
+        playable = [
159 185
             x, ox, plus, oplus, star, ostar,
160 186
             new Arrow(Pos(-1, -1)),
161 187
             new Arrow(Pos(-1,  0)),
@@ -169,57 +195,6 @@ abstract class BlockType
169 195
     }
170 196
 }
171 197
 
172
-struct Point
173
-{
174
-    double x, y;
175
-
176
-    Point rotate(real radians)
177
-    {
178
-        return this;
179
-    }
180
-
181
-    Point opBinary(string op)(Point other) if (op == "+" || op == "-")
182
-    {
183
-        return Point(
184
-                mixin("this.x " ~ op ~ " other.x"),
185
-                mixin("this.y " ~ op ~ " other.y"));
186
-    }
187
-}
188
-
189
-class SvgSink
190
-{
191
-    this(double scale)
192
-    {
193
-        this.scale = scale;
194
-        ap.reserve(1024 * 10);
195
-        ap.put(format(`<svg viewBox="0 0 %s %s" xmlns="http://www.w3.org/2000/svg">`));
196
-    }
197
-
198
-    Appender!string ap;
199
-
200
-    void put(string s)
201
-    {
202
-        ap.put(s);
203
-        ap.put("\n");
204
-    }
205
-
206
-    void write(string filename)
207
-    {
208
-        ap.put(`</svg>`);
209
-        import std.stdio: toFile;
210
-        ap.data.toFile(filename);
211
-    }
212
-
213
-    Point center(Pos p)
214
-    {
215
-        return Point(2 * halfScale * p.x + halfScale, 2 * halfScale * p.y + halfScale);
216
-    }
217
-
218
-    double halfScale() { return 20; }
219
-    double strokeWidth() { return 1; }
220
-    double thinStroke() { return 0.1; }
221
-}
222
-
223 198
 class BlockMine : BlockType
224 199
 {
225 200
     override const(Pos[]) downstreamOffsets() { return null; }
@@ -333,7 +308,7 @@ class BlockStar : BlockType
333 308
 
334 309
 class BlockOStar : BlockType
335 310
 {
336
-    private static immutable Pos[] offsets = [
311
+    static immutable Pos[] offsets = [
337 312
         Pos(-2,  0),
338 313
         Pos( 2,  0),
339 314
         Pos( 0, -2),
@@ -370,13 +345,14 @@ class Arrow : BlockType
370 345
 
371 346
     override void draw(SvgSink sink, Pos pos)
372 347
     {
373
-        auto scale = sink.halfScale;
348
+        auto scale = (toward.x == 0 || toward.y == 0) ? sink.halfScale : sink.angleScale;
349
+        scale *= sink.drawablePortion;
374 350
         auto center = sink.center(pos);
375 351
         auto xstart = scale * -toward.x + center.x;
376 352
         auto ystart = scale * -toward.y + center.y;
377 353
         auto xend = scale * toward.x + center.x;
378 354
         auto yend = scale * toward.y + center.y;
379
-        sink.put(format(`<line x1=%s y1=%s x2=%s y2=%s stroke="black" stroke-width="%s" />`,
355
+        sink.put(format(`<line x1="%s" y1="%s" x2="%s" y2="%s" stroke="black" stroke-width="%s" />`,
380 356
                 xstart, ystart, xend, yend, sink.strokeWidth));
381 357
 
382 358
         // Now the head. This requires a bit of math.
@@ -390,46 +366,58 @@ class Arrow : BlockType
390 366
         auto wing1 = wingCore.rotate(PI / 4) + center;
391 367
         auto wing2 = wingCore.rotate(PI / -4) + center;
392 368
         sink.put(format(`<path d="M %s %s L %s %s L %s %s" stroke="black" stroke-width="%s" />`,
393
-                wing1.x, wing1.y, xend, yend, wing2.x, wing2.y));
369
+                wing1.x, wing1.y, xend, yend, wing2.x, wing2.y, sink.strokeWidth));
394 370
     }
395 371
 }
396 372
 
397 373
 class BlockSource : BlockType
398 374
 {
375
+    override void draw(SvgSink sink, Pos pos)
376
+    {
377
+        // A half-sized circle
378
+        auto scale = sink.halfScale;
379
+        auto center = sink.center(pos);
380
+        sink.put(format(`<circle cx="%s" cy="%s" r="%s" stroke="black" stroke-width="%s" fill="none" />`,
381
+                    center.x, center.y, scale * 0.5, sink.strokeWidth));
382
+    }
383
+    override const(Pos[]) downstreamOffsets()
384
+    {
385
+        return BlockOStar.offsets;
386
+    }
399 387
 }
400 388
 
401 389
 void drawPlus(SvgSink sink, Pos pos)
402 390
 {
403
-    auto scale = sink.halfScale;
391
+    auto scale = sink.halfScale * sink.drawablePortion;
404 392
     auto center = sink.center(pos);
405 393
     auto x1 = center.x - scale;
406 394
     auto x2 = center.x + scale;
407 395
     auto y1 = center.y - scale;
408 396
     auto y2 = center.y + scale;
409
-    sink.put(format(`<line x1=%s y1=%s x2=%s y2=%s stroke="black" stroke-width="%s" />`,
397
+    sink.put(format(`<line x1="%s" y1="%s" x2="%s" y2="%s" stroke="black" stroke-width="%s" />`,
410 398
                 x1, center.y, x2, center.y, sink.strokeWidth));
411
-    sink.put(format(`<line x1=%s y1=%s x2=%s y2=%s stroke="black" stroke-width="%s" />`,
399
+    sink.put(format(`<line x1="%s" y1="%s" x2="%s" y2="%s" stroke="black" stroke-width="%s" />`,
412 400
                 center.x, y2, center.x, y1, sink.strokeWidth));
413 401
 }
414 402
 
415 403
 void drawX(SvgSink sink, Pos pos)
416 404
 {
417
-    auto scale = sink.halfScale;
405
+    auto scale = sink.angleScale * sink.drawablePortion;
418 406
     auto center = sink.center(pos);
419 407
     auto x1 = center.x - scale;
420 408
     auto x2 = center.x + scale;
421 409
     auto y1 = center.y - scale;
422 410
     auto y2 = center.y + scale;
423
-    sink.put(format(`<line x1=%s y1=%s x2=%s y2=%s stroke="black" stroke-width="%s" />`,
411
+    sink.put(format(`<line x1="%s" y1="%s" x2="%s" y2="%s" stroke="black" stroke-width="%s" />`,
424 412
                 x1, y1, x2, y2, sink.strokeWidth));
425
-    sink.put(format(`<line x1=%s y1=%s x2=%s y2=%s stroke="black" stroke-width="%s" />`,
413
+    sink.put(format(`<line x1="%s" y1="%s" x2="%s" y2="%s" stroke="black" stroke-width="%s" />`,
426 414
                 x1, y2, x2, y1, sink.strokeWidth));
427 415
 }
428 416
 
429 417
 void drawCircle(SvgSink sink, Pos pos)
430 418
 {
431
-    auto scale = sink.halfScale;
419
+    auto scale = sink.halfScale * sink.drawablePortion;
432 420
     auto center = sink.center(pos);
433
-    sink.put(format(`<circle cx=%s cy=%s r=%s stroke="black" stroke-width="%s" />`,
421
+    sink.put(format(`<circle cx="%s" cy="%s" r="%s" stroke="black" stroke-width="%s" fill="none" />`,
434 422
                 center.x, center.y, scale, sink.strokeWidth));
435 423
 }

+ 70
- 0
source/cosmic/drawing.d View File

@@ -0,0 +1,70 @@
1
+module cosmic.drawing;
2
+
3
+import cosmic.base;
4
+import cosmic.drawing;
5
+
6
+class SvgSink
7
+{
8
+    import std.array : Appender;
9
+
10
+    this()
11
+    {
12
+        this(new Theme);
13
+    }
14
+
15
+    this(Theme theme)
16
+    {
17
+        this.theme = theme;
18
+        ap.reserve(1024 * 10);
19
+
20
+        import std.format : format;
21
+        ap.put(format(`<svg viewBox="0 0 %s %s" xmlns="http://www.w3.org/2000/svg">`,
22
+                    theme.halfScale * WIDTH * 2,
23
+                    theme.halfScale * HEIGHT * 2));
24
+        ap.put("\n\t");
25
+    }
26
+
27
+    Appender!string ap;
28
+
29
+    void put(string s)
30
+    {
31
+        ap.put(s);
32
+        ap.put("\n\t");
33
+    }
34
+
35
+    void write(string filename)
36
+    {
37
+        ap.put("\n");
38
+        ap.put(`</svg>`);
39
+        import std.stdio: toFile;
40
+        ap.data.toFile(filename);
41
+    }
42
+
43
+    Point center(Pos p)
44
+    {
45
+        return Point(2 * halfScale * p.x + halfScale, 2 * halfScale * p.y + halfScale);
46
+    }
47
+
48
+
49
+    Theme theme = new Theme;
50
+    alias theme this;
51
+}
52
+
53
+
54
+class Theme
55
+{
56
+    double scale = 40;
57
+    double halfScale() @property pure { return scale / 2; }
58
+    double angleScale() @property pure { return (scale / 2) / (2 ^^ 0.5); }
59
+
60
+    double drawablePortion = 0.8;
61
+
62
+    double strokeWidth = 4;
63
+    double thinStroke = 1;
64
+
65
+    string colorA = "#ff0000",
66
+           colorB = "#0000ff",
67
+           colorBoth = "#ff00ff",
68
+           colorBg = "#E3EAEA",
69
+           colorGrid = "#CAD0D1";
70
+}

+ 1
- 0
source/cosmic/random.d View File

@@ -0,0 +1 @@
1
+module cosmic.random;

+ 0
- 1
source/cosmicai/ai.d View File

@@ -1 +0,0 @@
1
-module cosmicai.ai;

+ 0
- 0
source/cosmicai/random.d View File