1 module shogi.shogiban; 2 3 import shogi.constants, shogi.bitboard, shogi.move; 4 5 class Shogiban { 6 //-------------------------------------------------------- 7 // 盤面表現 8 //-------------------------------------------------------- 9 //各駒の有る位置を表す(指し手生成に利用) 10 mixin("Bitboard _bbYYXX;".generateReplace("XX", KOMA_BB).generateReplace("YY", [ "B", "W" ])); 11 //成金については金のビットボードに集約する 12 mixin("alias _bbYYXX=_bbYYKI;".generateReplace("XX", [ "pFU", "pKY", "pKE", "pGI" ]).generateReplace("YY", [ "B", "W" ])); 13 14 //駒の有る位置を表す(取る手生成、打つ手生成、飛び利きの計算に利用) 15 Bitboard _bbOccupyB, _bbOccupyW, _bbOccupy; 16 17 byte[81] _masu; //マスごとの駒(取る手の更新に使用) 18 Teban _teban; //手番 19 Hash _boardHash; //盤面ハッシュ(持ち駒のハッシュ値は含まない) 20 21 //持ち駒 22 Mochigoma _mochigomaB = Mochigoma(0), _mochigomaW = Mochigoma(1); 23 24 //-------------------------------------------------------- 25 // 構造体定義 26 //-------------------------------------------------------- 27 //盤面ハッシュ 28 struct Hash { 29 /// current hash key 30 ulong _key; 31 alias _key this; 32 33 /// update hash 34 void update(in uint to, in uint koma) @nogc { _key ^= _zobrist[koma - 4][to]; } 35 36 /// hash 37 static private immutable ulong[81][28] _zobrist = { 38 import std.random: Random, uniform; 39 ulong[81][28] z; 40 auto gen = Random(77); 41 foreach (koma_i; 4..32) 42 foreach (square_i; 0..81) { z[koma_i - 4][square_i] = uniform(ulong.min, ulong.max, gen) << 1; } 43 return z; 44 }(); 45 } 46 47 //持ち駒 48 struct Mochigoma { 49 uint _a; 50 alias _a this; 51 void init(int i) { _a = i; } 52 //そのままでもハッシュキーとして使えるように桁数の多い歩の数を上位のビットに 53 //そのままでも優劣比較ができるように1bitずつ空ける 54 enum idx { FU, KY, KE, GI, KI, KA, HI, OU }; 55 static immutable int[9] shift = [ 23, 19, 15, 11, 7, 4, 1, 29, 0 ]; 56 static immutable int[9] mask = [ 31, 7, 7, 7, 7, 3, 3, 3, 0 ]; 57 mixin(q{ 58 void addYYXX() @nogc { _a += 1 << shift[idx.XX]; } 59 void remYYXX() @nogc { _a -= 1 << shift[idx.XX]; } 60 uint numYYXX() @nogc const { return (_a >> shift[idx.XX]) & mask[idx.XX]; } 61 }.generateReplace("YY", [ "", "p" ]) 62 .generateReplace("XX", [ "FU", "KY", "KE", "GI", "KI", "KA", "HI", "OU" ])); 63 uint num(in uint i) const { return (_a >> shift[i]) & mask[i]; } 64 wstring toString(in uint i, in uint w) const { 65 import std.conv, std.format; 66 immutable wstring strKoma = "歩香桂銀金角飛玉 "; 67 return num(i) ? format(" %s%s%2d %s", w ? "\x1b[31m" : "", strKoma[i], num(i), w ? "\x1b[39m" : "").to !wstring : " "; 68 } 69 }; 70 71 //------------------------------------------------------- 72 // 初期化とか局面のセットとか 73 //------------------------------------------------------- 74 75 // SFENの読み込み 76 this(in string sfen = "lnsgkgsnl/1r5b1/ppppppppp/9/9/9/PPPPPPPPP/1B5R1/LNSGKGSNL b - 1") { 77 import std.range : split, front, retro; 78 import std.algorithm : countUntil, map, reverse, joiner; 79 import std.ascii : isDigit; 80 immutable PieceToChar = "PpLlNnSsBbRrGgKk"; 81 82 //盤面、手番、持ち駒、手数の文字列に分割 83 auto list = sfen.split; 84 85 //盤面 86 int idx = -1; 87 int sq = 0; 88 foreach (token; list[0].split('/').map !retro.joiner) { 89 if (token.isDigit) 90 sq += token - '0'; // sqを進める 91 else if (token == '+') { 92 setKomaToBoard(komaType.BFU + idx, sq - 1); // XOR 93 setKomaToBoard(komaType.BFU + idx + 16, sq - 1); //直前のsqの駒を成にする 94 } else if ((idx = cast(int)(PieceToChar.countUntil(token))) != -1) 95 setKomaToBoard(komaType.BFU + idx, sq++); 96 } 97 98 //手番 99 _teban = (list[1].front == 'b' ? Teban.SENTE : Teban.GOTE); 100 101 //持ち駒 102 uint num = 1; //数の指定が無ければ持ち駒は1枚 103 uint beforeNum = 0; 104 foreach (token; list[2]) { 105 if (token.isDigit) { 106 num = token - '0' + beforeNum; 107 beforeNum = num * 10; 108 } else if ((idx = cast(int)(PieceToChar.countUntil(token))) != -1) { 109 foreach (_; 0..num) { setKomaToHand(komaType.BFU + idx); } 110 num = 1; 111 beforeNum = 0; 112 } 113 } 114 // TODO 手数を考慮する必要があるケースがあるかも(256手で引き分けとか) 115 } 116 117 //駒の設置。初期化や盤面読み込み用 118 void setKomaToBoard(in uint kt, in uint sq) { 119 assert(sq < 81); 120 assert(kt < 32); 121 final switch (cast(komaType) kt) { 122 mixin(q{ 123 case komaType.YYXX: 124 mixin(q{ BB ^= MASK_SQ[sq]; }.generateReplace("BB", [ "_bbYYXX", "_bbOccupyYY", "_bbOccupy" ])); 125 _masu[sq] = komaType.YYXX; 126 _boardHash.update(sq, komaType.YYXX); 127 break; 128 }.generateReplace("YY", [ "B", "W" ]) 129 .generateReplace("XX", KOMA)); 130 case komaType.none: 131 break; 132 } 133 } 134 void setKomaToHand(in uint kt) { 135 import std.algorithm : startsWith; 136 assert(kt < 20); 137 final switch (cast(komaType) kt) { 138 mixin(q{ 139 case komaType.YYXX: 140 static if ("XX".startsWith("FU", "KY", "KE", "GI", "KA", "HI", "KI")) { 141 _mochigomaYY.addXX; 142 break; 143 } 144 assert(false); 145 }.generateReplace("YY", [ "B", "W" ]) 146 .generateReplace("XX", KOMA)); 147 case komaType.none: 148 break; 149 } 150 } 151 152 override string toString() const { 153 import std.conv, std.format, std.range; 154 immutable wstring strKoma = "・X歩香桂銀角飛金玉と杏圭全馬竜"; 155 auto app = appender !wstring; 156 app.put(format("%s手番\n", "先後"w[_teban & 1])); 157 app.put(" 987654321\n"); 158 foreach (i; 0..9) { 159 app.put(_mochigomaW.toString(i, 1)); 160 foreach (j; 0..9) { 161 uint k = _masu[i * 9 + 8 - j]; 162 app.put(format("%s%s%s", (k & 1) ? "\x1b[31m" : "", strKoma[k >> 1], (k & 1) ? "\x1b[39m" : "")); 163 } 164 app.put(format("%s%s\n", "一二三四五六七八九"w[i], _mochigomaB.toString(8 - i, 0))); 165 } 166 app.put(format("\nHashkey(Board): %016x", _boardHash)); 167 app.put(format("\nHashkey(HandB): %016x", _mochigomaB._a)); 168 app.put(format("\nHashkey(HandW): %016x", _mochigomaW._a)); 169 return app.data.to !string; 170 } 171 172 //盤面更新の展開 173 mixin(import("_domove.d").generateReplace("ACT", [ "do", "undo" ])); 174 } 175 176 //手生成の展開 177 mixin(import("_movegen.d").generateReplace("YY", "ZZ", [ "B", "W" ]));