1 /**
2 Unstable
3 */
4 
5 module dkh.dungeon;
6 
7 /// [+row, +col, -row, -col]
8 immutable static int[2][4] direction4 = [
9     [1, 0], [0, 1], [-1, 0], [0, -1],
10 ];
11 
12 /// [+col, +row+col, +row, +row-col, ...]
13 immutable static int[2][8] direction8 = [
14     [1, 0],
15     [1, 1],
16     [0, 1],
17     [-1, 1],
18     [-1, 0],
19     [-1, -1],
20     [0, -1],
21     [1, -1],
22 ];
23 
24 static int[2] addInt2(in int[2] a, in int[2] b) {
25     int[2] c;
26     c[] = a[] + b[];
27     return c;
28 }
29 
30 /**
31 プロコンでよくある2Dダンジョン探索を支援するライブラリ
32 $(D int[2] = [row, column])をベースとする
33  */
34 struct Dungeon {
35     /// pからdir方向に移動したときの座標
36     static int[2] move4(int[2] p, int dir) {
37         return addInt2(p, direction4[dir]);
38     }
39     /// pからdir方向に移動したときの座標, 8方向
40     static int[2] move8(int[2] p, int dir) {
41         return addInt2(p, direction8[dir]);
42     }
43 
44     immutable int R, C;
45     /**
46     Params:
47         R = row_max
48         C = column_max
49     */
50     this(int R, int C) {
51         this.R = R;
52         this.C = C;
53     }
54     /// pが[0, 0] ~ [R-1, C-1]に入っているか?
55     bool isInside(int[2] p) const {
56         int r = p[0], c = p[1];
57         return 0 <= r && r < R && 0 <= c && c < C;
58     }
59     /// 1次元に潰したときのID, r*R+c
60     int getID(int[2] p) const {
61         int r = p[0], c = p[1];
62         return r*R+c;
63     }
64 }
65 
66 ///
67 auto neighbors4(int[2] p) {
68     static struct Rng {
69         int[2] center;
70         size_t i;
71         bool empty() const { return i == 4;}
72         int[2] front() const { return addInt2(center, direction4[i]); }
73         void popFront() { i++; }
74     }
75     return Rng(p, 0);
76 }
77 
78 ///
79 unittest {
80     import std.algorithm : equal;
81     assert(equal(
82         [3, 5].neighbors4,
83         [[4, 5], [3, 6], [2, 5], [3, 4]],
84         ));
85 }
86 
87 /// list neighbors only inside
88 auto neighbors4(int[2] p, in Dungeon dg) {
89     static struct Rng {
90         int[2] center;
91         Dungeon dg;
92         size_t i;
93         this(in int[2] center, in Dungeon dg) {
94             this.center = center;
95             this.dg = dg;
96             while (!empty() && !dg.isInside(front)) i++;
97         }
98         bool empty() const { return i == 4;}
99         int[2] front() const { return addInt2(center, direction4[i]); }
100         void popFront() {
101             i++;
102             while (!empty() && !dg.isInside(front)) i++;
103         }
104     }
105     return Rng(p, dg);
106 }
107 
108 ///
109 unittest {
110     import std.algorithm : equal;
111     assert(equal(
112         [0, 0].neighbors4(Dungeon(3, 3)),
113         [[1, 0], [0, 1]],
114         ));
115 }
116 
117 /// list neighbors only inside
118 auto neighbors8(int[2] p, in Dungeon dg) {
119     static struct Rng {
120         int[2] center;
121         Dungeon dg;
122         size_t i;
123         this(in int[2] center, in Dungeon dg) {
124             this.center = center;
125             this.dg = dg;
126             while (!empty() && !dg.isInside(front)) i++;
127         }
128         bool empty() const { return i == 8;}
129         int[2] front() const { return addInt2(center, direction8[i]); }
130         void popFront() {
131             i++;
132             while (!empty() && !dg.isInside(front)) i++;
133         }
134     }
135     return Rng(p, dg);
136 }
137 
138 ///
139 unittest {
140     import std.algorithm : equal;
141     assert(equal(
142         [0, 0].neighbors8(Dungeon(3, 3)),
143         [[1, 0], [1, 1], [0, 1]],
144         ));
145 }