今回、WCSC31の決勝に残ったフルスクラッチのソフトにQugiyというソフトがある。評価関数は水匠(NNUE)をそのまま使っているらしいのだけど、探索部丸ごと(NNUEまで含めて)自力で実装したという、その技術力の高さに驚かされると共に、指し手生成がやねうら王よりも速く、飛び利き(飛車・角のような長い利き)もMagic Bitboardを用いずにbit演算で高速に求めているのだそうだ。
ここでその解説をする余力はないので、気になる人はPR文書を見て欲しい。
// WCSC31 Qugiy PR文書 : https://www.apply.computer-shogi.org/wcsc31/appeal/Qugiy/appeal.pdf
あと、Qugiyの作者の森さんから教えていただいたのだが、飛車、角もこの記事の末尾のようなコードで(小さなテーブルと)bit演算だけで求まるようだ。これは凄い…!
たこっと[2016]のようにbyteboard(bitboardではなくbyteboard)を用いないと飛び利きをbit演算で求めるのは不可能だと将棋ソフト開発者の間では思われていたのに、まさかbitbooardでもbit演算で飛び利きを求められるとは!!これには完全に脱帽。今年の将棋ソフトの技術大賞をもらえるレベル。(そんな賞はないが、WCSCには独創賞があるので、それには値すると思う。)
// SDT4 たこっとPR文書 : https://denou.jp/tournament2016/img/PR/takotto.pdf
また、作者の森さんによるとQugiyをGitHubか何かで全体のソースコードを公開しようと思っているのだそうだ。
この利きのコード等を近日中にやねうら王に取り込む予定で、それによってたぶん+R10~20のレーティング向上があるはずである。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
// shuffleのアイディア:http://www.amy.hi-ho.ne.jp/okuhara/bitboard.htm を応用しています inline Bitboard bishopAttacks(int i, const Bitboard& occupancy) { const __m256i shuffle = _mm256_set_epi8(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15); const __m256i bishop_mask_lo = _mm256_load_si256((__m256i*)BISHOP_MASK[0][i]); const __m256i bishop_mask_hi = _mm256_load_si256((__m256i*)BISHOP_MASK[1][i]); __m256i occ2 = _mm256_broadcastsi128_si256(occupancy.xmm()); __m256i rocc2 = _mm256_shuffle_epi8(occ2, shuffle); __m256i hi = _mm256_unpackhi_epi64(occ2, rocc2); __m256i lo = _mm256_unpacklo_epi64(occ2, rocc2); hi = _mm256_and_si256(hi, bishop_mask_hi); lo = _mm256_and_si256(lo, bishop_mask_lo); __m256i t1 = _mm256_add_epi64(hi, _mm256_cmpeq_epi64(lo, _mm256_setzero_si256())); __m256i t0 = _mm256_add_epi64(lo, _mm256_set1_epi64x(-1ULL)); t1 = _mm256_and_si256(_mm256_xor_si256(t1, hi), bishop_mask_hi); t0 = _mm256_and_si256(_mm256_xor_si256(t0, lo), bishop_mask_lo); __m256i a2 = _mm256_shuffle_epi8(_mm256_unpackhi_epi64(t0, t1), shuffle); a2 = _mm256_or_si256(a2, _mm256_unpacklo_epi64(t0, t1)); return _mm_or_si128(_mm256_castsi256_si128(a2), _mm256_extracti128_si256(a2, 1)); } inline Bitboard rookAttacks(int i, const Bitboard& occupancy) { const __m128i shuffle = _mm_set_epi8(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15); const __m128i rook_mask_lo = _mm_load_si128((__m128i*)ROOK_MASK[0][i]); const __m128i rook_mask_hi = _mm_load_si128((__m128i*)ROOK_MASK[1][i]); __m128i rocc = _mm_shuffle_epi8(occupancy.xmm(), shuffle); __m128i hi = _mm_unpackhi_epi64(occupancy.xmm(), rocc); __m128i lo = _mm_unpacklo_epi64(occupancy.xmm(), rocc); hi = _mm_and_si128(hi, rook_mask_hi); lo = _mm_and_si128(lo, rook_mask_lo); __m128i t1 = _mm_add_epi64(hi, _mm_cmpeq_epi64(lo, _mm_setzero_si128())); __m128i t0 = _mm_add_epi64(lo, _mm_set1_epi64x(-1ULL)); // cmpeqとどっちがいいか? t1 = _mm_xor_si128(t1, hi); t0 = _mm_xor_si128(t0, lo); t1 = _mm_and_si128(t1, rook_mask_hi); t0 = _mm_and_si128(t0, rook_mask_lo); __m128i updown = _mm_shuffle_epi8(_mm_unpackhi_epi64(t0, t1), shuffle); updown = _mm_or_si128(updown, _mm_unpacklo_epi64(t0, t1)); return lanceAttacks<BLACK>(i, occupancy) | lanceAttacks<WHITE>(i, occupancy) | Bitboard(updown); } |
[2021/05/05 18:30追記]
QugiyがWCSC31で独創賞を獲得されました。Qugiyの森さん、おめでとうございます。
// この記事で私が推したことが関係しているかも知れませんが、私には何の権限もないので、厳正な審査の結果です。
[2021/11/27 0:00 追記] Qugiyのその後のアピール文書にテーブルの初期化コード等が詳しく載ってました。
https://www.apply.computer-shogi.org/wcsc31/appeal/Qugiy/appeal_210518.pdf