From 69dc0ccd0d373e19e857008bacde761959682f06 Mon Sep 17 00:00:00 2001 From: Sakoda Shintaro Date: Sun, 6 May 2018 00:40:08 +0900 Subject: [PATCH] =?UTF-8?q?WCSC28=E3=83=90=E3=83=BC=E3=82=B8=E3=83=A7?= =?UTF-8?q?=E3=83=B3=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 9 +- kaitei_WCSC28/Makefile | 14 + kaitei_WCSC28/bitboard.cpp | 260 +++++ kaitei_WCSC28/bitboard.hpp | 312 ++++++ kaitei_WCSC28/book.hpp | 57 + kaitei_WCSC28/calcScore.cpp | 391 +++++++ kaitei_WCSC28/common.hpp | 95 ++ kaitei_WCSC28/control.cpp | 220 ++++ kaitei_WCSC28/control.hpp | 6 + kaitei_WCSC28/eval_elements.hpp | 44 + kaitei_WCSC28/eval_params.hpp | 442 ++++++++ kaitei_WCSC28/hand.hpp | 78 ++ kaitei_WCSC28/hash_entry.hpp | 34 + kaitei_WCSC28/hash_table.cpp | 30 + kaitei_WCSC28/hash_table.hpp | 40 + kaitei_WCSC28/history.hpp | 63 ++ kaitei_WCSC28/learn.cpp | 544 ++++++++++ kaitei_WCSC28/learn.hpp | 21 + kaitei_WCSC28/learn_self.cpp | 325 ++++++ kaitei_WCSC28/learn_self.hpp | 7 + kaitei_WCSC28/load_game.cpp | 174 ++++ kaitei_WCSC28/load_game.hpp | 30 + kaitei_WCSC28/main.cpp | 23 + kaitei_WCSC28/move.hpp | 135 +++ kaitei_WCSC28/move_picker.cpp | 224 ++++ kaitei_WCSC28/move_picker.hpp | 59 ++ kaitei_WCSC28/piece.cpp | 155 +++ kaitei_WCSC28/piece.hpp | 118 +++ kaitei_WCSC28/piece_state.cpp | 213 ++++ kaitei_WCSC28/piece_state.hpp | 55 + kaitei_WCSC28/position.cpp | 1561 ++++++++++++++++++++++++++++ kaitei_WCSC28/position.hpp | 154 +++ kaitei_WCSC28/position_for_learn.h | 282 +++++ kaitei_WCSC28/pv_table.hpp | 69 ++ kaitei_WCSC28/search_stack.hpp | 29 + kaitei_WCSC28/searcher.cpp | 1217 ++++++++++++++++++++++ kaitei_WCSC28/searcher.hpp | 141 +++ kaitei_WCSC28/shared_data.hpp | 17 + kaitei_WCSC28/square.cpp | 111 ++ kaitei_WCSC28/square.hpp | 220 ++++ kaitei_WCSC28/test.cpp | 140 +++ kaitei_WCSC28/test.hpp | 13 + kaitei_WCSC28/thread.cpp | 60 ++ kaitei_WCSC28/thread.hpp | 86 ++ kaitei_WCSC28/types.cpp | 19 + kaitei_WCSC28/types.hpp | 85 ++ kaitei_WCSC28/usi.cpp | 225 ++++ kaitei_WCSC28/usi.hpp | 26 + kaitei_WCSC28/usi_options.hpp | 22 + 49 files changed, 8652 insertions(+), 3 deletions(-) create mode 100644 kaitei_WCSC28/Makefile create mode 100644 kaitei_WCSC28/bitboard.cpp create mode 100644 kaitei_WCSC28/bitboard.hpp create mode 100644 kaitei_WCSC28/book.hpp create mode 100644 kaitei_WCSC28/calcScore.cpp create mode 100644 kaitei_WCSC28/common.hpp create mode 100644 kaitei_WCSC28/control.cpp create mode 100644 kaitei_WCSC28/control.hpp create mode 100644 kaitei_WCSC28/eval_elements.hpp create mode 100644 kaitei_WCSC28/eval_params.hpp create mode 100644 kaitei_WCSC28/hand.hpp create mode 100644 kaitei_WCSC28/hash_entry.hpp create mode 100644 kaitei_WCSC28/hash_table.cpp create mode 100644 kaitei_WCSC28/hash_table.hpp create mode 100644 kaitei_WCSC28/history.hpp create mode 100644 kaitei_WCSC28/learn.cpp create mode 100644 kaitei_WCSC28/learn.hpp create mode 100644 kaitei_WCSC28/learn_self.cpp create mode 100644 kaitei_WCSC28/learn_self.hpp create mode 100644 kaitei_WCSC28/load_game.cpp create mode 100644 kaitei_WCSC28/load_game.hpp create mode 100644 kaitei_WCSC28/main.cpp create mode 100644 kaitei_WCSC28/move.hpp create mode 100644 kaitei_WCSC28/move_picker.cpp create mode 100644 kaitei_WCSC28/move_picker.hpp create mode 100644 kaitei_WCSC28/piece.cpp create mode 100644 kaitei_WCSC28/piece.hpp create mode 100644 kaitei_WCSC28/piece_state.cpp create mode 100644 kaitei_WCSC28/piece_state.hpp create mode 100644 kaitei_WCSC28/position.cpp create mode 100644 kaitei_WCSC28/position.hpp create mode 100644 kaitei_WCSC28/position_for_learn.h create mode 100644 kaitei_WCSC28/pv_table.hpp create mode 100644 kaitei_WCSC28/search_stack.hpp create mode 100644 kaitei_WCSC28/searcher.cpp create mode 100644 kaitei_WCSC28/searcher.hpp create mode 100644 kaitei_WCSC28/shared_data.hpp create mode 100644 kaitei_WCSC28/square.cpp create mode 100644 kaitei_WCSC28/square.hpp create mode 100644 kaitei_WCSC28/test.cpp create mode 100644 kaitei_WCSC28/test.hpp create mode 100644 kaitei_WCSC28/thread.cpp create mode 100644 kaitei_WCSC28/thread.hpp create mode 100644 kaitei_WCSC28/types.cpp create mode 100644 kaitei_WCSC28/types.hpp create mode 100644 kaitei_WCSC28/usi.cpp create mode 100644 kaitei_WCSC28/usi.hpp create mode 100644 kaitei_WCSC28/usi_options.hpp diff --git a/README.md b/README.md index f9c7e77..980dfd0 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # 概要 将棋ソフト『海底』はUSIプロトコル準拠の思考エンジンです。 -ReleaseされているものはWindows10 64bit環境でのみ動作確認済みです。SDT5バージョンからはbmi、bmi2が使える必要があります。 +ReleaseされているものはWindows10 64bit環境でのみ動作確認済みです。SDT5以降のバージョンからはbmi、bmi2が使える必要があります。 ## 利用方法 使用しているOSがWindowsであれば上方の"Release"タブを選択し、必要な実行ファイルおよび評価パラメータファイルをダウンロードしてお使いください。 @@ -19,7 +19,10 @@ ReleaseされているものはWindows10 64bit環境でのみ動作確認済み 第27回世界コンピュータ将棋選手権に出場した際のバージョンです。駒割、玉周り8マスの利きの数、盤上の利きの総数から局面の評価を行い、アルファベータ法を基本として探索をしています。検討モードには対応しておりません。レートはhttp://www.uuunuuun.com/ によると715となっています。 * kaitei_mini -kaitei_WCSC27を改良したものとなります。棋力が多少向上し、検討モードに対応しました。レートは1080ほどと予想されます。 +kaitei_WCSC27を改良したものとなります。棋力が多少向上し、検討モードに対応しました。レートは800ほどです。 * kaitei_SDT5 -第5回将棋電王トーナメントに出場した際のバージョンです。Bitboardによる盤面情報の管理を導入し、2駒関係をボナンザメソッドで学習させました。レートは1200ほどと予想されます。 +第5回将棋電王トーナメントに出場した際のバージョンです。Bitboardによる盤面情報の管理を導入し、2駒関係をボナンザメソッドで学習させました。レートは900ほどです。 + +* kaitei_WCSC28 +第5回将棋電王トーナメントに出場した際のバージョンです。強化学習により多少の改善に成功しました。レートは1100ほどと予想されます。 diff --git a/kaitei_WCSC28/Makefile b/kaitei_WCSC28/Makefile new file mode 100644 index 0000000..f382da4 --- /dev/null +++ b/kaitei_WCSC28/Makefile @@ -0,0 +1,14 @@ +CXX = g++ +TARGET = kaitei_WCSC28 +CXXFLAGS = -O3 -std=c++14 -mbmi2 -mbmi +LDFLAGS = -pthread -lstdc++fs +SRCS = $(wildcard *.cpp) +OBJS := $(SRCS:.cpp=.o) + +$(TARGET) : $(OBJS) + $(CXX) -o $@ $(OBJS) $(LDFLAGS) + +all : $(OBJS) + +clean: + rm -f $(TARGET) $(OBJS) diff --git a/kaitei_WCSC28/bitboard.cpp b/kaitei_WCSC28/bitboard.cpp new file mode 100644 index 0000000..b6af761 --- /dev/null +++ b/kaitei_WCSC28/bitboard.cpp @@ -0,0 +1,260 @@ +#include"bitboard.hpp" + +Bitboard BOARD_BB; +Bitboard SQUARE_BB[SquareNum]; +Bitboard FILE_BB[FileNum]; +Bitboard RANK_BB[RankNum]; +Bitboard PROMOTION_ZONE_BB[ColorNum]; +Bitboard FRONT_BB[ColorNum][RankNum]; +Bitboard BETWEEN_BB[SquareNum][SquareNum]; +Bitboard ADJACENT_CONTROL_BB[PieceNum]; + +Bitboard PAWN_CONTROL_BB[ColorNum][SquareNum]; +Bitboard KNIGHT_CONTROL_BB[ColorNum][SquareNum]; +Bitboard SILVER_CONTROL_BB[ColorNum][SquareNum]; +Bitboard GOLD_CONTROL_BB[ColorNum][SquareNum]; + +Bitboard BishopEffect[2][SquareNum][128]; +Bitboard BishopEffectMask[2][SquareNum]; + +uint64_t RookFileEffect[RankNum][128]; +Bitboard RookRankEffect[FileNum][128]; + +Bitboard KING_CONTROL_BB[SquareNum]; + +int Slide[] = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, + -1, 10, 10, 10, 10, 10, 10, 10, 10, 10, -1, + -1, 19, 19, 19, 19, 19, 19, 19, 19, 19, -1, + -1, 28, 28, 28, 28, 28, 28, 28, 28, 28, -1, + -1, 37, 37, 37, 37, 37, 37, 37, 37, 37, -1, + -1, 46, 46, 46, 46, 46, 46, 46, 46, 46, -1, + -1, 55, 55, 55, 55, 55, 55, 55, 55, 55, -1, + -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, + -1, 10, 10, 10, 10, 10, 10, 10, 10, 10, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, +}; + +std::ostream& operator << (std::ostream& os, const Bitboard& rhs) { + for (int r = Rank1; r <= Rank9; ++r) { + for (int f = File9; f >= File1; --f) { + Bitboard target = rhs & SQUARE_BB[FRToSquare[f][r]]; + os << (target ? " *" : " ."); + } + os << std::endl; + } + return os; +} + +void Bitboard::init() { + //1.SQUARE_BB + for (auto sq : SquareList) { + SQUARE_BB[sq] = Bitboard(sq); + BOARD_BB |= SQUARE_BB[sq]; + } + + //2.FILE_BB,RANK_BB + for (int f = File1; f <= File9; ++f) { + for (int r = Rank1; r <= Rank9; ++r) { + FILE_BB[f] |= SQUARE_BB[FRToSquare[f][r]]; + RANK_BB[r] |= SQUARE_BB[FRToSquare[f][r]]; + } + } + + //3.PROMOTION_ZONE_BBとFRONT_BB + PROMOTION_ZONE_BB[BLACK] = RANK_BB[Rank1] | RANK_BB[Rank2] | RANK_BB[Rank3]; + PROMOTION_ZONE_BB[WHITE] = RANK_BB[Rank7] | RANK_BB[Rank8] | RANK_BB[Rank9]; + + for (int rank = Rank1; rank <= Rank9; ++rank) { + for (int black_front = rank - 1; black_front >= Rank1; --black_front) + FRONT_BB[BLACK][rank] |= RANK_BB[black_front]; + for (int white_front = rank + 1; white_front <= Rank9; ++white_front) + FRONT_BB[WHITE][rank] |= RANK_BB[white_front]; + } + + //BETWEEN_BB + for (Square sq1 : SquareList) { + for (Square sq2 : SquareList) { + auto dir = directionAtoB(sq1, sq2); + if (dir == H) + continue; + //1マスずつたどっていく + for (Square between = sq1 + dir; between != sq2; between = between + dir) { + BETWEEN_BB[sq1][sq2] |= SQUARE_BB[between]; + } + } + } + + //4.飛び利き + auto indexToOccupied = [](const int index, const int bits, const Bitboard& mask_) { + auto mask = mask_; + auto result = Bitboard(0, 0); + for (int i = 0; i < bits; ++i) { + const Square sq = mask.pop(); + if (index & (1 << i)) + result |= SQUARE_BB[sq]; + } + return result; + }; + + //角の利きのために用意しておく + //n = 0が右上・左下 + //n = 1が左上・右下 + static const Dir diagonal_deltas[2][2] = { + { RU, LD }, + { RD, LU } + }; + + auto calcBishopEffectMask = [](Square sq, int n) { + auto result = Bitboard(0, 0); + + for (auto delta : diagonal_deltas[n]) { + for (Square to = sq + delta; isOnBoard(to); to = to + delta) { + result |= SQUARE_BB[to]; + } + } + + //端は関係ないので外す + result = result & ~(FILE_BB[File1]); + result = result & ~(FILE_BB[File9]); + result = result & ~(RANK_BB[Rank1]); + result = result & ~(RANK_BB[Rank9]); + return result; + }; + + //角の利きを初期化 + for (int n : {0, 1}) { + for (auto sq : SquareList) { + auto& mask = BishopEffectMask[n][sq]; + mask = calcBishopEffectMask(sq, n); + + assert(!mask.crossOver()); + + //全てのBitが立っている場合が最大 + const int bits = static_cast(mask.pop_count()); + const int num = 1 << bits; + for (int i = 0; i < num; ++i) { + //邪魔駒の位置を示すindexであるiからoccupiedへ変換する + Bitboard occupied = indexToOccupied(i, bits, mask); + uint64_t index = occupiedToIndex(occupied, BishopEffectMask[n][sq]); + + //occupiedを考慮した利きを求める + for (auto delta : diagonal_deltas[n]) { + for (Square to = sq + delta; isOnBoard(to); to = to + delta) { + BishopEffect[n][sq][index] |= SQUARE_BB[to]; + + //邪魔駒があったらそこまで + if (occupied & SQUARE_BB[to]) + break; + } + } + } + } + } + + //飛車の縦方向 + for (int rank = Rank1; rank <= Rank9; ++rank) { + const int num1s = 7; + for (int i = 0; i < (1 << num1s); ++i) { + //iが邪魔駒の配置を表したindex + //1つシフトすればそのまま2~8段目のマスの邪魔駒を表す + int occupied = i << 1; + uint64_t bb = 0; + + //上に利きを伸ばす + for (int r = rank - 1; r >= Rank1; --r) { + bb |= (1LL << SquareToNum[FRToSquare[File1][r]]); + //邪魔駒があったらそこまで + if (occupied & (1 << (r - Rank1))) + break; + } + + //下に利きを伸ばす + for (int r = rank + 1; r <= Rank9; ++r) { + bb |= (1LL << SquareToNum[FRToSquare[File1][r]]); + //邪魔駒があったらそこまで + if (occupied & (1 << (r - Rank1))) + break; + } + RookFileEffect[rank][i] = bb; + } + } + + //飛車の横方向 + for (int file = File1; file <= File9; ++file) { + const int num1s = 7; + for (int i = 0; i < (1 << num1s); ++i) { + int j = i << 1; + Bitboard bb(0, 0); + for (int f = file - 1; f >= File1; --f) { + bb |= SQUARE_BB[FRToSquare[f][Rank1]]; + if (j & (1 << (f - File1))) + break; + } + for (int f = file + 1; f <= File9; ++f) { + bb |= SQUARE_BB[FRToSquare[f][Rank1]]; + if (j & (1 << (f - File1))) + break; + } + RookRankEffect[file][i] = bb; + } + } + + //5.近接駒の利き + for (auto sq : SquareList) { + //歩 + if (isOnBoard(sq + U)) + PAWN_CONTROL_BB[BLACK][sq] |= SQUARE_BB[sq + U]; + if (isOnBoard(sq + D)) + PAWN_CONTROL_BB[WHITE][sq] |= SQUARE_BB[sq + D]; + + //香車は飛車の利きを用いて計算するのでテーブルは必要ない + + //桂馬 + if (SquareToRank[sq] >= Rank3) { + if (isOnBoard(sq + RUU)) + KNIGHT_CONTROL_BB[BLACK][sq] |= SQUARE_BB[sq + RUU]; + if (isOnBoard(sq + LUU)) + KNIGHT_CONTROL_BB[BLACK][sq] |= SQUARE_BB[sq + LUU]; + } + if (SquareToRank[sq] <= Rank7) { + if (isOnBoard(sq + RDD)) + KNIGHT_CONTROL_BB[WHITE][sq] |= SQUARE_BB[sq + RDD]; + if (isOnBoard(sq + LDD)) + KNIGHT_CONTROL_BB[WHITE][sq] |= SQUARE_BB[sq + LDD]; + } + + //銀 + for (Dir delta : CanMove[BLACK_SILVER]) { + Square to = sq + delta; + if (isOnBoard(to)) + SILVER_CONTROL_BB[BLACK][sq] |= SQUARE_BB[to]; + } + for (Dir delta : CanMove[WHITE_SILVER]) { + Square to = sq + delta; + if (isOnBoard(to)) + SILVER_CONTROL_BB[WHITE][sq] |= SQUARE_BB[to]; + } + + //金 + for (Dir delta : CanMove[BLACK_GOLD]) { + Square to = sq + delta; + if (isOnBoard(to)) + GOLD_CONTROL_BB[BLACK][sq] |= SQUARE_BB[to]; + } + for (Dir delta : CanMove[WHITE_GOLD]) { + Square to = sq + delta; + if (isOnBoard(to)) + GOLD_CONTROL_BB[WHITE][sq] |= SQUARE_BB[to]; + } + + //飛車・角は前ステップで初期化した + + //王 + KING_CONTROL_BB[sq] = bishopControl(sq, BOARD_BB) | rookControl(sq, BOARD_BB); + + //と金・成香・成桂・成銀は金と同じ動き + //竜・馬は飛車角と王を合成すればいい + } +} \ No newline at end of file diff --git a/kaitei_WCSC28/bitboard.hpp b/kaitei_WCSC28/bitboard.hpp new file mode 100644 index 0000000..1593805 --- /dev/null +++ b/kaitei_WCSC28/bitboard.hpp @@ -0,0 +1,312 @@ +#pragma once +#ifndef BITBOARD_HPP +#define BITBOARD_HPP + +#include"square.hpp" +#include"common.hpp" +#include + +class Bitboard { +public: + //引数なしコンストラクタは空でいいかな + Bitboard() {} + + //値を直接引数に持つコンストラクタ + Bitboard(uint64_t b0, uint64_t b1) : board_{ b0, b1 } {} + + //Squareを指定してそこだけを立てるコンストラクタ + Bitboard(Square sq) { + if (sq <= SQ79) { + board_[0] = 1LL << SquareToNum[sq]; + board_[1] = 0; + } else { + board_[0] = 0; + board_[1] = 1LL << (SquareToNum[sq] - SquareToNum[SQ81]); + } + } + + //Stockfishとの互換性がなんちゃら + //普通にあった方が便利そうだけども + operator bool() const { + return !(board_[0] == 0 && board_[1] == 0); + } + + uint64_t merge() const { + return board_[0] | board_[1]; + } + + bool crossOver() const { + return board_[0] & board_[1]; + } + + Square pop() { + Square sq; + if (board_[0] != 0) { + sq = SquareList[pop_lsb(board_[0])]; + } else { + sq = SquareList[pop_lsb(board_[1]) + 63]; + } + assert(isOnBoard(sq)); + return sq; + } + + auto pop_count() const { + return POP_CNT64(board_[0]) + POP_CNT64(board_[1]); + } + + template + void forEach(Function f) const { + Bitboard copy = *this; + while (copy) { + Square sq = copy.pop(); + f(sq); + } + } + + //演算子類 + Bitboard operator ~() const { + return Bitboard(~board_[0], ~board_[1]); + } + Bitboard operator |(const Bitboard& bb) const { + return Bitboard(board_[0] | bb.board_[0], board_[1] | bb.board_[1]); + } + Bitboard operator |(const Square sq) { + return *this | Bitboard(sq); + } + Bitboard operator &(const Bitboard& bb) const { + return Bitboard(board_[0] & bb.board_[0], board_[1] & bb.board_[1]); + } + Bitboard& operator|=(const Bitboard& rhs) { + board_[0] |= rhs.board_[0]; + board_[1] |= rhs.board_[1]; + return *this; + } + Bitboard& operator&=(const Bitboard& rhs) { + board_[0] &= rhs.board_[0]; + board_[1] &= rhs.board_[1]; + return *this; + } + Bitboard& operator <<= (const int shift) { + board_[0] <<= shift; + board_[1] <<= shift; + return *this; + } + Bitboard operator << (const int shift) { + return Bitboard(*this) <<= shift; + } + + static void init(); + + static int part(const Square sq) { + return (sq > SQ79 ? 1 : 0); + } + + //bitレイアウトはboard_[0]に1筋から7筋まで、board_[1]に残りの8,9筋を入れたい + //64bit目は不使用? + + /* + union { + uint64_t board_[2]; + __m128i m_; + }; + */ + uint64_t board_[2]; +}; + +extern Bitboard BOARD_BB; +extern Bitboard SQUARE_BB[SquareNum]; +extern Bitboard FILE_BB[FileNum]; +extern Bitboard RANK_BB[RankNum]; +extern Bitboard PROMOTION_ZONE_BB[ColorNum]; +extern Bitboard FRONT_BB[ColorNum][RankNum]; +extern Bitboard BETWEEN_BB[SquareNum][SquareNum]; +extern Bitboard ADJACENT_CONTROL_BB[PieceNum]; + +extern Bitboard PAWN_CONTROL_BB[ColorNum][SquareNum]; +extern Bitboard KNIGHT_CONTROL_BB[ColorNum][SquareNum]; +extern Bitboard SILVER_CONTROL_BB[ColorNum][SquareNum]; +extern Bitboard GOLD_CONTROL_BB[ColorNum][SquareNum]; + +extern Bitboard BishopEffect[2][SquareNum][128]; +extern Bitboard BishopEffectMask[2][SquareNum]; + +extern uint64_t RookFileEffect[RankNum][128]; +extern Bitboard RookRankEffect[FileNum][128]; + +extern Bitboard KING_CONTROL_BB[SquareNum]; + +extern int Slide[]; + +std::ostream& operator << (std::ostream& os, const Bitboard& rhs); + +inline Bitboard pawnControl(const Color color, const Square sq) { + return PAWN_CONTROL_BB[color][sq]; +} + +inline Bitboard blackPawnControl(const Square sq, const Bitboard &occ) { + return PAWN_CONTROL_BB[BLACK][sq]; +} + +inline Bitboard whitePawnControl(const Square sq, const Bitboard &occ) { + return PAWN_CONTROL_BB[WHITE][sq]; +} + +inline Bitboard knightControl(const Color color, const Square sq) { + return KNIGHT_CONTROL_BB[color][sq]; +} + +inline Bitboard blackNightControl(const Square sq, const Bitboard& occ) { + return KNIGHT_CONTROL_BB[BLACK][sq]; +} + +inline Bitboard whiteNightControl(const Square sq, const Bitboard& occ) { + return KNIGHT_CONTROL_BB[WHITE][sq]; +} + +inline Bitboard silverControl(const Color color, const Square sq) { + return SILVER_CONTROL_BB[color][sq]; +} + +inline Bitboard blackSilverControl(const Square sq, const Bitboard& occ) { + return SILVER_CONTROL_BB[BLACK][sq]; +} + +inline Bitboard whiteSilverControl(const Square sq, const Bitboard& occ) { + return SILVER_CONTROL_BB[WHITE][sq]; +} + +inline Bitboard goldControl(const Color color, const Square sq) { + return GOLD_CONTROL_BB[color][sq]; +} + +inline Bitboard blackGoldControl(const Square sq, const Bitboard& occ) { + return GOLD_CONTROL_BB[BLACK][sq]; +} + +inline Bitboard whiteGoldControl(const Square sq, const Bitboard& occ) { + return GOLD_CONTROL_BB[WHITE][sq]; +} + +inline uint64_t occupiedToIndex(const Bitboard& occupied, const Bitboard& mask) { + return PEXT64(occupied.merge(), mask.merge()); +} + +inline Bitboard bishopEffect0(const Square sq, const Bitboard& occupied) { + const Bitboard block0(occupied & BishopEffectMask[0][sq]); + return BishopEffect[0][sq][occupiedToIndex(block0, BishopEffectMask[0][sq])]; +} + +inline Bitboard bishopEffect1(const Square sq, const Bitboard& occupied) { + const Bitboard block1(occupied & BishopEffectMask[1][sq]); + return BishopEffect[1][sq][occupiedToIndex(block1, BishopEffectMask[1][sq])]; +} + +inline Bitboard bishopControl(const Square sq, const Bitboard& occupied) { + return bishopEffect0(sq, occupied) | bishopEffect1(sq, occupied); +} + +inline Bitboard rookFileControl(const Square sq, const Bitboard& occupied) { + const int index = (occupied.board_[Bitboard::part(sq)] >> Slide[sq]) & 0x7f; + const File f = SquareToFile[sq]; + return (f <= File7) ? + Bitboard(RookFileEffect[SquareToRank[sq]][index] << (9 * (f - File1)), 0) : + Bitboard(0, RookFileEffect[SquareToRank[sq]][index] << (9 * (f - File8))); +} + +inline Bitboard rookRankControl(const Square sq, const Bitboard& occupied) { + int r = SquareToRank[sq]; + uint64_t u = (occupied.board_[1] << 6 * 9) + (occupied.board_[0] >> 9); + uint64_t index = PEXT64(u, 0b1000000001000000001000000001000000001000000001000000001 << (r - Rank1)); + return RookRankEffect[SquareToFile[sq]][index] << (r - Rank1); +} + +inline Bitboard rookControl(const Square sq, const Bitboard& occupied) { + return rookFileControl(sq, occupied) | rookRankControl(sq, occupied); +} + +inline Bitboard lanceControl(const Color color, const Square sq, const Bitboard& occupied) { + return rookControl(sq, occupied) & FRONT_BB[color][SquareToRank[sq]]; +} + +inline Bitboard blackLanceControl(const Square sq, const Bitboard& occ) { + return lanceControl(BLACK, sq, occ); +} + +inline Bitboard whiteLanceControl(const Square sq, const Bitboard& occ) { + return lanceControl(WHITE, sq, occ); +} + +inline Bitboard kingControl(const Square sq, const Bitboard& occ) { + return KING_CONTROL_BB[sq]; +} + +inline Bitboard horseControl(const Square sq, const Bitboard& occupied) { + return bishopControl(sq, occupied) | kingControl(sq, occupied); +} + +inline Bitboard dragonControl(const Square sq, const Bitboard& occupied) { + return rookControl(sq, occupied) | kingControl(sq, occupied); +} + +//速くならなかった +static Bitboard(*controlFunc[])(const Square sq, const Bitboard& occupied) = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //0~9 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //10~19 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //20~29 + 0, 0, 0, //30~32 + blackPawnControl, //33 + blackLanceControl, //34 + blackNightControl, //35 + blackSilverControl,//36 + blackGoldControl, //37 + bishopControl, //38 + rookControl, //39 + kingControl, //40 + 0, 0, 0, 0, 0, 0, 0, 0, //40~48 + blackGoldControl, //49 と + blackGoldControl, //50 成香 + blackGoldControl, //51 成桂 + blackGoldControl, //52 成銀 + 0, //53 成金などない + horseControl, //54 馬 + dragonControl, //55 竜 + 0, 0, 0, 0, 0, 0, 0, 0, 0, //56~64 + whitePawnControl, //65 + whiteLanceControl, //66 + whiteNightControl, //67 + whiteSilverControl,//68 + whiteGoldControl, //69 + bishopControl, //70 + rookControl, //71 + kingControl, //72 + 0, 0, 0, 0, 0, 0, 0, 0, //73~80 + whiteGoldControl, //81 と + whiteGoldControl, //82 成香 + whiteGoldControl, //83 成桂 + whiteGoldControl, //84 成銀 + 0, //85 成金などない + horseControl, //86 馬 + dragonControl, //87 竜 +}; + +inline Bitboard controlBB(const Square sq, const Piece p, const Bitboard& occupied) { + return controlFunc[p](sq, occupied); +} + +//手番側から見て一番奥の段を返す(駒打ちの時に利用) +inline Bitboard farRank1FromColor(const Color c) { + return (c == BLACK ? RANK_BB[Rank1] : RANK_BB[Rank9]); +} + +//手番側から見て奥から2つ目の段を返す(駒打ちの時に利用) +inline Bitboard farRank2FromColor(const Color c) { + return (c == BLACK ? RANK_BB[Rank2] : RANK_BB[Rank8]); +} + +//手番側から見て手前にある7段を返す(駒打ちの時に利用) +inline Bitboard frontRank7FromColor(const Color c) { + return (c == BLACK ? RANK_BB[Rank3] | RANK_BB[Rank4] | RANK_BB[Rank5] | RANK_BB[Rank6] | RANK_BB[Rank7] | RANK_BB[Rank8] | RANK_BB[Rank9] + : RANK_BB[Rank1] | RANK_BB[Rank2] | RANK_BB[Rank3] | RANK_BB[Rank4] | RANK_BB[Rank5] | RANK_BB[Rank6] | RANK_BB[Rank7]); +} + +#endif // !BITBOARD_HPP \ No newline at end of file diff --git a/kaitei_WCSC28/book.hpp b/kaitei_WCSC28/book.hpp new file mode 100644 index 0000000..18ba412 --- /dev/null +++ b/kaitei_WCSC28/book.hpp @@ -0,0 +1,57 @@ +#pragma once +#ifndef BOOK_HPP +#define BOOK_HPP + +#include"move.hpp" +#include"position.hpp" +#include + +//ǖʂɑ΂Վ̃f[^ +class BookEntry { +public: + BookEntry() { + best_move_[BLACK] = best_move_[WHITE] = NULL_MOVE; + } + + Move pickBest(const Color c) { + return best_move_[c]; + } + + Move pickOneMove(const Color c) { + std::random_device seed_gen; + std::mt19937 engine(seed_gen()); + std::shuffle(other_moves_[c].begin(), other_moves_[c].end(), engine); + return other_moves_[c][0]; + } + + void update(const Move move, const Color c) { + if (best_move_[c] == NULL_MOVE) { + best_move_[c] = move; + return; + } + if (best_move_[c] != move) { + other_moves_[c].push_back(best_move_[c]); + best_move_[c] = move; + } + } + +private: + //KvӊOɓ + //Ƃ肠best_moveƂȊOɂ܂Ăg݂łĂ݂悤 + Move best_move_[ColorNum]; + std::vector other_moves_[ColorNum]; +}; + +class Book { +public: + Move probe(const Position& pos, const bool do_pick_best) const; + void makeBookByThink(); + void makeBookByGames(); + void readFromFile(); + void writeToFile() const; +private: + //sfen󂯎Ă̋ǖʂ̒ՎԂ + std::unordered_map book_; +}; + +#endif // !BOOK_HPP \ No newline at end of file diff --git a/kaitei_WCSC28/calcScore.cpp b/kaitei_WCSC28/calcScore.cpp new file mode 100644 index 0000000..8c757eb --- /dev/null +++ b/kaitei_WCSC28/calcScore.cpp @@ -0,0 +1,391 @@ +#include"position.hpp" +#include"piece.hpp" +#include"common.hpp" +#include"piece_state.hpp" +#include"eval_params.hpp" +#include +#include + +const int EVAL_SCALE = 32; + +enum { + PAWN_VALUE = 100, + LANCE_VALUE = 267, + KNIGHT_VALUE = 295, + SILVER_VALUE = 424, + GOLD_VALUE = 510, + BISHOP_VALUE = 654, + ROOK_VALUE = 738, + PAWN_PROMOTE_VALUE = 614, + LANCE_PROMOTE_VALUE = 562, + KNIGHT_PROMOTE_VALUE = 586, + SILVER_PROMOTE_VALUE = 569, + BISHOP_PROMOTE_VALUE = 951, + ROOK_PROMOTE_VALUE = 1086, +}; + +int piece_value[] = { + 0, static_cast(PAWN_VALUE * 1.05), static_cast(LANCE_VALUE * 1.05), static_cast(KNIGHT_VALUE * 1.05), static_cast(SILVER_VALUE * 1.05), + static_cast(GOLD_VALUE * 1.05), static_cast(BISHOP_VALUE * 1.05), static_cast(ROOK_VALUE * 1.05), 0, 0, //0~9 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //10~19 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //20~29 + 0, 0, 0, PAWN_VALUE, LANCE_VALUE, KNIGHT_VALUE, SILVER_VALUE, GOLD_VALUE, BISHOP_VALUE, ROOK_VALUE, //30~39 + 0, 0, 0, 0, 0, 0, 0, 0, 0, PAWN_PROMOTE_VALUE, //40~49 + LANCE_PROMOTE_VALUE, KNIGHT_PROMOTE_VALUE, SILVER_PROMOTE_VALUE, 0, BISHOP_PROMOTE_VALUE, ROOK_PROMOTE_VALUE, 0, 0, 0, 0, //50~59 + 0, 0, 0, 0, 0, -PAWN_VALUE, -LANCE_VALUE, -KNIGHT_VALUE, -SILVER_VALUE, -GOLD_VALUE, //60~69 + -BISHOP_VALUE, -ROOK_VALUE, 0, 0, 0, 0, 0, 0, 0, 0, //70~79 + 0, -PAWN_PROMOTE_VALUE, -LANCE_PROMOTE_VALUE, -KNIGHT_PROMOTE_VALUE, -SILVER_PROMOTE_VALUE, 0, -BISHOP_PROMOTE_VALUE, -ROOK_PROMOTE_VALUE, 0, 0 //80~89 +}; + +Score Position::initScore() { + piece_score_ = Score(0); + + //盤上にある駒の価値 + for (Square sq : SquareList) { + if (0 > board_[sq] || PieceNum < board_[sq]) { + print(); + printHistory(); + assert(false); + } + piece_score_ += piece_value[board_[sq]]; + } + + //持ち駒の価値 + for (Piece p = PAWN; p <= ROOK; p++) { + piece_score_ += piece_value[p] * hand_[BLACK].num(p); + piece_score_ -= piece_value[p] * hand_[WHITE].num(p); + } + kp_score_ = Score(0); + pp_score_ = Score(0); + + if (eval_params == nullptr) { + return Score(0); + } + + auto ee = makeEvalElements(); + for (unsigned int i = 0; i < PIECE_STATE_LIST_SIZE; i++) { + kp_score_ += eval_params->kp[ee.black_king_sq][ee.piece_state_list[i]]; + kp_score_ -= eval_params->kp[ee.white_king_sq_reversed][invPieceState(ee.piece_state_list[i])]; + for (unsigned int j = i; j < PIECE_STATE_LIST_SIZE; j++) { + pp_score_ += eval_params->pp[ee.piece_state_list[i]][ee.piece_state_list[j]]; + } + } + + return piece_score_; +} + +void Position::testPieceValue() { + std::cout << "HAND_PAWN_VALUE : " << piece_value[PAWN] << std::endl; + std::cout << "HAND_LANCE_VALUE : " << piece_value[LANCE] << std::endl; + std::cout << "HAND_KNIGHT_VALUE : " << piece_value[KNIGHT] << std::endl; + std::cout << "HAND_SILVER_VALUE : " << piece_value[SILVER] << std::endl; + std::cout << "HAND_GOLD_VALUE : " << piece_value[GOLD] << std::endl; + std::cout << "HAND_BISHOP_VALUE : " << piece_value[BISHOP] << std::endl; + std::cout << "HAND_ROOK_VALUE : " << piece_value[ROOK] << std::endl; + + std::cout << "BLACK_PAWN_VALUE : " << piece_value[BLACK_PAWN] << std::endl; + std::cout << "BLACK_LANCE_VALUE : " << piece_value[BLACK_LANCE] << std::endl; + std::cout << "BLACK_KNIGHT_VALUE : " << piece_value[BLACK_KNIGHT] << std::endl; + std::cout << "BLACK_SILVER_VALUE : " << piece_value[BLACK_SILVER] << std::endl; + std::cout << "BLACK_GOLD_VALUE : " << piece_value[BLACK_GOLD] << std::endl; + std::cout << "BLACK_BISHOP_VALUE : " << piece_value[BLACK_BISHOP] << std::endl; + std::cout << "BLACK_ROOK_VALUE : " << piece_value[BLACK_ROOK] << std::endl; + std::cout << "BLACK_KING_VALUE : " << piece_value[BLACK_KING] << std::endl; + + std::cout << "BLACK_PAWN_PROMOTE_VALUE : " << piece_value[BLACK_PAWN_PROMOTE] << std::endl; + std::cout << "BLACK_LANCE_PROMOTE_VALUE : " << piece_value[BLACK_LANCE_PROMOTE] << std::endl; + std::cout << "BLACK_KNIGHT_PROMOTE_VALUE : " << piece_value[BLACK_KNIGHT_PROMOTE] << std::endl; + std::cout << "BLACK_SILVER_PROMOTE_VALUE : " << piece_value[BLACK_SILVER_PROMOTE] << std::endl; + std::cout << "BLACK_BISHOP_PROMOTE_VALUE : " << piece_value[BLACK_BISHOP_PROMOTE] << std::endl; + std::cout << "BLACK_ROOK_PROMOTE_VALUE : " << piece_value[BLACK_ROOK_PROMOTE] << std::endl; + + std::cout << "WHITE_PAWN_VALUE : " << piece_value[WHITE_PAWN] << std::endl; + std::cout << "WHITE_LANCE_VALUE : " << piece_value[WHITE_LANCE] << std::endl; + std::cout << "WHITE_KNIGHT_VALUE : " << piece_value[WHITE_KNIGHT] << std::endl; + std::cout << "WHITE_SILVER_VALUE : " << piece_value[WHITE_SILVER] << std::endl; + std::cout << "WHITE_GOLD_VALUE : " << piece_value[WHITE_GOLD] << std::endl; + std::cout << "WHITE_BISHOP_VALUE : " << piece_value[WHITE_BISHOP] << std::endl; + std::cout << "WHITE_ROOK_VALUE : " << piece_value[WHITE_ROOK] << std::endl; + std::cout << "WHITE_KING_VALUE : " << piece_value[WHITE_KING] << std::endl; + + std::cout << "WHITE_PAWN_PROMOTE_VALUE : " << piece_value[WHITE_PAWN_PROMOTE] << std::endl; + std::cout << "WHITE_LANCE_PROMOTE_VALUE : " << piece_value[WHITE_LANCE_PROMOTE] << std::endl; + std::cout << "WHITE_KNIGHT_PROMOTE_VALUE : " << piece_value[WHITE_KNIGHT_PROMOTE] << std::endl; + std::cout << "WHITE_SILVER_PROMOTE_VALUE : " << piece_value[WHITE_SILVER_PROMOTE] << std::endl; + std::cout << "WHITE_BISHOP_PROMOTE_VALUE : " << piece_value[WHITE_BISHOP_PROMOTE] << std::endl; + std::cout << "WHITE_ROOK_PROMOTE_VALUE : " << piece_value[WHITE_ROOK_PROMOTE] << std::endl; +} + +Score Position::score() const { + return piece_score_ + (kp_score_ + pp_score_) / EVAL_SCALE; +} + +Features Position::makeEvalElements() const { + Features ee; + ee.black_king_sq = SquareToNum[king_sq_[BLACK]]; + ee.white_king_sq_reversed = SquareToNum[InvSquare[king_sq_[WHITE]]]; + + + int index = 0; + for (Square sq : SquareList) { + if (board_[sq] != EMPTY && kind(board_[sq]) != KING) { + ee.piece_state_list[index++] = pieceState(board_[sq], sq); + } + } + for (Piece p = PAWN; p <= ROOK; p++) { + for (Color c : {BLACK, WHITE}) { + for (int i = 0; i < hand_[c].num(p); i++) { + ee.piece_state_list[index++] = pieceState(kind(p), i + 1, c); + } + } + } + assert(index == PIECE_STATE_LIST_SIZE); + return ee; +} + +Features Position::makeEvalElements(std::vector& pv, Color& leaf_color) { + //pvがうまくとれているとは限らないので局面を動かした回数を数えておく + int counter = 0; + for (Move move : pv) { + if (isLegalMove(move)) { + doMove(move); + counter++; + } else { + break; + } + } + leaf_color = color_; +#if DEBUG + print(); + printHistory(); +#endif + Features ee = ee_; + for (int i = 0; i < counter; i++) + undo(); + return ee; +} + +Move Position::transformValidMove(const Move move) { + //stringToMoveではどっちの手番かがわからない + //つまりsubjectが完全には入っていないので手番付きの駒を入れる + if (move.isDrop()) { + return dropMove(move.to(), (color_ == BLACK ? toBlack(move.subject()) : toWhite(move.subject()))); + } else { + return Move(move.to(), move.from(), false, move.isPromote(), board_[move.from()], board_[move.to()]); + } +} + +Score Position::calcScoreDiff() { + Move move = lastMove(); + //移動する駒が玉かどうか、captureかどうかの4通りで場合わけ + if (kind(move.subject()) == KING) { + if (move.capture() == EMPTY) { //玉を動かし、駒は取らない手 + if (pieceToColor(move.subject()) == BLACK) { //先手玉を動かす場合 + for (int i = 0; i < PIECE_STATE_LIST_SIZE; i++) { + kp_score_ -= eval_params->kp[SquareToNum[move.from()]][ee_.piece_state_list[i]]; + kp_score_ += eval_params->kp[SquareToNum[move.to()]][ee_.piece_state_list[i]]; + } + } else { //後手玉を動かす場合 + for (int i = 0; i < PIECE_STATE_LIST_SIZE; i++) { + kp_score_ += eval_params->kp[SquareToNum[InvSquare[move.from()]]][invPieceState(ee_.piece_state_list[i])]; + kp_score_ -= eval_params->kp[SquareToNum[InvSquare[move.to()]]][invPieceState(ee_.piece_state_list[i])]; + } + } + } else { //玉を動かし、駒を取る手 + Color move_color = pieceToColor(move.subject()); + if (move_color == BLACK) { //先手玉を動かす場合 + PieceState captured = pieceState(move.capture(), move.to(), ~move_color); + int change_index = -1; + //captureされたPについてKP, PPを引く + for (int i = 0; i < PIECE_STATE_LIST_SIZE; i++) { + kp_score_ -= eval_params->kp[SquareToNum[move.from()]][ee_.piece_state_list[i]]; + pp_score_ -= eval_params->pp[captured][ee_.piece_state_list[i]]; + if (captured == ee_.piece_state_list[i]) { + change_index = i; + } + } + //敵玉とのKP + kp_score_ += eval_params->kp[ee_.white_king_sq_reversed][invPieceState(captured)]; + + assert(change_index != -1); + + PieceState add = pieceState(kind(move.capture()), hand_[move_color].num(kind(move.capture())), move_color); + + //追加されるPについてKP,PPを足す + for (int i = 0; i < PIECE_STATE_LIST_SIZE; i++) { + if (i == change_index) { + ee_.piece_state_list[i] = add; + } + kp_score_ += eval_params->kp[SquareToNum[move.to()]][ee_.piece_state_list[i]]; + pp_score_ += eval_params->pp[add][ee_.piece_state_list[i]]; + } + //敵玉とのKP + kp_score_ -= eval_params->kp[ee_.white_king_sq_reversed][invPieceState(add)]; + + } else { //後手玉を動かす場合 + PieceState captured = pieceState(move.capture(), move.to(), ~move_color); + int change_index = -1; + //captureされたPについてKP, PPを引く + for (int i = 0; i < PIECE_STATE_LIST_SIZE; i++) { + kp_score_ += eval_params->kp[SquareToNum[InvSquare[move.from()]]][invPieceState(ee_.piece_state_list[i])]; + pp_score_ -= eval_params->pp[captured][ee_.piece_state_list[i]]; + if (captured == ee_.piece_state_list[i]) { + change_index = i; + } + } + //敵玉とのKP + kp_score_ -= eval_params->kp[ee_.black_king_sq][captured]; + + assert(change_index != -1); + + PieceState add = pieceState(kind(move.capture()), hand_[move_color].num(kind(move.capture())), move_color); + + //追加されるPについてKP,PPを足す + for (int i = 0; i < PIECE_STATE_LIST_SIZE; i++) { + if (i == change_index) { + ee_.piece_state_list[i] = add; + } + kp_score_ -= eval_params->kp[SquareToNum[InvSquare[move.to()]]][invPieceState(ee_.piece_state_list[i])]; + pp_score_ += eval_params->pp[add][ee_.piece_state_list[i]]; + } + + //敵玉とのKP + kp_score_ += eval_params->kp[ee_.black_king_sq][add]; + } + } + } else if (move.isDrop()) { //駒を打つ手 + //変化するのはKP, PP両方 + //打つために消えるKP,PPを引く + PieceState dropped_from = pieceState(kind(move.subject()), hand_[pieceToColor(move.subject())].num(move.subject()) + 1, pieceToColor(move.subject())); + int change_index = -1; + //KP + kp_score_ -= eval_params->kp[ee_.black_king_sq][dropped_from]; + kp_score_ += eval_params->kp[ee_.white_king_sq_reversed][invPieceState(dropped_from)]; + //PP + for (int i = 0; i < PIECE_STATE_LIST_SIZE; i++) { + pp_score_ -= eval_params->pp[dropped_from][ee_.piece_state_list[i]]; + if (dropped_from == ee_.piece_state_list[i]) { + change_index = i; + } + } + + assert(change_index != -1); + + PieceState dropped_to = pieceState(move.subject(), move.to(), pieceToColor(move.subject())); + + //KP + kp_score_ += eval_params->kp[ee_.black_king_sq][dropped_to]; + kp_score_ -= eval_params->kp[ee_.white_king_sq_reversed][invPieceState(dropped_to)]; + //PP + for (int i = 0; i < PIECE_STATE_LIST_SIZE; i++) { + if (i == change_index) { + ee_.piece_state_list[i] = dropped_to; + } + pp_score_ += eval_params->pp[dropped_to][ee_.piece_state_list[i]]; + } + } else { + if (move.capture() == EMPTY) { + //変化するのはKP, PP両方 + //打つために消えるKP,PPを引く + PieceState removed_from = pieceState(move.subject(), move.from(), pieceToColor(move.subject())); + int change_index = -1; + //KP + kp_score_ -= eval_params->kp[ee_.black_king_sq][removed_from]; + kp_score_ += eval_params->kp[ee_.white_king_sq_reversed][invPieceState(removed_from)]; + //PP + for (int i = 0; i < PIECE_STATE_LIST_SIZE; i++) { + pp_score_ -= eval_params->pp[removed_from][ee_.piece_state_list[i]]; + if (removed_from == ee_.piece_state_list[i]) { + change_index = i; + } + } + + assert(change_index != -1); + + PieceState added_to = pieceState(move.isPromote() ? promote(move.subject()) : move.subject(), move.to(), pieceToColor(move.subject())); + + //KP + kp_score_ += eval_params->kp[ee_.black_king_sq][added_to]; + kp_score_ -= eval_params->kp[ee_.white_king_sq_reversed][invPieceState(added_to)]; + //PP + for (int i = 0; i < PIECE_STATE_LIST_SIZE; i++) { + if (i == change_index) { + ee_.piece_state_list[i] = added_to; + } + pp_score_ += eval_params->pp[added_to][ee_.piece_state_list[i]]; + } + } else { + //2つPが消えて2つPが増える.厄介 + PieceState removed1 = pieceState(move.subject(), move.from(), pieceToColor(move.subject())); + PieceState removed2 = pieceState(move.capture(), move.to(), pieceToColor(move.capture())); + int change_index1 = -1, change_index2 = -1; + //KP + kp_score_ -= eval_params->kp[ee_.black_king_sq][removed1]; + kp_score_ += eval_params->kp[ee_.white_king_sq_reversed][invPieceState(removed1)]; + kp_score_ -= eval_params->kp[ee_.black_king_sq][removed2]; + kp_score_ += eval_params->kp[ee_.white_king_sq_reversed][invPieceState(removed2)]; + //PP + for (int i = 0; i < PIECE_STATE_LIST_SIZE; i++) { + pp_score_ -= eval_params->pp[removed1][ee_.piece_state_list[i]]; + pp_score_ -= eval_params->pp[removed2][ee_.piece_state_list[i]]; + if (removed1 == ee_.piece_state_list[i]) { + change_index1 = i; + } + if (removed2 == ee_.piece_state_list[i]) { + change_index2 = i; + } + } + //同じものを2回引いているので補正する + pp_score_ += eval_params->pp[removed1][removed2]; + + assert(change_index1 != -1 && change_index2 != -1); + + PieceState added1 = pieceState(move.isPromote() ? promote(move.subject()) : move.subject(), move.to(), pieceToColor(move.subject())); + PieceState added2 = pieceState(kind(move.capture()), hand_[pieceToColor(move.subject())].num(move.capture()), pieceToColor(move.subject())); + + //KP + kp_score_ += eval_params->kp[ee_.black_king_sq][added1]; + kp_score_ -= eval_params->kp[ee_.white_king_sq_reversed][invPieceState(added1)]; + kp_score_ += eval_params->kp[ee_.black_king_sq][added2]; + kp_score_ -= eval_params->kp[ee_.white_king_sq_reversed][invPieceState(added2)]; + + //PP + for (int i = 0; i < PIECE_STATE_LIST_SIZE; i++) { + if (i == change_index1) { + ee_.piece_state_list[i] = added1; + } + if (i == change_index2) { + ee_.piece_state_list[i] = added2; + } + + pp_score_ += eval_params->pp[added1][ee_.piece_state_list[i]]; + pp_score_ += eval_params->pp[added2][ee_.piece_state_list[i]]; + } + //同じものを2回足しているので補正する + pp_score_ -= eval_params->pp[added1][added2]; + } + } + +#ifdef DEBUG + int old_kp = kp_score_, old_pp = pp_score_; + initScore(); + if (old_kp != kp_score_ || old_pp != pp_score_) { + print(); + move.print(); + assert(false); + } +#endif + + return piece_score_ + (kp_score_ + pp_score_) / EVAL_SCALE; +} + +void Position::printScoreDetail() const { + auto ee = makeEvalElements(); + for (unsigned int i = 0; i < PIECE_STATE_LIST_SIZE; i++) { + std::cout << ee.black_king_sq << " " << ee.piece_state_list[i] << " += " << eval_params->kp[ee.black_king_sq][ee.piece_state_list[i]] << std::endl; + std::cout << ee.white_king_sq_reversed << " " << invPieceState(ee.piece_state_list[i]) << " -= " << eval_params->kp[ee.white_king_sq_reversed][invPieceState(ee.piece_state_list[i])] << std::endl; + for (unsigned int j = i; j < PIECE_STATE_LIST_SIZE; j++) { + std::cout << ee.piece_state_list[i] << " " << ee.piece_state_list[j] << " += " << eval_params->pp[ee.piece_state_list[i]][ee.piece_state_list[j]] << std::endl; + } + } +} \ No newline at end of file diff --git a/kaitei_WCSC28/common.hpp b/kaitei_WCSC28/common.hpp new file mode 100644 index 0000000..96899e6 --- /dev/null +++ b/kaitei_WCSC28/common.hpp @@ -0,0 +1,95 @@ +#ifndef COMMON_HPP +#define COMMON_HPP + +#include +#include +#include +#ifdef _MSC_VER +#include +#elif __GNUC__ +#include +#include +#endif + +#ifdef __GNUC__ +//cf : http://blog.jiubao.org/2015/01/gcc-bitscanforward-bitscanreverse-msvc.html +unsigned char inline GNUBitScanForward(unsigned long *Index, unsigned long long Mask) { + if (Mask) { + *Index = __builtin_ctzll(Mask); + return 1; + } else { + /* 戻り値が0のとき、*Index がどうなるかは未定義。*/ + return 0; + } +} + +unsigned char inline GNUBitScanReverse(unsigned long *Index, unsigned long long Mask) { + if (Mask) { + *Index = 63 - __builtin_clzll(Mask); + return 1; + } else { + /* 戻り値が0のとき、*Index がどうなるかは未定義。*/ + return 0; + } +} +#endif + +inline int MSB64(unsigned long long v) { + assert(v != 0); + unsigned long index; + +#ifdef _MSC_VER + _BitScanReverse64(&index, v); +#elif __GNUC__ + GNUBitScanReverse(&index, v); +#endif + + return index; +} + +inline auto PEXT64(unsigned long long a, unsigned long long b) { + return _pext_u64(a, b); +} + +inline auto POP_CNT64(uint64_t bits) { +#ifdef _MSC_VER + return __popcnt64(bits); +#elif __GNUC__ + return __builtin_popcountll (bits); +#endif +} + +inline int pop_lsb(uint64_t& b) { + unsigned long index; + +#ifdef _MSC_VER + _BitScanForward64(&index, b); +#elif __GNUC__ + GNUBitScanForward(&index, b); +#endif + + b = _blsr_u64(b); + return index; +} + +template +static double sigmoid(Type x, double gain) { + return 1 / (1 + exp(-gain * x)); +} + +template +static double standardSigmoid(Type x) { + return sigmoid(x, 1.0); +} + +template +static double d_sigmoid(Type x, double gain) { + return gain * sigmoid(x, gain) * (1 - sigmoid(x, gain)); +} + +template +static double d_standardSigmoid(Type x) { + return d_sigmoid(x, 1.0); +} + +#endif \ No newline at end of file diff --git a/kaitei_WCSC28/control.cpp b/kaitei_WCSC28/control.cpp new file mode 100644 index 0000000..b231346 --- /dev/null +++ b/kaitei_WCSC28/control.cpp @@ -0,0 +1,220 @@ +#include"position.hpp" + +//PositionNX̗Ɋւ̃t@Cɂ܂Ƃ߂Ă + +static constexpr int EXTEND = 0, BLOCK = 1; + +void Position::putPiece(Piece p, Square sq) { + //uƂɂ蒷Ղ + for (Color c : {BLACK, WHITE}) { + uint8_t long_control_direction = controls_[c][sq].direction; + //while (long_control_direction > 0) { + // auto index = MSB64(long_control_direction); + // long_control_direction &= ~(1 << index); + //} + for (auto long_control : LongControlList) { + if (long_control_direction & long_control) { + changeLongControl(sq, long_control, c, BLOCK); + } + } + } + + //u̗𔭐 + changeControl(p, sq, 1); +} + +void Position::removePiece(Piece p, Square sq) { + //uĂ̗ + changeControl(p, sq, -1); + + //菜Ƃɂ蒷ʂ + for (Color c : {BLACK, WHITE}) { + uint8_t long_control_direction = controls_[c][sq].direction; + for (auto long_control : LongControlList) { + if (long_control_direction & long_control) { + changeLongControl(sq, long_control, c, EXTEND); + } + } + } +} + +void Position::changeControl(Piece p, Square sq, int diff) { + Color c = pieceToColor(p); + if (kind(p) == LANCE && !(p & PROMOTE)) { + //ĂȂ + uint8_t dir_bit = (pieceToColor(p) == BLACK ? Con_D : Con_U); + Dir d = (pieceToColor(p) == BLACK ? U : D); + for (Square to = sq + d; board_[to] != WALL; to = to + d) { + if (diff == 1) { + controls_[c][to].direction |= dir_bit; + controls_[c][to].number++; + } else { + controls_[c][to].direction &= ~dir_bit; + controls_[c][to].number--; + } + if (board_[to] != EMPTY) { + break; + } + } + } else if (kind(p) == BISHOP) { + if (p & PROMOTE) { + //nȂ㉺Eɗאڗ𑫂 + controls_[c][sq + U].number += diff; + controls_[c][sq + R].number += diff; + controls_[c][sq + D].number += diff; + controls_[c][sq + L].number += diff; + //Ƃ͊pƓ + } + + Dir dirs[4] = { RU, RD, LD, LU }; + ControlDir condirs[4] = { Con_LD, Con_LU, Con_RU, Con_RD }; + for (int i = 0; i < 4; i++) { + for (Square to = sq + dirs[i]; board_[to] != WALL; to = to + dirs[i]) { + if (diff == 1) { + controls_[c][to].direction |= condirs[i]; + controls_[c][to].number++; + } else { + controls_[c][to].direction &= ~condirs[i]; + controls_[c][to].number--; + } + if (board_[to] != EMPTY) { + break; + } + } + } + } else if (kind(p) == ROOK) { + if (p & PROMOTE) { + //Ȃ΂4ɗאڗ𑫂 + controls_[c][sq + RU].number += diff; + controls_[c][sq + RD].number += diff; + controls_[c][sq + LD].number += diff; + controls_[c][sq + LU].number += diff; + //Ƃ͔ԂƓ + } + + Dir dirs[4] = { U, R, D, L }; + ControlDir condirs[4] = { Con_D, Con_L, Con_U, Con_R }; + for (int i = 0; i < 4; i++) { + for (Square to = sq + dirs[i]; board_[to] != WALL; to = to + dirs[i]) { + if (diff == 1) { + controls_[c][to].direction |= condirs[i]; + controls_[c][to].number++; + } else { + controls_[c][to].direction &= ~condirs[i]; + controls_[c][to].number--; + } + if (board_[to] != EMPTY) { + break; + } + } + } + } else { + //̑ + controlBB(sq, p, occupied_all_).forEach([&](const Square to) { + controls_[c][to].number += diff; + }); + } +} + +void Position::changeLongControl(Square sq, ControlDir cd, Color c, int extend_or_block) { + //sqɂcd,c̗L΂ + //cdƋtɔՖʂǂĂ + Dir d = ConDirToOppositeDir[cd]; + if (d == H) { + return; + } + for (Square to = sq + d; board_[to] != WALL; to = to + d) { + if (extend_or_block == EXTEND) { + controls_[c][to].direction |= cd; + controls_[c][to].number++; + } else { + controls_[c][to].direction &= ~cd; + controls_[c][to].number--; + } + if (board_[to] != EMPTY) { + break; + } + } +} + +void Position::initControl() { + for (Square sq : SquareList) { + controls_[BLACK][sq].number = 0; + controls_[BLACK][sq].direction = 0; + controls_[WHITE][sq].number = 0; + controls_[WHITE][sq].direction = 0; + } + for (Square sq : SquareList) { + if (board_[sq] == EMPTY) { + continue; + } + changeControl(board_[sq], sq, 1); + } +} + +void Position::checkControl(){ + Control before[ColorNum][SquareNum]; + for (Square sq : SquareList) { + before[BLACK][sq] = controls_[BLACK][sq]; + before[WHITE][sq] = controls_[WHITE][sq]; + } + + initControl(); + for (Square sq : SquareList) { + if (before[BLACK][sq].number != controls_[BLACK][sq].number) { + print(); + printf("Ԉ̗\n"); + std::printf("XWVUTSRQP\n"); + std::printf("------------------\n"); + for (int r = Rank1; r <= Rank9; r++) { + for (int f = File9; f >= File1; f--) { + printf("%2d", (int)before[BLACK][FRToSquare[f][r]].number); + } + printf("|%d\n", r); + } + } + if (before[BLACK][sq].direction != controls_[BLACK][sq].direction) { + print(); + assert(false); + } + if (before[WHITE][sq].number != controls_[WHITE][sq].number) { + print(); + printf("Ԉ̗\n"); + std::printf("XWVUTSRQP\n"); + std::printf("------------------\n"); + for (int r = Rank1; r <= Rank9; r++) { + for (int f = File9; f >= File1; f--) { + printf("%2d", (int)before[WHITE][FRToSquare[f][r]].number); + } + printf("|%d\n", r); + } + assert(false); + } + if (before[WHITE][sq].direction != controls_[WHITE][sq].direction) { + print(); + assert(false); + } + } +} + +void Position::printControl() const { + // + printf("̗\n"); + std::printf("XWVUTSRQP\n"); + std::printf("------------------\n"); + for (int r = Rank1; r <= Rank9; r++) { + for (int f = File9; f >= File1; f--) { + printf("%2d", (int)controls_[BLACK][FRToSquare[f][r]].number); + } + printf("|%d\n", r); + } + printf("̗\n"); + std::printf("XWVUTSRQP\n"); + std::printf("------------------\n"); + for (int r = Rank1; r <= Rank9; r++) { + for (int f = File9; f >= File1; f--) { + printf("%2d", (int)controls_[WHITE][FRToSquare[f][r]].number); + } + printf("|%d\n", r); + } +} diff --git a/kaitei_WCSC28/control.hpp b/kaitei_WCSC28/control.hpp new file mode 100644 index 0000000..87d6a78 --- /dev/null +++ b/kaitei_WCSC28/control.hpp @@ -0,0 +1,6 @@ +#pragma once + +struct Control { + uint8_t number; //̐ + uint8_t direction; //̕ +}; \ No newline at end of file diff --git a/kaitei_WCSC28/eval_elements.hpp b/kaitei_WCSC28/eval_elements.hpp new file mode 100644 index 0000000..864056c --- /dev/null +++ b/kaitei_WCSC28/eval_elements.hpp @@ -0,0 +1,44 @@ +#pragma once +#ifndef EVAL_ELEMENTS_HPP +#define EVAL_ELEMENTS_HPP + +#include"piece.hpp" +#include"piece_state.hpp" +#include + +constexpr int PIECE_STATE_LIST_SIZE = 38; + +//評価値の計算に用いる特徴量をまとめたもの +struct Features { + //玉位置がint型なのは現状まだSquare型は壁(番兵)分があって数字とマスがうまく対応していないから + int black_king_sq; //先手玉の位置 + int white_king_sq_reversed; //後手玉の位置を反転させていれる + PieceState piece_state_list[PIECE_STATE_LIST_SIZE]; + + inline bool operator==(const Features& rhs) { + return (black_king_sq == rhs.black_king_sq + && white_king_sq_reversed == rhs.white_king_sq_reversed + && piece_state_list == rhs.piece_state_list); + } + inline bool operator!=(const Features& rhs) { + return !(*this == rhs); + } +}; + +inline bool isEqual(Features f1, Features f2) { + //ソートしてpiece_state_listの中身さえ合っていればいいという比較関数 + std::sort(f1.piece_state_list, f1.piece_state_list + PIECE_STATE_LIST_SIZE); + std::sort(f2.piece_state_list, f2.piece_state_list + PIECE_STATE_LIST_SIZE); + return (f1.black_king_sq == f1.black_king_sq && f2.white_king_sq_reversed == f2.white_king_sq_reversed && f1.piece_state_list == f2.piece_state_list); +} + +inline std::ostream& operator<<(std::ostream& os, Features f) { + os << "先手玉:" << f.black_king_sq << std::endl; + os << "後手玉:" << f.white_king_sq_reversed << std::endl; + for (auto e : f.piece_state_list) { + os << e << std::endl; + } + return os; +} + +#endif // !EVAL_ELEMENT_HPP diff --git a/kaitei_WCSC28/eval_params.hpp b/kaitei_WCSC28/eval_params.hpp new file mode 100644 index 0000000..bbd17fd --- /dev/null +++ b/kaitei_WCSC28/eval_params.hpp @@ -0,0 +1,442 @@ +#pragma once +#ifndef EVAL_PARAMS_HPP +#define EVAL_PARAMS_HPP + +#include"piece.hpp" +#include"eval_elements.hpp" +#include"square.hpp" +#include +#include +#include +#include + +//評価関数のパラメータをまとめたもの +//インスタンスとしては実際のパラメータ(int)と、学習時の勾配(float?)がありえる + +template +class EvalParams { +public: + T pp[PieceStateNum][PieceStateNum]; + T kp[81][PieceStateNum]; + + void updateGradient(const Features &ee, const T delta); + void updateParamsSGD(std::unique_ptr>& grad, const double learn_rate); + void updateParamsAdaGrad(std::unique_ptr>& grad, std::unique_ptr>& RMSgrad, const double learn_rate); + void updateParamsAdaDelta(std::unique_ptr>& grad, std::unique_ptr>& RMSgrad, std::unique_ptr>& RMSdelta, const double decay_rate); + void clear(); + void readFile(std::string file_name = "parameters.bin"); + void writeFile(std::string file_name = "parameters.bin"); + T abs() const; + T max_abs() const; + + //すべてのパラメータに同じ操作をする場合これを使って書けばいいのでは + template void forEach(Function f); + + template void copy(std::unique_ptr>& source); + template void roundCopy(std::unique_ptr>& source); + + EvalParams& operator*=(double rhs) { + forEach([&](T& value) { + value = static_cast(value * rhs); + }); + return *this; + } + + EvalParams operator*(const double x); + EvalParams operator+(const EvalParams rhs); + EvalParams square(); + + void calcRMS(std::unique_ptr>& grad, const double decay_rate); + void addSquare(std::unique_ptr>& grad); + + void addL1grad(const EvalParams& eval_params, const double coefficient) { + //これはforEachを使っては書けない気がするなぁ + for (int p = 0; p < PieceStateNum; p++) { + for (int k = 0; k < 81; k++) { + if (eval_params.kp[k][p] == 0) + continue; + kp[k][p] += (eval_params.kp[k][p] > 0 ? coefficient : -coefficient); + } + for (int p2 = 0; p2 < PieceStateNum; p2++) { + if (eval_params.pp[p][p2] == 0) + continue; + pp[p][p2] += (eval_params.pp[p][p2] > 0 ? coefficient : -coefficient); + } + } + } + + void printHistgram() { + //ヒストグラムを取ってみよう + std::map histgram; + forEach([&](T value) { + histgram[static_cast(value)]++; + }); + for (auto e : histgram) { + std::cout << e.first << ":" << e.second << ", "; + } + std::cout << std::endl; + } + + void printBigParams(T threshold, std::ostream& os) const { + for (int p1 = 0; p1 < PieceStateNum; p1++) { + for (int p2 = 0; p2 < PieceStateNum; p2++) { + if (std::abs(pp[p1][p2]) >= threshold) { + os << "pp[" << PieceState(p1) << "][" << PieceState(p2) << "] = " << pp[p1][p2] << std::endl; + } + } + for (int k = 0; k < 81; k++) { + if (std::abs(kp[k][p1]) >= threshold) { + os << "kp[" << SquareList[k] << "][" << PieceState(p1) << "] = " << kp[k][p1] << std::endl; + } + } + } + } + + void printParamsSorted(std::ostream& os) { + for (auto compare = max_abs(); compare >= -max_abs(); --compare) { + if (compare == 0) { + continue; + } + for (int p1 = 0; p1 < PieceStateNum; p1++) { + for (int p2 = 0; p2 < PieceStateNum; p2++) { + if (pp[p1][p2] == compare) { + os << "pp[" << PieceState(p1) << "][" << PieceState(p2) << "] = " << pp[p1][p2] << std::endl; + } + } + for (int k = 0; k < 81; k++) { + if (kp[k][p1] == compare) { + os << "kp[" << SquareList[k] << "][" << PieceState(p1) << "] = " << kp[k][p1] << std::endl; + } + } + } + } + } +}; + +template +void EvalParams::updateGradient(const Features &ee, const T delta) { + //eeに出てくる特徴量に関わる勾配すべてをdeltaだけ変える + for (unsigned int i = 0; i < PIECE_STATE_LIST_SIZE; i++) { + kp[ee.black_king_sq][ee.piece_state_list[i]] += delta; + kp[ee.white_king_sq_reversed][invPieceState(ee.piece_state_list[i])] -= delta; + for (unsigned int j = i; j < PIECE_STATE_LIST_SIZE; j++) { + //普通のp1,p2 + pp[ee.piece_state_list[i]][ee.piece_state_list[j]] += delta; + + //inv(p1), inv(p2)について + pp[invPieceState(ee.piece_state_list[i])][invPieceState(ee.piece_state_list[j])] -= delta; + + if (ee.piece_state_list[i] != ee.piece_state_list[j]) { + //p1,p2の順番逆にしたものにも同じ勾配を足す + pp[ee.piece_state_list[j]][ee.piece_state_list[i]] += delta; + pp[invPieceState(ee.piece_state_list[j])][invPieceState(ee.piece_state_list[i])] -= delta; + } + } + } +} + +template +void EvalParams::updateParamsSGD(std::unique_ptr>& grad, const double learn_rate) { + double sum_of_delta = 0, max_delta = 0; + + //ヒストグラムを取ってみよう + std::map histgram; + + for (int p1 = 0; p1 < PieceStateNum; p1++) { + for (int p2 = 0; p2 < PieceStateNum; p2++) { + double delta = ((learn_rate * grad->pp[p1][p2])); + + max_delta = std::max(max_delta, std::fabs(delta)); + + histgram[static_cast(delta)]++; + + //パラメータ更新 + pp[p1][p2] -= static_cast(delta); + + //合計いくら変わったか見るため + sum_of_delta += std::fabs(delta); + } + for (int k = 0; k < 81; k++) { + double delta = ((learn_rate * grad->kp[k][p1])); + + max_delta = std::max(max_delta, std::abs(delta)); + + histgram[static_cast(delta)]++; + + //パラメータ更新 + kp[k][p1] -= static_cast(delta); + + //合計いくら変わったか見るため + sum_of_delta += std::fabs(delta); + } + } + std::cout << "sum_of_delta = " << sum_of_delta << ", max_delta_abs = " << max_delta << std::endl; + std::cout << "delta-histgram" << std::endl; + for (auto e : histgram) { + std::cout << e.first << ":" << e.second << ", "; + } + std::cout << std::endl; +} + +template +inline void EvalParams::updateParamsAdaGrad(std::unique_ptr>& grad, std::unique_ptr>& RMSgrad, const double learn_rate) { + double sum_of_delta = 0, max_delta = 0; + + //ヒストグラムを取ってみよう + std::map histgram; + + static const double epcilon = 0.00000001; + for (int p1 = 0; p1 < PieceStateNum; p1++) { + for (int p2 = 0; p2 < PieceStateNum; p2++) { + double delta = (learn_rate * grad->pp[p1][p2]) / std::sqrt(RMSgrad->pp[p1][p2] + epcilon); + + max_delta = std::max(max_delta, std::fabs(delta)); + + histgram[static_cast(delta)]++; + + //パラメータ更新 + pp[p1][p2] -= delta; + + //合計いくら変わったか見るため + sum_of_delta += std::fabs(delta); + } + for (int k = 0; k < 81; k++) { + double delta = (learn_rate * grad->kp[k][p1]) / std::sqrt(RMSgrad->kp[k][p1] + epcilon); + + max_delta = std::max(max_delta, std::abs(delta)); + + histgram[static_cast(delta)]++; + + //パラメータ更新 + kp[k][p1] -= delta; + + //合計いくら変わったか見るため + sum_of_delta += std::fabs(delta); + } + } + std::cout << "delta = " << sum_of_delta << ", max_delta_abs = " << max_delta << std::endl; + std::cout << "delta-histgram" << std::endl; + for (auto e : histgram) { + std::cout << e.first << ":" << e.second << ", "; + } + std::cout << std::endl; +} + +template +inline void EvalParams::updateParamsAdaDelta(std::unique_ptr>& grad, std::unique_ptr>& RMSgrad, std::unique_ptr>& RMSdelta, const double decay_rate) { + double sum_of_delta = 0, max_delta = 0; + + //ヒストグラムを取ってみよう + std::map histgram; + static const double e = 0.1; + + for (int p1 = 0; p1 < PieceStateNum; p1++) { + for (int p2 = 0; p2 < PieceStateNum; p2++) { + double delta = std::sqrt(RMSdelta->pp[p1][p2] + e) / std::sqrt(RMSgrad->pp[p1][p2] + e) * grad->pp[p1][p2]; + + //四捨五入 + delta = std::round(delta); + + max_delta = std::max(max_delta, std::fabs(delta)); + + histgram[static_cast(delta)]++; + + //パラメータ更新 + pp[p1][p2] -= static_cast(delta); + + //合計いくら変わったか見るため + sum_of_delta += std::fabs(delta); + + //減衰させながら足す + RMSdelta->pp[p1][p2] = decay_rate * RMSdelta->pp[p1][p2] + (1 - decay_rate) * delta * delta; + } + for (int k = 0; k < 81; k++) { + double delta = std::sqrt(RMSdelta->kp[k][p1] + e) / std::sqrt(RMSgrad->kp[k][p1] + e) * grad->kp[k][p1]; + + //四捨五入 + delta = std::round(delta); + + max_delta = std::max(max_delta, std::abs(delta)); + + histgram[static_cast(delta)]++; + + //パラメータ更新 + kp[k][p1] -= static_cast(delta); + + //合計いくら変わったか見るため + sum_of_delta += std::fabs(delta); + + //減衰させながら足す + RMSdelta->kp[k][p1] = decay_rate * RMSdelta->kp[k][p1] + (1 - decay_rate) * delta * delta; + } + } + std::cout << "delta = " << sum_of_delta << ", max_delta_abs = " << max_delta << std::endl; + + for (auto e : histgram) { + std::cout << e.first << ":" << e.second << ", "; + } + std::cout << std::endl; +} + +template +void EvalParams::clear() { + for (int p1 = 0; p1 < PieceStateNum; p1++) { + for (int p2 = 0; p2 < PieceStateNum; p2++) { + pp[p1][p2] = 0; + } + for (int k = 0; k < 81; k++) { + kp[k][p1] = 0; + } + } +} + +template +inline void EvalParams::readFile(std::string file_name) { + std::ifstream ifs(file_name, std::ios::binary); + if (ifs.fail()) { + std::cerr << file_name << " cannot open (mode r)" << std::endl; + clear(); + return; + } + ifs.read(reinterpret_cast(&pp[0][0]), PieceStateNum * PieceStateNum * sizeof(T)); + ifs.read(reinterpret_cast(&kp[0][0]), 81 * PieceStateNum * sizeof(T)); +} + +template +inline void EvalParams::writeFile(std::string file_name) { + std::ofstream ofs(file_name, std::ios::binary | std::ios::trunc); + if (ofs.fail()) { + std::cerr << file_name << " cannot open (mode w)" << std::endl; + return; + } + ofs.write(reinterpret_cast(&pp[0][0]), PieceStateNum * PieceStateNum * sizeof(T)); + ofs.write(reinterpret_cast(&kp[0][0]), 81 * PieceStateNum * sizeof(T)); +} + +template +T EvalParams::abs() const { + T sum = 0; + for (int p = 0; p < PieceStateNum; p++) { + for (int k = 0; k < 81; k++) { + sum += std::abs(kp[k][p]); + } + for (int p2 = 0; p2 < PieceStateNum; p2++) { + sum += std::abs(pp[p][p2]); + } + } + return sum; +} + +template +inline T EvalParams::max_abs() const { + T max_val = INT_MIN; + for (int p = 0; p < PieceStateNum; p++) { + for (int k = 0; k < 81; k++) { + max_val = std::max(max_val, std::abs(kp[k][p])); + } + for (int p2 = 0; p2 < PieceStateNum; p2++) { + max_val = std::max(max_val, std::abs(pp[p][p2])); + } + } + return max_val; +} + +template +template +void EvalParams::forEach(Function f) { + for (int p = 0; p < PieceStateNum; p++) { + for (int k = 0; k < 81; k++) { + f(kp[k][p]); + } + for (int p2 = 0; p2 < PieceStateNum; p2++) { + f(pp[p][p2]); + } + } +} + +template +template +inline void EvalParams::copy(std::unique_ptr>& source) { + for (int p = 0; p < PieceStateNum; ++p) { + for (int k = 0; k < 81; ++k) { + kp[k][p] = static_cast(source->kp[k][p]); + } + for (int p2 = 0; p2 < PieceStateNum; ++p2) { + pp[p][p2] = static_cast(source->pp[p][p2]); + } + } +} + +template +template +inline void EvalParams::roundCopy(std::unique_ptr>& source) { + for (int p = 0; p < PieceStateNum; ++p) { + for (int k = 0; k < 81; ++k) { + kp[k][p] = static_cast(std::round(source->kp[k][p])); + } + for (int p2 = 0; p2 < PieceStateNum; ++p2) { + pp[p][p2] = static_cast(std::round(source->pp[p][p2])); + } + } +} + +template +inline EvalParams EvalParams::operator*(const double x) { + auto copy = *this; + copy.forEach([&](auto& value) { + value *= x; + }); + return copy; +} + +template +inline EvalParams EvalParams::operator+(const EvalParams rhs) { + auto copy = *this; + for (int p = 0; p < PieceStateNum; p++) { + for (int k = 0; k < 81; k++) { + copy.kp[k][p] += rhs.kp[k][p]; + } + for (int p2 = 0; p2 < PieceStateNum; p2++) { + copy.pp[p][p2] += rhs.pp[p][p2]; + } + } + return copy; +} + +template +inline EvalParams EvalParams::square() { + auto copy = *this; + copy.forEach([&](auto& value) { + value *= value; + }); + return copy; +} + +template +inline void EvalParams::calcRMS(std::unique_ptr>& grad, const double decay_rate) { + //*RMSgrad = *RMSgrad * DECAY_RATE + grad->square() * (1 - DECAY_RATE); + for (int p = 0; p < PieceStateNum; ++p) { + for (int k = 0; k < 81; ++k) { + kp[k][p] = kp[k][p] * decay_rate + grad->kp[k][p] * grad->kp[k][p] * (1 - decay_rate); + } + for (int p2 = 0; p2 < PieceStateNum; ++p2) { + pp[p][p2] = pp[p][p2] * decay_rate + grad->pp[p][p2] * grad->pp[p][p2] * (1 - decay_rate); + } + } +} + +template +inline void EvalParams::addSquare(std::unique_ptr>& grad) { + for (int p = 0; p < PieceStateNum; ++p) { + for (int k = 0; k < 81; ++k) { + kp[k][p] += grad->kp[k][p] * grad->kp[k][p]; + } + for (int p2 = 0; p2 < PieceStateNum; ++p2) { + pp[p][p2] += grad->pp[p][p2] * grad->pp[p][p2]; + } + } +} + +extern std::unique_ptr> eval_params; + +#endif // !EVAL_PARAMS_HPP \ No newline at end of file diff --git a/kaitei_WCSC28/hand.hpp b/kaitei_WCSC28/hand.hpp new file mode 100644 index 0000000..d41d31b --- /dev/null +++ b/kaitei_WCSC28/hand.hpp @@ -0,0 +1,78 @@ +#pragma once + +#ifndef HAND_HPP +#define HAND_HPP + +#include"piece.hpp" + +enum HandConst { + HAND_PAWN_SHIFT = 0, + HAND_LANCE_SHIFT = HAND_PAWN_SHIFT + 7, + HAND_KNIGHT_SHIFT = HAND_LANCE_SHIFT + 4, + HAND_SILVER_SHIFT = HAND_KNIGHT_SHIFT + 4, + HAND_GOLD_SHIFT = HAND_SILVER_SHIFT + 4, + HAND_BISHOP_SHIFT = HAND_GOLD_SHIFT + 4, + HAND_ROOK_SHIFT = HAND_BISHOP_SHIFT + 3, + + HAND_PAWN_MASK = 0b111111, + HAND_LANCE_MASK = 0b111 << HAND_LANCE_SHIFT, + HAND_KNIGHT_MASK = 0b111 << HAND_KNIGHT_SHIFT, + HAND_SILVER_MASK = 0b111 << HAND_SILVER_SHIFT, + HAND_GOLD_MASK = 0b111 << HAND_GOLD_SHIFT, + HAND_BISHOP_MASK = 0b11 << HAND_BISHOP_SHIFT, + HAND_ROOK_MASK = 0b11 << HAND_ROOK_SHIFT, + + NU = 0, +}; + +static int PieceToHandShift[] = { + 0, HAND_PAWN_SHIFT, HAND_LANCE_SHIFT, HAND_KNIGHT_SHIFT, HAND_SILVER_SHIFT, HAND_GOLD_SHIFT, HAND_BISHOP_SHIFT, HAND_ROOK_SHIFT +}; + +static int PieceToHandMask[] = { + 0, HAND_PAWN_MASK, HAND_LANCE_MASK, HAND_KNIGHT_MASK, HAND_SILVER_MASK, HAND_GOLD_MASK, HAND_BISHOP_MASK, HAND_ROOK_MASK, +}; + +class Hand { + //0000 0000 0000 0000 0000 0000 0011 1111 PAWN + //0000 0000 0000 0000 0000 0011 1000 0000 LANCE + //0000 0000 0000 0000 0011 1000 0000 0000 KNIGHT + //0000 0000 0000 0011 1000 0000 0000 0000 SILVER + //0000 0000 0011 1000 0000 0000 0000 0000 GOLD + //0000 0001 1000 0000 0000 0000 0000 0000 BISHOP + //0000 1100 0000 0000 0000 0000 0000 0000 ROOK + unsigned int hand_; +public: + //RXgN^ + Hand() : hand_(0) {} + Hand(int pawn, int lance, int knight, int silver, int gold, int bishop, int rook) : hand_(pawn << HAND_PAWN_SHIFT | lance << HAND_LANCE_SHIFT + | knight << HAND_KNIGHT_SHIFT | silver << HAND_SILVER_SHIFT | gold << HAND_GOLD_SHIFT | bishop << HAND_BISHOP_SHIFT | rook << HAND_ROOK_SHIFT) {} + + //̐Ԃ + inline int num(Piece p) const { + return ((hand_ & PieceToHandMask[kind(p)]) >> PieceToHandShift[kind(p)]); + } + + //capture(Piece^)󂯎Ď𑝂₷ + inline void add(Piece p) { + hand_ += 1 << PieceToHandShift[kind(p)]; + } + inline void sub(Piece p) { + hand_ -= 1 << PieceToHandShift[kind(p)]; + } + + //̂Ƃg + void set(Piece p, int num) { hand_ += num << PieceToHandShift[kind(p)]; } + + //zeroNA + void clear() { hand_ = 0; } + + //\ + void print() const { + for (Piece p = PAWN; p <= ROOK; p++) + if (num(p)) std::cout << PieceToStr[p] << num(p) << " "; + std::cout << std::endl; + } +}; + +#endif // !HAND_HPP diff --git a/kaitei_WCSC28/hash_entry.hpp b/kaitei_WCSC28/hash_entry.hpp new file mode 100644 index 0000000..86b57ab --- /dev/null +++ b/kaitei_WCSC28/hash_entry.hpp @@ -0,0 +1,34 @@ +#ifndef HASH_ENTRY_HPP +#define HASH_ENTRY_HPP + +#include"move.hpp" + +struct HashEntry{ + //ハッシュの値 + long long hash_val_; + + //その局面における(仮の)最善手 + //評価値込み + Move best_move_; + + //残り探索深さ + Depth depth_; + + //登録されているか + bool flag_; + + HashEntry() : + hash_val_(0), + best_move_(NULL_MOVE), + depth_(Depth(-1)), + flag_(false) {} + + void print() { + printf("hash_val_ = %llx\n", hash_val_); + printf("best_move_ = "); best_move_.printWithScore(); + printf("depth_ = %d\n", depth_); + printf("flag_ = %s\n", flag_ ? "YES" : "NO"); + } +}; + +#endif \ No newline at end of file diff --git a/kaitei_WCSC28/hash_table.cpp b/kaitei_WCSC28/hash_table.cpp new file mode 100644 index 0000000..cc3c3dc --- /dev/null +++ b/kaitei_WCSC28/hash_table.cpp @@ -0,0 +1,30 @@ +#include "hash_table.hpp" +#include "common.hpp" +#include + +HashEntry* HashTable::find(long long key) { + if (table_[key & key_mask_].flag_ == false) return nullptr; + if (table_[key & key_mask_].hash_val_ != key) return nullptr; + return &table_[key & key_mask_]; +} + +void HashTable::save(long long key, Move move, Score score, Depth depth) { + HashEntry* target = &table_[key & key_mask_]; + target->hash_val_ = key; + target->best_move_ = move; + target->depth_ = depth; + target->best_move_.score = score; + if (!target->flag_) { + target->flag_ = true; + hashfull_++; + } +} + +void HashTable::setSize(long long megabytes) { + long long bytes = megabytes * 1024 * 1024; + size_ = 1ull << MSB64(bytes / sizeof(HashEntry)); + age_ = 0; + key_mask_ = size_ - 1; + hashfull_ = 0; + table_.resize(size_); +} \ No newline at end of file diff --git a/kaitei_WCSC28/hash_table.hpp b/kaitei_WCSC28/hash_table.hpp new file mode 100644 index 0000000..c608474 --- /dev/null +++ b/kaitei_WCSC28/hash_table.hpp @@ -0,0 +1,40 @@ +#ifndef HASH_TABLE_HPP +#define HASH_TABLE_HPP + +#include"hash_entry.hpp" +#include +#include + +class Move; + +class HashTable{ +public: + HashTable() { setSize(64); } + HashEntry* find(long long key); + void save(long long key, Move move, Score score, Depth depth); + void setSize(long long megabytes); + double hashfull() { return static_cast(hashfull_) / static_cast(size_) * 1000.0; } + void clear() { + table_.clear(); + } +private: + //ハッシュエントリのvector:これが本体 + std::vector table_; + + //ハッシュテーブルの要素数 + //これtable.size()では? + //上位ソフトだといくつかのエントリをまとめて一つにしてるからこれが別に必要なんだろうか + //それにしてもそのまとめた数さえ保持しておけばいいんじゃないのか + size_t size_; + + //ハッシュキーからテーブルのインデックスを求めるためのマスク + size_t key_mask_; + + //使用済みエントリの数 + size_t hashfull_; + + //ハッシュテーブルに入っている情報の古さ + uint8_t age_; +}; + +#endif \ No newline at end of file diff --git a/kaitei_WCSC28/history.hpp b/kaitei_WCSC28/history.hpp new file mode 100644 index 0000000..e6db825 --- /dev/null +++ b/kaitei_WCSC28/history.hpp @@ -0,0 +1,63 @@ +#pragma once +#ifndef HISTORY_HPP +#define HISTORY_HPP + +#include"piece.hpp" +#include"square.hpp" +#include"move.hpp" +#include + +class History { +private: + Score table_[PieceNum][SquareNum]; +public: + void updateBetaCutMove(const Move move, const Depth depth) { + //std::cout << "_(depth = "<< depth / PLY << " :"; + //move.print(); + table_[move.subject()][move.to()] += static_cast(depth / PLY) * depth / PLY; + } + + void updateNonBetaCutMove(const Move move, const Depth depth) { + //std::cout << "_(depth = " << depth / PLY << " :"; + //move.print(); + table_[move.subject()][move.to()] -= static_cast(depth / PLY) * depth / PLY; + } + + Score operator[](const Move& move) const { + return table_[move.subject()][move.to()]; + } + + void print() { + std::priority_queue >> pq; + + for (int rank = Rank1; rank <= Rank9; rank++) { + for (int file = File9; file >= File1; file--) { + int sum = 0; + for (Piece p : PieceList) { + sum += table_[p][FRToSquare[file][rank]]; + pq.push({ table_[p][FRToSquare[file][rank]], {p, FRToSquare[file][rank]} }); + } + printf("%6d ", sum); + } + printf("\n"); + } + + printf("10\n"); + for (int i = 0; i < 10; i++) { + auto p = pq.top(); + pq.pop(); + std::cout << Piece(p.second.first) << " " << Square(p.second.second) << " " << p.first << std::endl; + } + } + + void clear() { + for (int piece = 0; piece < PieceNum; ++piece) { + for (int to = 0; to < SquareNum; ++to) { + table_[piece][to] = SCORE_ZERO; + } + } + } +}; + + +#endif // !HISTORY_HPP \ No newline at end of file diff --git a/kaitei_WCSC28/learn.cpp b/kaitei_WCSC28/learn.cpp new file mode 100644 index 0000000..284e7e5 --- /dev/null +++ b/kaitei_WCSC28/learn.cpp @@ -0,0 +1,544 @@ +#include"learn.hpp" +#include"position.hpp" +#include"searcher.hpp" +#include"load_game.hpp" +#include"eval_params.hpp" +#include"usi_options.hpp" +#include"thread.hpp" +#include +#include +#include +#include +#include +#include +#include + +extern USIOption usi_option; + +//1回パラメータを更新するまでの学習局数 +static int MINI_BATCH_SIZE; + +//学習する棋譜 +static std::vector games; + +//学習しているスレッドの数 +static unsigned int thread_num; + +//評価関数バイナリに書き込むときは排他制御必要だと思う +std::mutex MUTEX; + +//L1正則化項にかける係数 +static double L1_GRAD_COEFFICIENT; + +//勝率と勝敗の負の対数尤度項の係数 +static const double COEFFICIENT_OF_WIN_RATE = 10.0; + +//学習情報を書き込むファイル +static std::ofstream loss_history; + +static std::ofstream explain; + +//学習する際の深さ +static Depth learn_depth; + +enum Optimizer { + SGD, ADAGRAD, RMSPROP, ADADELTA +}; +static int optimizer_mode; + +//学習率 +static double learn_rate; + +//次に使う棋譜のid +static std::atomic grobal_game_id; + +//損失関数にsigmoidを使うかy = axを使うか +static int loss_function_mode; + +//シグモイド関数のゲイン +//どのくらいの値にすればいいのかはよくわからないけどとりあえずは http://woodyring.blog.so-net.ne.jp/2015-02-06-1 を参考に +static const double GAIN = 0.0274; + +//sigmoidのx = 0における微分値に合わせよう +//static const double linear_coefficient = GAIN / 4; +static const double linear_coefficient = 1.0; + +//学習用のdouble型パラメータ +static std::unique_ptr> parameters(new EvalParams); + +//勾配 +static std::unique_ptr> grad(new EvalParams); + +//勾配の平均(Root Mean Square):AdaGrad,RMSProp,AdaDeltaの時に使う.AdaGradの時は二乗の和だけど、まぁ別に用意するのも変な気がするので +static std::unique_ptr> RMSgrad; + +//パラメータの移動量の平均(Root Mean Square):AdaDeltaの時に使う +static std::unique_ptr> RMSdelta; + +//RMSProp,AdaDeltaの時の減衰指数 +static const double DECAY_RATE = 0.9; + +//学習した局面の数 +static std::atomic learned_position_num; + +//学習した棋譜の数 +//これがMINI_BATCH_SIZEに達するごとにパラメータを更新し、これを0に戻す +static std::atomic learned_games_num; + +//指し手が一致した局面の数 +static std::atomic succeeded_position_num; + +//損失 +static std::atomic Loss; + +void BonanzaMethod() { + std::string path; + std::cout << "学習する棋譜があるフォルダへのパス : "; + std::cin >> path; + + int games_size; + std::cout << "学習する棋譜の数 : "; + std::cin >> games_size; + + std::cout << "ミニバッチサイズ : "; + std::cin >> MINI_BATCH_SIZE; + + std::cout << "L1正則化項の係数 : "; + std::cin >> L1_GRAD_COEFFICIENT; + + //std::cout << "学習する際の深さ : "; + //std::cin >> learn_depth; + //深さは1じゃないと現実的な時間で終わらない + learn_depth = Depth(1); + + std::string optimizer_name; + while (true) { //Optimizerの選択 + std::cout << "Optimizer(SGD, AdaGrad, RMSProp, AdaDelta から選択):"; + std::cin >> optimizer_name; + if (optimizer_name == "SGD") { + optimizer_mode = SGD; + break; + } else if (optimizer_name == "AdaGrad") { + optimizer_mode = ADAGRAD; + break; + } else if (optimizer_name == "RMSProp") { + optimizer_mode = RMSPROP; + break; + } else if (optimizer_name == "AdaDelta") { + optimizer_mode = ADADELTA; + break; + } else { + std::cerr << "Optimizerは[SGD, AdaGrad, RMSProp, AdaDelta]から選択" << std::endl; + } + } + //Adadeltaでは学習率を設定しない + if (optimizer_mode == ADADELTA) { + //適当に1にでもしておく + learn_rate = 1; + } else { + std::cout << "学習率 : "; + std::cin >> learn_rate; + } + + //std::cout << "損失関数(0:sigmoid, 1:Linear): "; + //std::cin >> loss_function_mode; + //Linearで統一 + loss_function_mode = 1; + + std::cout << "スレッド数 : "; + std::cin >> thread_num; + + std::cout << "start BonanzaMethod" << std::endl; + + //これで待機してた探索用スレッドは終了してくれるはず + threads.clear(); + + grobal_game_id = 0; + + loss_history.open("loss_history.txt", std::ios::out); + + explain.open("explain.txt", std::ios::out); + + //評価関数ロード + eval_params->readFile(); + std::cout << "eval_params->printHistgram()" << std::endl; + parameters->copy(eval_params); + eval_params->printHistgram(); + + if (optimizer_mode == ADAGRAD || optimizer_mode == RMSPROP) { + //AdaGrad, RMSPropならRMSgradだけ準備する + RMSgrad.reset(new EvalParams()); + RMSgrad->readFile("RMSgrad.bin"); + std::cout << "RMSgrad->printHistgram()" << std::endl; + RMSgrad->printHistgram(); + } else if (optimizer_mode == ADADELTA) { + //AdaDeltaならRMSgradとRMSdeltaを準備する + RMSgrad.reset(new EvalParams()); + RMSgrad->readFile("RMSgrad.bin"); + std::cout << "RMSgrad->printHistgram()" << std::endl; + RMSgrad->printHistgram(); + + RMSdelta.reset(new EvalParams()); + RMSdelta->readFile("RMSdelta.bin"); + std::cout << "RMSdelta->printHistgram()" << std::endl; + RMSdelta->printHistgram(); + } + + //棋譜を読み込む + std::cout << "start load_games ..."; + games = load_games(path, games_size); + std::cout << " done" << std::endl; + + //棋譜シャッフル + std::random_device rd; + for (int i = 0; i < games.size(); i++) { + unsigned int j = rd() % games.size(); + std::swap(games[i], games[j]); + } + + //勾配初期化 + grad->clear(); + grad->addL1grad(*eval_params, L1_GRAD_COEFFICIENT); + + //学習情報の初期化 + Loss = L1_GRAD_COEFFICIENT * eval_params->abs(); + learned_games_num = 0; + learned_position_num = 0; + succeeded_position_num = 0; + + //学習パラメータの設定を書きだしておく + explain << "最初のabs : " << eval_params->abs() << ", 学習する棋譜の数 : " << games.size() << ", ミニバッチサイズ : " << MINI_BATCH_SIZE << ", L1正則化項の係数 : " << L1_GRAD_COEFFICIENT + << ", 学習探索の深さ : " << learn_depth << ", 学習率 : " << learn_rate << ", 損失関数 : " << loss_function_mode << ", スレッド数 : " << thread_num << ", オプティマイザ : " << optimizer_name << std::endl; + + //lossファイルの各行が何を表すかを示しておく + loss_history << "SumLoss, L1_NORM, loss_average, Percentage of correct move, eval_params.abs()" << std::endl; + loss_history << std::fixed; + + //探索が止まらないようにする + shared_data.stop_signal = false; + + thread_num = std::min(std::max(1u, thread_num), std::thread::hardware_concurrency()); + std::cout << "使用するスレッド数 : " << thread_num << std::endl; + std::cout << std::fixed; + + std::vector learn_threads(thread_num); + for (unsigned int i = 0; i < thread_num; ++i) { + learn_threads[i] = std::thread(BonanzaMethodSlave, i); + } + for (auto& t : learn_threads) { + t.join(); + } + + loss_history.close(); + exit(0); +} + +void BonanzaMethodSlave(const int thread_id) { + //探索クラス:スレーブとして作れば停止はstop_signalだけなので止まらない + Searcher searcher(Searcher::Role::SLAVE); + + //時間初期化 + auto start = std::chrono::system_clock::now(); + + //使用する棋譜のidは各スレッドで共有(std::atomic) + while (true) { + //局面を初期化(初期局面でいいはず) + Position position; + + int game_id = grobal_game_id++; + + if (game_id >= games.size()) { + break; + } + + //棋譜 + const Game& game = games[game_id]; + + //最後の方は王手ラッシュで意味がないと判断して0.8をかけた手数で打ち切り + int num_moves = static_cast(game.moves.size() * 0.8); + + //局面を1手ごと進めながら各局面について損失,勾配を計算する + for (int j = 0; j < num_moves; j++) { + //全指し手生成 + std::vector move_list = position.generateAllMoves(); + + if (move_list.size() == 0) { + //おそらく詰みの局面には行かないはずだけど…… + break; + } + + if (move_list.size() == 1) { + //可能な指し手が1つしかないなら学習に適してないのでスキップ + //一手進める + position.doMove(game.moves[j]); + continue; + } + + //教師となる指し手 + const Move teacher_move = game.moves[j]; + + //全ての指し手に評価値をつけてPVを設定する + //この評価値は手番側から見たものだから、教師手が最大になってほしい + std::vector>> move_and_pv; + for (Move& move : move_list) { + position.doMove(move); + searcher.setRoot(position); + searcher.resetPVTable(); + move.score = -searcher.search(position, MIN_SCORE, MAX_SCORE, learn_depth, 0); + position.undo(); + std::vector pv = searcher.pv(); + if (pv.size() == 0) { + //ときどき0になってることは確認できるけど、とくにどうしようもなくダメということもないので表示しなくていいかぁ + //std::cout << "pv.size() == 0でした" << std::endl; + } + pv.insert(pv.begin(), move); + move_and_pv.emplace_back(move, pv); + } + + //ソート + std::sort(move_and_pv.begin(), move_and_pv.end(), std::greater>>()); + + //教師手を探す + int teacher_index = -1; + for (unsigned int k = 0; k < move_and_pv.size(); k++) { + if (move_and_pv[k].first == teacher_move) { + teacher_index = k; + break; + } + } + if (teacher_index == -1) { + //生成された合法手の中に教師手が含まれなかったということ + //飛・角・歩とかの不成はここに来る + //学習には適さないのでスキップする + position.doMove(teacher_move); + continue; + } else if (teacher_index == 0) { + //一致したということ + learned_position_num++; + succeeded_position_num++; + position.doMove(teacher_move); + continue; + } + + //教師手を先頭に持ってくる + std::swap(move_and_pv[0], move_and_pv[teacher_index]); + + //教師手のpvを棋譜のものに変更する + //これやらないほうがいいのでは + //for (auto l = 0; l < move_and_pv[0].second.size(); ++l) { + // move_and_pv[0].second[l] = game.moves[j + l]; + //} + + //教師手以外をスコアでソート + std::sort(move_and_pv.begin() + 1, move_and_pv.end(), std::greater>>()); + + //Lossの本体:第1項 教師手との差 + //教師手のリーフノードの特徴量を用意 +#if DEBUG + printf("教師手のリーフノード----------------------------\n"); +#endif + Color teacher_leaf_color; + Features teacher_leaf_element = position.makeEvalElements(move_and_pv[0].second, teacher_leaf_color); + + unsigned int fail_move_num = 0; + double progress = std::min(static_cast(j) / 100.0, 0.85); + Score margin = Score(10) + (int)(256 * progress); + //各パラメータについて勾配を計算 + //各指し手についてリーフに出てくる特徴量に対応する勾配を増やし,その分だけ教師手リーフに出てくる特徴量に対応する勾配を減らす + for (unsigned int m = 1; m < move_and_pv.size(); m++) { + if (move_and_pv[m].first.score + margin >= move_and_pv[0].first.score) { + //どれだけの手が教師手より良いと誤って判断されたのかカウントする + fail_move_num++; + } else { + break; + } + } + if (fail_move_num > 0) { + for (unsigned int m = 1; m < move_and_pv.size(); m++) { + auto diff = move_and_pv[m].first.score + margin - move_and_pv[0].first.score; + if (diff < 0) { //教師手の方がスコアが良かったということ + break; + } + //atomicだと+=演算子使えないっぽい + Loss = Loss + loss_function(diff) / fail_move_num; +#if DEBUG + printf("\nダメ手のリーフノード\n"); + printf("スコアの差 = %d\n", move_and_pv[m].first.score - move_and_pv[0].first.score); +#endif + //勾配を変化させる量は損失を微分した値 / ダメだった手の数 + double delta = d_loss_function(diff) / fail_move_num; + if (position.color() == WHITE) { + delta = -delta; + } + + //リーフノードの特徴量 + Color leaf_color; + Features leaf_element = position.makeEvalElements(move_and_pv[m].second, leaf_color); + + MUTEX.lock(); + //高く評価しすぎた手について勾配を増やす + grad->updateGradient(leaf_element, delta); + //教師手について勾配を減らす + grad->updateGradient(teacher_leaf_element, -delta); + MUTEX.unlock(); + } + } + + //Lossの本体:第2項 勝率と勝敗の負の対数尤度 + //予測勝率をp, 1回の試行で先手が勝った回数をy(ようは先手が勝ったかどうか)とすると + //尤度 = 1Cw * p^y + (1 - p)^(1 - y) + //最小化問題に帰着させたいので負にする、累乗は扱いにくいので対数 + + if (false) { + + //いわゆるponanza定数 + const double GAIN_OF_WIN_RATE = 1 / 600.0; + + //この局面を探索した評価値 + searcher.setRoot(position); + searcher.resetPVTable(); + int score = searcher.NegaAlphaBeta(position, MIN_SCORE, MAX_SCORE, Depth(3), 0); + + //先手から見た勝率に変換したいので先手から見たスコアにする + score *= position.color() == BLACK ? 1 : -1; + + //あまりにも大きい評価値がでる局面は学習に適してないだろう + if (std::abs(score) >= 10000) break; + + //std::printf("score = %d, ", score); + std::vector pv = searcher.pv(); + double p = sigmoid(score, GAIN_OF_WIN_RATE); + int y = game.winner; + Loss = Loss + COEFFICIENT_OF_WIN_RATE * (-(y * std::log(p) + (1 - y) * std::log(1.0 - p))); + + //勾配を計算 + double grad_of_win_rate = COEFFICIENT_OF_WIN_RATE * GAIN_OF_WIN_RATE * (p - y); +#if DEBUG + printf("grad_of_win_rate = %f\n", grad_of_win_rate); +#endif + + //リーフノードで出る特徴量に勾配を足しこむ + Color leaf_color; + Features feature_for_win_rate = position.makeEvalElements(pv, leaf_color); + //下のlead_colorに応じて変えないといけない + MUTEX.lock(); + grad->updateGradient(feature_for_win_rate, grad_of_win_rate); + MUTEX.unlock(); + } + + //学習した局面を増やす + learned_position_num++; + + //一手進める + position.doMove(game.moves[j]); + + } //ここで1局分が終わる + + if ((game_id + 1) <= 50 || (game_id + 1) % 10 == 0) { + auto time = std::chrono::system_clock::now(); + auto elapsed = std::chrono::duration_cast(time - start); + MUTEX.lock(); + printf("finish %5d games, 経過時間:%3ld時間%3ld分, ", game_id + 1, elapsed.count() / 60, elapsed.count() % 60); + int r_minutes = static_cast((double)(games.size() - (game_id + 1)) / (game_id + 1) * elapsed.count()); + printf("残り時間:%3d時間%3d分\n", r_minutes / 60, r_minutes % 60); + MUTEX.unlock(); + } + + //必要局数に達していたらパラメータ更新 + if (++learned_games_num == MINI_BATCH_SIZE) { + std::unique_lock lock(MUTEX); + update(); + } + } + + //最後にあまりが残っていると思う + std::cout << "learned_games_num = " << learned_games_num << std::endl; + + //必要局数の8割くらいやってたらもったいないからパラメータ更新する + if (learned_games_num >= MINI_BATCH_SIZE * 0.8) { + update(); + } + + std::cout << "BonanzaMethod finish" << std::endl; +} + +void update() { + //double型の学習用重みをアップデート + if (optimizer_mode == SGD) { + parameters->updateParamsSGD(grad, learn_rate); + } else if (optimizer_mode == ADAGRAD) { + //2乗和を足す + RMSgrad->addSquare(grad); + //重み更新 + parameters->updateParamsAdaGrad(grad, RMSgrad, learn_rate); + //RMSgradを書き出す + RMSgrad->writeFile("RMSgrad.bin"); + } else if (optimizer_mode == RMSPROP) { + //減衰させながら足す + RMSgrad->calcRMS(grad, DECAY_RATE); + //更新の式はAdaGradと同じ + parameters->updateParamsAdaGrad(grad, RMSgrad, learn_rate); + //RMSgradを書き出す + RMSgrad->writeFile("RMSgrad.bin"); + } else if (optimizer_mode == ADADELTA) { + //減衰させながら足す + RMSgrad->calcRMS(grad, DECAY_RATE); + //重み更新:内部でRMSdeltaも更新される + parameters->updateParamsAdaDelta(grad, RMSgrad, RMSdelta, DECAY_RATE); + //RMS系を書き出しておく + RMSgrad->writeFile("RMSgrad.bin"); + RMSdelta->writeFile("RMSdelta.bin"); + } + //int型の計算用重みにコピーして書き出し + eval_params->copy(parameters); + eval_params->writeFile(); + + //学習情報を出力 + std::cout << "SumLoss = " << Loss << ", L1_PENALTY = " << L1_GRAD_COEFFICIENT * eval_params->abs() << ", loss_average = " << Loss / learned_position_num + << ", Percentage of correct move = " << (double)succeeded_position_num / learned_position_num * 100 << ", grad.max_abs() = " << grad->max_abs() + << ", eval_params->abs() = " << eval_params->abs() << std::endl; + eval_params->printHistgram(); + + loss_history << Loss << " \t" << L1_GRAD_COEFFICIENT * eval_params->abs() << "\t" << Loss / learned_position_num + << "\t" << (double)succeeded_position_num / learned_position_num * 100 << "\t" << eval_params->abs() << std::endl; + + //勾配初期化 + grad->clear(); + grad->addL1grad(*eval_params, L1_GRAD_COEFFICIENT); + + //学習情報を0に戻す + Loss = L1_GRAD_COEFFICIENT * eval_params->abs(); + learned_games_num = 0; + learned_position_num = 0; + succeeded_position_num = 0; +} + +double loss_function(int score_diff) { + //どれかを利用する + if (loss_function_mode == 0) { + //sigmoid + return sigmoid(score_diff, GAIN); + } else if (loss_function_mode == 1) { + //線形 + return linear_coefficient * score_diff; + } else { + //ここには来ないはず + assert(false); + return 0.0; + } +} + +double d_loss_function(int score_diff) { + //どれかを利用する + if (loss_function_mode == 0) { + //sigmoid + return d_sigmoid(score_diff, GAIN); + } else if (loss_function_mode == 1) { + //線形 + return linear_coefficient; + } else { + //ここには来ないはず + assert(false); + return 0.0; + } +} \ No newline at end of file diff --git a/kaitei_WCSC28/learn.hpp b/kaitei_WCSC28/learn.hpp new file mode 100644 index 0000000..9780323 --- /dev/null +++ b/kaitei_WCSC28/learn.hpp @@ -0,0 +1,21 @@ +#pragma once + +#ifndef LEARN_HPP +#define LEARN_HPP + +#include"piece.hpp" +#include"common.hpp" +#include"piece_state.hpp" + +double loss_function(int score_diff); +double d_loss_function(int score_diff); + +//sigmoidを取って評価値の差から更新する量を計算する +void BonanzaMethod(); + +//並列化するとき各スレッドが実行する関数 +void BonanzaMethodSlave(const int thread_id); + +void update(); + +#endif // !LEARN_HPP diff --git a/kaitei_WCSC28/learn_self.cpp b/kaitei_WCSC28/learn_self.cpp new file mode 100644 index 0000000..291ad7e --- /dev/null +++ b/kaitei_WCSC28/learn_self.cpp @@ -0,0 +1,325 @@ +#include"learn_self.hpp" +#include"position.hpp" +#include"searcher.hpp" +#include"eval_params.hpp" +#include"thread.hpp" +#include"position_for_learn.h" +#include + +//񉻃Xbh +static std::vector slave_threads; + +//~job`TCY +//ǂꂭ炢̂悭킩Ȃ +static int mini_batch_size; + +//wK +static double learn_rate; + +static std::mutex MUTEX; + +static std::atomic stop_learn; + +static std::chrono::time_point start_time; + +static std::unique_ptr> learning_parameters(new EvalParams); + +static std::ofstream learn_log; + +static unsigned long long sum_learned_games = 0, sum_learned_pos = 0; + +static std::atomic win_num(0), lose_num(0), consecutive_lose_num(0); + +static double threshold, win_sum = 0.5, deep_coefficient; + +static int teacher_depth; + +void learnSelf() { + std::cout << "start learnSelf()" << std::endl; + + start_time = std::chrono::steady_clock::now(); + + //őҋ@ĂTpXbh͏IĂ͂ + threads.clear(); + + //]֐ǂݍ + eval_params->readFile(); + //p[^wKp̂̂ɃRs[Ă + learning_parameters->copy(eval_params); + + //IvVwKɐݒ + shared_data.limit_msec = LLONG_MAX; + usi_option.byoyomi_margin = 0LL; + + std::cout << "mini_batch_size = "; + std::cin >> mini_batch_size; + std::cout << "learn_rate = "; + std::cin >> learn_rate; + std::cout << "threshold(0.0~1.0) = "; + std::cin >> threshold; + std::cout << "tǖʂT[ = "; + std::cin >> teacher_depth; + std::cout << "[T̕ɂW(0.0~1.0) = "; + std::cin >> deep_coefficient; + + unsigned int thread_num; + std::cout << "thread_num = "; + std::cin >> thread_num; + //Œł1,ōłg鐔܂ + thread_num = std::min(std::max(1u, thread_num), std::thread::hardware_concurrency()); + printf("gpXbh = %u\n", thread_num); + + stop_learn = false; + + learn_log.open("learn_self.log"); + learn_log << "mini_batch_size " << mini_batch_size << " learn_rate " << learn_rate << " threshold " << threshold << std::endl; + + //Xbh̍쐬 + slave_threads.resize(thread_num); + for (unsigned int i = 0; i < thread_num; i++) { + slave_threads[i] = std::thread(learnSelfSlave, i); + } + + while (true) { + std::string input; + std::cin >> input; + if (input == "stop") { + stop_learn = true; + break; + } + } + for (unsigned int i = 0; i < thread_num; i++) { + slave_threads[i].join(); + printf("%2dXbhjoin\n", i); + } + + learn_log.close(); + std::cout << "finish learnSelf()" << std::endl; +} + +void learnSelfSlave(int id) { + //_Ɏw萔_Ɍ肷 + std::random_device seed_gen; + std::default_random_engine engine(seed_gen()); + std::uniform_int_distribution dist(2, 20); + + //games[j]firstMove̕(), second͌(1Ȃ菟,0Ȃ菟) + std::vector, double>> games(mini_batch_size); + + //TNX + //history傫(?) IɊmۂȂƃX^bNI[o[t[ɂȂ + std::unique_ptr searcher(new Searcher(Searcher::MAIN)); + + //z + std::unique_ptr> grad(new EvalParams); + + for (unsigned long long i = 0; !stop_learn; i++) { + games.clear(); + games.resize(mini_batch_size); + + double win_point = 0; + + //߂ + for (int j = 0; j < mini_batch_size; j++) { + Position pos; + std::unique_ptr pos_standard(new PositionForLearn); + pos_standard->ptr.reset(new EvalParams()); + pos_standard->ptr->readFile(); + + if (j == 0) { + std::cout << "eval_params->abs() = " << eval_params->abs() << ", ptr->abs() = " << pos_standard->ptr->abs() << std::endl; + } + + games[j].first.clear(); + + unsigned int random_turn = dist(engine); + while (true) { + //ĵƂpos + Move best_move = ((pos.turn_number() % 2) == (j % 2) ? searcher->thinkForGenerateLearnData(pos, Depth(teacher_depth) * (int)PLY, random_turn) + : searcher->thinkForGenerateLearnData(*pos_standard, Depth(teacher_depth) * (int)PLY, random_turn)); + + if (stop_learn) { //~VOi̊mF + return; + } + + if (best_move == NULL_MOVE || (best_move.score != MIN_SCORE && best_move.score <= -4000)) { //NULL_MOVE͓ + if (pos.color() == BLACK) { + games[j].second = 0.0; + } else { + games[j].second = 1.0; + } + break; + } + + if (pos.turn_number() % 2 == 1 && best_move.score != MIN_SCORE) { + best_move.score = -best_move.score; + } + + games[j].first.push_back(best_move); + if (!pos.isLegalMove(best_move)) { + pos.printForDebug(); + best_move.printWithScore(); + } + pos.doMove(best_move); + pos_standard->doMove(best_move); + + Score dummy; + if (pos.isRepeating(dummy)) { // + games[j].second = 0.5; + break; + } + + if (pos.turn_number() >= 256) { //H + //]lɊÂʂƂ + //games[j].second = sigmoid((double)best_move.score, 1.0 / 600); + ////őł0.25~0.75炢ɃLbv + //games[j].second = std::max(0.25, std::min(0.75, games[j].second)); + //̕ǂ + games[j].second = 0.5; + break; + } + } + //ǖڂpos,łȂƂpos_standard + if (j % 2 == 0) { + win_point += games[j].second; + } else { + win_point += 1 - games[j].second; + } + + //if ((j + 1) % 1 == 0) { + // printf("(%2d) %3dǐI : %3u, result = %.2f, random_turn = %3u\n", id, j + 1, pos.turn_number(), games[j].second, random_turn); + //} + } + win_point /= mini_batch_size; + + //wK + grad->clear(); + std::pair sum_loss = { 0.0, 0.0 }; + unsigned int learn_pos_num = 0; + for (int j = 0; j < mini_batch_size; j++) { + auto game = games[j]; + const int finish_turn_num = static_cast(game.first.size()); + + Position pos; + + for (Move m : game.first) { + if (m.score == MIN_SCORE) { //_[uƂƂȂ̂ŊwK͂Ȃ + if (!pos.isLegalMove(m)) { + pos.printForDebug(); + m.printWithScore(); + } + + pos.doMove(m); + continue; + } + + if (stop_learn) { //~VOi̊mF + return; + } + + if (isMatedScore(m.score)) { //l݂̒l甲 + break; + } + + //󂢒T̕]l + Move shallow_best = (searcher->thinkForGenerateLearnData(pos, Depth(0), 0)); + + auto pv = searcher->pv(); + int move_num = 0; + for (auto pv_move : pv) { + pos.doMove(pv_move); + move_num++; + } + + Score shallow_score = pos.score(); + + //̌vZ + auto loss = calcLoss(shallow_score, m.score, game.second); + sum_loss.first += loss.first; + sum_loss.second += loss.second; + learn_pos_num++; + + //zω + double delta = calcGrad(shallow_score, m.score, game.second, pos.turn_number(), finish_turn_num); + + //zXV + grad->updateGradient(pos.makeEvalElements(), delta); + + for (int num = 0; num < move_num; num++) { + pos.undo(); + } + + if (!pos.isLegalMove(m)) { + pos.printForDebug(); + m.printWithScore(); + } + + pos.doMove(m); + } + } + + MUTEX.lock(); + //p[^XV + //doublê̂ŊwKĂʏg(int)ɃRs[ + learning_parameters->updateParamsSGD(grad, learn_rate); + eval_params->roundCopy(learning_parameters); + + //o + eval_params->writeFile("tmp.bin"); + + //ɂ‚Ďwړς + win_sum = 0.8 * win_sum + 0.2 * win_point; + + sum_learned_games += mini_batch_size; + + auto now_time = std::chrono::steady_clock::now(); + auto elapsed = std::chrono::duration_cast(now_time - start_time); + const int seconds = static_cast(elapsed.count()); + assert(learn_pos_num > 0); + sum_learned_pos += learn_pos_num; + printf("(%2d) %5lldI(v%9lld, %9lldǖ):%3d%2d%2dbo, = (%.5f, %.5f)\n", id, i + 1, sum_learned_games, sum_learned_pos, seconds / 3600, seconds % 3600 / 60, seconds % 60, + sum_loss.first / learn_pos_num, sum_loss.second / learn_pos_num); + learn_log << sum_loss.first / learn_pos_num << " " << sum_loss.second / learn_pos_num << std::endl; + + //ȑ΋ǂ̌ʂmF + printf("(%2d) ̏ : %3.1f, Ϗ : %3.1f ", id, win_point * 100.0, win_sum * 100); + + if (win_sum >= threshold) { + //zȂt@Cɏ + eval_params->writeFile(); + win_sum = 0.3; + consecutive_lose_num = 0; + win_num++; + } else { + //z + std::cout << ++consecutive_lose_num << "As "; + lose_num++; + } + std::cout << win_num << "" << lose_num << "s" << std::endl; + + MUTEX.unlock(); + } +} + +double calcGrad(Score shallow_score, Score deep_score, double result, int curr_turn_num, int finish_turn_num) { + double shallow_win_rate = sigmoid((double)shallow_score, 1.0 / 600); + double deep_win_rate = sigmoid((double)deep_score, 1.0 / 600); + return deep_coefficient * (shallow_win_rate - deep_win_rate) + (1 - deep_coefficient) * (shallow_win_rate - result); +} + +std::pair calcLoss(Score shallow_score, Score deep_score, double result) { + std::pair loss; + double shallow_win_rate = sigmoid((double)shallow_score, 1.0 / 600); + double deep_win_rate = sigmoid((double)deep_score, 1.0 / 600); + + constexpr double epsilon = 0.000001; + loss.first = (-deep_win_rate * std::log(shallow_win_rate + epsilon) - (1.0 - deep_win_rate) * std::log(1.0 - shallow_win_rate + epsilon)); + loss.second = (-result * std::log(shallow_win_rate + epsilon) - (1.0 - result) * std::log(1.0 - shallow_win_rate + epsilon)); + + if (std::isnan(loss.first) || std::isnan(loss.second)) { + printf("shallow_score = %d, deep_score = %d, result = %f\n", shallow_score, deep_score, result); + assert(false); + } + + return loss; +} \ No newline at end of file diff --git a/kaitei_WCSC28/learn_self.hpp b/kaitei_WCSC28/learn_self.hpp new file mode 100644 index 0000000..55090ee --- /dev/null +++ b/kaitei_WCSC28/learn_self.hpp @@ -0,0 +1,7 @@ +#pragma once +#include"types.hpp" + +void learnSelf(); +void learnSelfSlave(int id); +double calcGrad(Score shallow_score, Score deep_score, double result, int curr_turn_num, int finish_turn_num); +std::pair calcLoss(Score shallow_score, Score deep_score, double result); \ No newline at end of file diff --git a/kaitei_WCSC28/load_game.cpp b/kaitei_WCSC28/load_game.cpp new file mode 100644 index 0000000..b8b2e4a --- /dev/null +++ b/kaitei_WCSC28/load_game.cpp @@ -0,0 +1,174 @@ +#include"load_game.hpp" +#include"position.hpp" +#include +#include +#include +#include +#include + +namespace sys = std::experimental::filesystem; + +static std::unordered_map CSAstringToPiece = { + { "FU", PAWN }, + { "KY", LANCE }, + { "KE", KNIGHT }, + { "GI", SILVER }, + { "KI", GOLD }, + { "KA", BISHOP }, + { "HI", ROOK }, + { "OU", KING }, + { "TO", PAWN_PROMOTE }, + { "NY", LANCE_PROMOTE }, + { "NK", KNIGHT_PROMOTE }, + { "NG", SILVER_PROMOTE }, + { "UM", BISHOP_PROMOTE }, + { "RY", ROOK_PROMOTE }, +}; + +Game load_game_from_csa(sys::path p) { + Position pos; + Game game; + game.path = p; + std::ifstream ifs(p); + std::string buf; + int move_counter = 1; + while (getline(ifs, buf)) { + if (buf[0] == '\'') continue; + if (buf[0] != '+' && buf[0] != '-') continue; + if (buf.size() == 1) continue; + + //上の分岐によりMoveだけ残る + Square from = FRToSquare[buf[1] - '0'][buf[2] - '0']; + Square to = FRToSquare[buf[3] - '0'][buf[4] - '0']; + //移動駒の種類 + Piece subject = CSAstringToPiece[buf.substr(5, 2)]; + //手番を設定 + subject = (pos.color() == BLACK ? toBlack(subject) : toWhite(subject)); + bool isDrop = (from == WALL00); + + //CSAのフォーマットから、動くものが成済なのにfromにある駒が不成の場合、成る手 + bool isPromote = ((subject & PROMOTE) && !(pos.on(from) & PROMOTE)); + if (isPromote) { + subject = static_cast(subject & ~PROMOTE); + } + Move move(to, from, isDrop, isPromote, subject); + move = pos.transformValidMove(move); + + if (!pos.isLegalMove(move)) { + pos.printForDebug(); + move.print(); + pos.isLegalMove(move); + std::cout << game.path << std::endl; + assert(false); + } + game.moves.push_back(move); + pos.doMove(move); + + } + game.winner = ~pos.color(); + return game; +} + +std::vector load_games(std::string path, int num) { + const sys::path dir(path); + std::vector games; + for (sys::directory_iterator p(dir); p != sys::directory_iterator(); p++) { + games.push_back(load_game_from_csa(p->path())); + if (--num == 0) + break; + } + return games; +} + +void clean_games(std::string path) { + const sys::path dir(path); + for (sys::directory_iterator p(dir); p != sys::directory_iterator(); p++) { + std::ifstream ifs(p->path()); + std::string buf; + int move_counter = 0; + double black_rate = 0, white_rate = 0; + while (getline(ifs, buf)) { + //名前読み込み + //if (buf[0] == 'N') { + // if (buf[1] == '+') { + // std::cout << "先手名前:" << buf.substr(2) << std::endl; + // continue; + // } else if (buf[1] == '-') { + // std::cout << "後手名前:" << buf.substr(2) << std::endl; + // continue; + // } else { + // assert(false); + // } + //} + + //レート読み込み + //2800より小さかったら削除 + if (buf.find("'black_rate") < buf.size()) { + //std::cout << "先手レート:" << buf.substr(buf.rfind(':') + 1) << std::endl; + black_rate = std::stod(buf.substr(buf.rfind(':') + 1)); + continue; + } else if (buf.find("'white_rate") < buf.size()) { + //std::cout << "後手レート:" << buf.substr(buf.rfind(':') + 1) << std::endl; + white_rate = std::stod(buf.substr(buf.rfind(':') + 1)); + continue; + } + + //summaryが投了以外のものは削除 + if (buf.find("summary") < buf.size()) { + if (buf.find("toryo") > buf.size()) { + ifs.close(); + std::cout << p->path().string().c_str() << " "; + if (remove(p->path().string().c_str()) == 0) { + std::cout << "削除成功" << std::endl; + } else { + std::cout << "失敗" << std::endl; + } + goto END; + } + continue; + } + + //std::cout << buf << std::endl; + if (buf[0] == '\'') continue; + if (buf[0] != '+' && buf[0] != '-') continue; + if (buf.size() == 1) continue; + move_counter++; + } + + //printf("black_rate = %f, white_rate = %f\n", black_rate, white_rate); + if (black_rate < 2800) { + ifs.close(); + std::cout << p->path().string().c_str() << " "; + if (remove(p->path().string().c_str()) == 0) { + std::cout << "削除成功" << std::endl; + } else { + std::cout << "失敗" << std::endl; + } + continue; + } + if (white_rate < 2800) { + ifs.close(); + std::cout << p->path().string().c_str() << " "; + if (remove(p->path().string().c_str()) == 0) { + std::cout << "削除成功" << std::endl; + } else { + std::cout << "失敗" << std::endl; + } + continue; + } + + //手数が50より小さいものはおかしい気がするので削除 + if (move_counter < 50) { + ifs.close(); + std::cout << p->path().string().c_str() << " "; + if (remove(p->path().string().c_str()) == 0) { + std::cout << "削除成功" << std::endl; + } else { + std::cout << "失敗" << std::endl; + } + continue; + } + + END:; + } +} diff --git a/kaitei_WCSC28/load_game.hpp b/kaitei_WCSC28/load_game.hpp new file mode 100644 index 0000000..221b0d1 --- /dev/null +++ b/kaitei_WCSC28/load_game.hpp @@ -0,0 +1,30 @@ +#pragma once + +#ifndef LOAD_GAME_HPP +#define LOAD_GAME_HPP + +#include"move.hpp" +#include +#include +#include + +struct Game { + //初期局面:どうせ普通の初期局面から始まった棋譜しか使わないし要らない気がする + std::string sfen_start_pos; + + //指し手のvector + std::vector moves; + + //ファイル名を表示するのにしか使ってない + std::experimental::filesystem::path path; + + //先手が勝ちなら1,後手が勝ちなら0 + //学習で使うときの都合でそうなっているけど、そう加工するのは使うときにするべきであって、ここではColor型で保持するのが筋かもしれない + int winner; +}; + +Game load_game_from_csa(std::experimental::filesystem::path p); +std::vector load_games(std::string path, int num); +void clean_games(std::string path); + +#endif // !LOAD_GAME_HPP diff --git a/kaitei_WCSC28/main.cpp b/kaitei_WCSC28/main.cpp new file mode 100644 index 0000000..1f7c65c --- /dev/null +++ b/kaitei_WCSC28/main.cpp @@ -0,0 +1,23 @@ +#include"usi.hpp" +#include"piece_state.hpp" +#include"usi_options.hpp" +#include"test.hpp" +#include"bitboard.hpp" + +std::unique_ptr> eval_params(new EvalParams); + +int main() +{ + initPieceToStateIndex(); + initInvPieceState(); + + initCanMove(); + initCanJump(); + + initConDirToOppositeDir(); + + Bitboard::init(); + + USI usi; + usi.loop(); +} \ No newline at end of file diff --git a/kaitei_WCSC28/move.hpp b/kaitei_WCSC28/move.hpp new file mode 100644 index 0000000..8bfcb86 --- /dev/null +++ b/kaitei_WCSC28/move.hpp @@ -0,0 +1,135 @@ +#ifndef MOVE_HPP +#define MOVE_HPP + +#include"square.hpp" +#include"piece.hpp" +#include"types.hpp" +#include +#include + +enum MoveConst { + //0000 0000 0000 0000 0000 0000 0111 1111 to + //0000 0000 0000 0000 0011 1111 1000 0000 from + //0000 0000 0000 0000 0100 0000 0000 0000 drop + //0000 0000 0000 0000 1000 0000 0000 0000 promote + //0000 0000 1111 1111 0000 0000 0000 0000 subject + //1111 1111 0000 0000 0000 0000 0000 0000 capture + MOVE_TO_SHIFT = 0, + MOVE_FROM_SHIFT = 7, + MOVE_DROP_SHIFT = 14, + MOVE_PROMOTE_SHIFT = 15, + MOVE_SUBJECT_SHIFT = 16, + MOVE_CAPTURE_SHIFT = 24, + MOVE_TO_MASK = 0b1111111, + MOVE_FROM_MASK = MOVE_TO_MASK << MOVE_FROM_SHIFT, + MOVE_DROP_MASK = 1 << MOVE_DROP_SHIFT, + MOVE_PROMOTE_MASK = 1 << MOVE_PROMOTE_SHIFT, + MOVE_SUBJECT_MASK = 0xff << MOVE_SUBJECT_SHIFT, + MOVE_CAPTURE_MASK = 0xff << MOVE_CAPTURE_SHIFT, +}; + +class Move { +public: + //コンストラクタ + Move() : move(0), score(Score(0)) {} + Move(Square to, Square from) : move(from << MOVE_FROM_SHIFT | to << MOVE_TO_SHIFT), score(Score(0)) {} + Move(Square to, Square from, bool isDrop) : move(isDrop << MOVE_DROP_SHIFT | from << MOVE_FROM_SHIFT | to << MOVE_TO_SHIFT) {} + Move(Square to, Square from, bool isDrop, bool isPromote) : move(isPromote << MOVE_PROMOTE_SHIFT | isDrop << MOVE_DROP_SHIFT | from << MOVE_FROM_SHIFT | to << MOVE_TO_SHIFT) {} + Move(Square to, Square from, bool isDrop, bool isPromote, Piece subject) : move(subject << MOVE_SUBJECT_SHIFT | isPromote << MOVE_PROMOTE_SHIFT | isDrop << MOVE_DROP_SHIFT | from << MOVE_FROM_SHIFT | to << MOVE_TO_SHIFT) {} + Move(Square to, Square from, bool isDrop, bool isPromote, Piece subject, Piece capture) : move(capture << MOVE_CAPTURE_SHIFT | subject << MOVE_SUBJECT_SHIFT | isPromote << MOVE_PROMOTE_SHIFT | isDrop << MOVE_DROP_SHIFT | from << MOVE_FROM_SHIFT | to << MOVE_TO_SHIFT) {} + + //表示 + void print() const { + std::cout << SquareToFile[to()] << SquareToRank[to()]; + std::cout << PieceToStr[subject()]; + if (isPromote()) fprintf(stdout, "成"); + else if (isDrop()) fprintf(stdout, "打"); + std::cout << "(" << SquareToFile[from()] << SquareToRank[from()] << ") "; + if (capture() != EMPTY) std::cout << "capture:" << PieceToStr[capture()] << std::endl; + else std::cout << std::endl; + } + + void printWithScore() const { + print(); + std::cout << "score:" << score << std::endl; + } + + //要素を取り出す関数ら + inline Square to() const { return static_cast(move & MOVE_TO_MASK); } + inline Square from() const { return static_cast((move & MOVE_FROM_MASK) >> MOVE_FROM_SHIFT); } + inline bool isDrop() const { return (move & MOVE_DROP_MASK) != 0; } + inline bool isPromote() const { return (move & MOVE_PROMOTE_MASK) != 0; } + inline Piece subject() const { return static_cast((move & MOVE_SUBJECT_MASK) >> MOVE_SUBJECT_SHIFT); } + inline Piece capture() const { return static_cast((move & MOVE_CAPTURE_MASK) >> MOVE_CAPTURE_SHIFT); } + + //演算子オーバーロード + bool operator==(const Move &rhs) const { return (move == rhs.move); } + bool operator!=(const Move &rhs) const { return !(*this == rhs); } + bool operator<(const Move &rhs) const { return (score < rhs.score); } + bool operator>(const Move &rhs) const { return (score > rhs.score); } + + inline bool isQuiet() const { + return (capture() == EMPTY); + } + + //探索時にSeacherクラスから気軽にアクセスできるようpublicにおいてるけど + int move; + Score score; +}; + +//駒を打つ手 +inline Move dropMove(Square to, Piece p) { return Move(to, WALL00, true, false, p, EMPTY); } + +//駒を動かす手を引数として、それの成った動きを返す +inline Move promotiveMove(Move non_promotive_move) { return Move(non_promotive_move.to(), non_promotive_move.from(), false, true, non_promotive_move.subject(), non_promotive_move.capture()); } + +//比較用に置いとくといいかも? +const Move NULL_MOVE; + +//sfen形式で出力するオーバーロード +inline std::ostream& operator<<(std::ostream& os, Move m) { + if (m.isDrop()) { + os << PieceToSfenStr[kind(m.subject())][0] << '*' << static_cast(SquareToFile[m.to()]) << static_cast(SquareToRank[m.to()] + 'a' - 1); + } else { + os << static_cast(SquareToFile[m.from()]) + << static_cast(SquareToRank[m.from()] + 'a' - 1) + << static_cast(SquareToFile[m.to()]) + << static_cast(SquareToRank[m.to()] + 'a' - 1); + if (m.isPromote()) + os << '+'; + } + return os; +} + +//これコンストラクタとかで書いた方がいい気がするけどうまく書き直せなかった +//まぁ動けばいいのかなぁ +static Move stringToMove(std::string input) { + static std::unordered_map charToPiece = { + { 'P', PAWN }, + { 'L', LANCE }, + { 'N', KNIGHT }, + { 'S', SILVER }, + { 'G', GOLD }, + { 'B', BISHOP }, + { 'R', ROOK }, + }; + Square to, from; + bool isDrop, isPromote; + Piece subject; + if ('A' <= input[0] && input[0] <= 'Z') { //持ち駒を打つ手 + to = FRToSquare[input[2] - '0'][input[3] - 'a' + 1]; + isDrop = true; + isPromote = false; + subject = charToPiece[input[0]]; + return dropMove(to, subject); + } else { //盤上の駒を動かす手 + from = FRToSquare[input[0] - '0'][input[1] - 'a' + 1]; + to = FRToSquare[input[2] - '0'][input[3] - 'a' + 1]; + isDrop = false; + if (input.size() == 5 && input[4] == '+') isPromote = true; + else isPromote = false; + return Move(to, from, isDrop, isPromote, EMPTY, EMPTY); + } +} + +#endif diff --git a/kaitei_WCSC28/move_picker.cpp b/kaitei_WCSC28/move_picker.cpp new file mode 100644 index 0000000..a900fb0 --- /dev/null +++ b/kaitei_WCSC28/move_picker.cpp @@ -0,0 +1,224 @@ +#include"move_picker.hpp" +#include"position.hpp" +#include"history.hpp" +#include"eval_params.hpp" +#include +#include + +extern int piece_value[]; + +enum Stage { + MAIN_SEARCH_START, + MAIN_SEARCH_TT, + MAIN_SEARCH_CAPTURE, + MAIN_SEARCH_KILLERS, + MAIN_SEARCH_NON_CAPTURE, + MAIN_SEARCH_END, + + ROOT_NODE, + + QSEARCH_START, + QSEARCH_TT, + QSEARCH_CAPTURE, + QSEARCH_END, +}; + +MovePicker::MovePicker(const Position& pos, const Move ttMove, const Depth depth, const History& history, const Move killers[2]) + : pos_(pos), tt_move_(ttMove), depth_(depth), history_(history) { + stage_ = MAIN_SEARCH_START - 1; + moves_ = new Move[MAX_MOVE_LIST_SIZE]; + + //܂‚͐擪w + cur_ = moves_; + end_ = moves_; + + killer_moves_[0] = killers[0]; + killer_moves_[1] = killers[1]; +} + +MovePicker::MovePicker(const Position& pos, const Move ttMove, const Depth depth, const History& history) + : pos_(pos), tt_move_(ttMove), depth_(depth), history_(history) { + stage_ = QSEARCH_START - 1; + moves_ = new Move[MAX_MOVE_LIST_SIZE]; + + //܂‚͐擪w + cur_ = moves_; + end_ = moves_; +} + +Move MovePicker::nextMove() { + //NULL_MOVEԂIƂ + while (true) { + while (cur_ == end_) { + //̃JeS̎𐶐 + generateNextStage(); + } + + switch (stage_) { + case MAIN_SEARCH_START: + case MAIN_SEARCH_TT: + return *(cur_++); + case MAIN_SEARCH_CAPTURE: + //tt_moveƂ̏d폜 + if (*cur_ != tt_move_) { + return *(cur_++); + } + cur_++; + break; + case MAIN_SEARCH_KILLERS: + return *(cur_++); + case MAIN_SEARCH_NON_CAPTURE: + //tt_move, killer_movesƂ̏d폜 + if (*cur_ != tt_move_ && *cur_ != killer_moves_[0] && *cur_ != killer_moves_[1]) { + return *(cur_++); + } + cur_++; + break; + case MAIN_SEARCH_END: + return *(cur_++); + + + //Î~T + case QSEARCH_START: + case QSEARCH_TT: + return *(cur_++); + case QSEARCH_CAPTURE: + //tt_moveƂ̏d폜 + if (*cur_ != tt_move_) { + return *(cur_++); + } + cur_++; + break; + case QSEARCH_END: + return *(cur_++); + } + } +} + +void MovePicker::generateNextStage() { + switch (++stage_) { + //ʏTĂ΂Ƃ͂ + case MAIN_SEARCH_START: + if (pos_.isKingChecked()) { + //肪ĂSčŌNULL_MOVElj + pos_.generateEvasionMovesBB(end_); + *(end_++) = NULL_MOVE; + } + break; + case MAIN_SEARCH_TT: + //tt_moveݒ肷 + //ttň@肪͎̂dȂ̂Ȃ +#if DEBUG + if (tt_move_ != NULL_MOVE && !pos_.isLegalMove(tt_move_)) { + pos_.print(); + Position tmp(pos_); + tmp.printAllMoves(); + tt_move_.print(); + assert(false); + } +#endif + if (tt_move_ != NULL_MOVE && pos_.isLegalMove(tt_move_)) { + *(end_++) = tt_move_; + } + break; + case MAIN_SEARCH_CAPTURE: + pos_.generateCaptureMovesBB(end_); + scoreCapture(); + break; + case MAIN_SEARCH_KILLERS: + if (killer_moves_[0] != NULL_MOVE && pos_.isLegalMove(killer_moves_[0]) && killer_moves_[0] != tt_move_) + *(end_++) = killer_moves_[0]; + if (killer_moves_[1] != NULL_MOVE && pos_.isLegalMove(killer_moves_[1]) && killer_moves_[1] != tt_move_) + *(end_++) = killer_moves_[1]; + break; + case MAIN_SEARCH_NON_CAPTURE: + pos_.generateNonCaptureMovesBB(end_); + scoreNonCapture(); + break; + case MAIN_SEARCH_END: + *(end_++) = NULL_MOVE; + //MovePickerNULL_MOVEԂ悤ɂȂ̂Ŏw萶I + break; + + + //Î~TĂ΂Ƃ͂ + case QSEARCH_START: + if (pos_.isKingChecked()) { + //肪ĂSčŌNULL_MOVElj + pos_.generateEvasionMovesBB(end_); + *(end_++) = NULL_MOVE; + } + break; + case QSEARCH_TT: + //tt_moveݒ肷 + //ttň@肪͎̂dȂ̂Ȃ +#if DEBUG + if (tt_move_ != NULL_MOVE && !pos_.isLegalMove(tt_move_)) { + pos_.print(); + Position tmp(pos_); + tmp.printAllMoves(); + tt_move_.print(); + assert(false); + } +#endif + if (tt_move_ != NULL_MOVE && pos_.isLegalMove(tt_move_) && tt_move_.capture() != EMPTY) { + *(end_++) = tt_move_; + } + break; + case QSEARCH_CAPTURE: + if (depth_ == 0) { + //[傤0̂ƂׂĐ + pos_.generateCaptureMovesBB(end_); + } else { + //[ȂԂ肾𐶐 + pos_.generateRecaptureMovesToBB(pos_.lastMove().to(), end_); + } + scoreCapture(); + break; + case QSEARCH_END: + *(end_++) = NULL_MOVE; + //MovePickerNULL_MOVEԂ悤ɂȂ̂Ŏw萶I + break; + } +} + +void MovePicker::scoreCapture() { + for (auto itr = cur_; itr != end_; ++itr) { + if (pos_.isMoreControl(itr->to())) { //GȔ + itr->score = static_cast(piece_value[itr->capture()]); + if (itr->score < 0) { //̋Captureƕ̒lt + itr->score *= -1; + } + } else { // + itr->score = static_cast(-piece_value[itr->subject()]); + if (itr->score > 0) { //̋ŋCaptureƐ̒l‚Ă܂ + itr->score *= -1; + } + } + } + std::sort(cur_, end_, std::greater()); +} + +void MovePicker::scoreNonCapture() { + for (auto itr = cur_; itr != end_; ++itr) { + itr->score = Score(0); + if (itr->isPromote()) { + if (pieceToColor(itr->subject()) == BLACK) { + itr->score += piece_value[promote(itr->subject())] - piece_value[itr->subject()]; + } else { + itr->score -= piece_value[promote(itr->subject())] - piece_value[itr->subject()]; + } + } + itr->score += history_[*itr]; + + //KP̒lp̂͂ǂȂ񂩂 + //ς肿ƎキȂĂH(v) + //if (kind(itr->subject()) != KING && !itr->isDrop()) { + // itr->score += eval_params->kp[SquareToNum[pos_.kingSquare(BLACK)]][pieceState(itr->subject(), itr->to())] + // - eval_params->kp[SquareToNum[pos_.kingSquare(BLACK)]][pieceState(itr->subject(), itr->from())]; + // itr->score += eval_params->kp[SquareToNum[InvSquare[pos_.kingSquare(WHITE)]]][invPieceState(pieceState(itr->subject(), itr->to()))] + // - eval_params->kp[SquareToNum[InvSquare[pos_.kingSquare(WHITE)]]][invPieceState(pieceState(itr->subject(), itr->from()))]; + //} + } + std::sort(cur_, end_, std::greater()); +} \ No newline at end of file diff --git a/kaitei_WCSC28/move_picker.hpp b/kaitei_WCSC28/move_picker.hpp new file mode 100644 index 0000000..8afedc3 --- /dev/null +++ b/kaitei_WCSC28/move_picker.hpp @@ -0,0 +1,59 @@ +#pragma once + +#include"move.hpp" +#include + +//enum Depth; +class Position; +class History; +extern const int MAX_MOVE_LIST_SIZE; + +class MovePicker { +private: + Move* begin() { return moves_; } + Move* end() { return end_; } + + const Position& pos_; + + Move killer_moves_[2]; + //Move countermove_; + Depth depth_; + Move tt_move_; + + //ProbCutp̎w萶ɗpAO̎wŕߊlꂽ̉l + //int threshold; + + //w萶̒iK + int stage_; + + //ɕԂAꂽw̖ABadCapturȅI[ + //Move *cur_, *end_, *endBadCaptures_; + Move *cur_, *end_; + + Move *moves_; + + const History& history_; + +public: + //ʏTĂ΂ۂ̃RXgN^ + MovePicker(const Position& pos, const Move ttMove, const Depth depth, const History& history, const Move killers[2]); + + //Î~TĂ΂ۂ̃RXgN^ + MovePicker(const Position& pos, const Move ttMove, const Depth depth, const History& history); + + ~MovePicker() { + delete[] moves_; + } + Move nextMove(); + void generateNextStage(); + void scoreCapture(); + void scoreNonCapture(); + + void printAllMoves() { + for (auto itr = cur_; itr != end_; itr++) { + itr->printWithScore(); + } + } +}; + +extern int PieceToHistory; \ No newline at end of file diff --git a/kaitei_WCSC28/piece.cpp b/kaitei_WCSC28/piece.cpp new file mode 100644 index 0000000..4c7bc45 --- /dev/null +++ b/kaitei_WCSC28/piece.cpp @@ -0,0 +1,155 @@ +#include"piece.hpp" +#include + +std::unordered_map PieceToStr = { + { PAWN, "歩" }, + { LANCE, "香" }, + { KNIGHT, "桂" }, + { SILVER, "銀" }, + { GOLD, "金" }, + { BISHOP, "角" }, + { ROOK, "飛" }, + { BLACK_PAWN, "先手歩" }, + { BLACK_LANCE, "先手香" }, + { BLACK_KNIGHT, "先手桂" }, + { BLACK_SILVER, "先手銀" }, + { BLACK_GOLD, "先手金" }, + { BLACK_BISHOP, "先手角" }, + { BLACK_ROOK, "先手飛車" }, + { BLACK_KING, "先手玉" }, + { BLACK_PAWN_PROMOTE, "先手と" }, + { BLACK_LANCE_PROMOTE, "先手成香" }, + { BLACK_KNIGHT_PROMOTE, "先手成桂" }, + { BLACK_SILVER_PROMOTE, "先手成銀" }, + { BLACK_BISHOP_PROMOTE, "先手馬" }, + { BLACK_ROOK_PROMOTE, "先手竜" }, + { WHITE_PAWN, "後手歩" }, + { WHITE_LANCE, "後手香" }, + { WHITE_KNIGHT, "後手桂" }, + { WHITE_SILVER, "後手銀" }, + { WHITE_GOLD, "後手金" }, + { WHITE_BISHOP, "後手角" }, + { WHITE_ROOK, "後手飛車" }, + { WHITE_KING, "後手玉" }, + { WHITE_PAWN_PROMOTE, "後手と" }, + { WHITE_LANCE_PROMOTE, "後手成香" }, + { WHITE_KNIGHT_PROMOTE, "後手成桂" }, + { WHITE_SILVER_PROMOTE, "後手成銀" }, + { WHITE_BISHOP_PROMOTE, "後手馬" }, + { WHITE_ROOK_PROMOTE, "後手竜" }, +}; + +std::unordered_map PieceToSfenStr = { + { EMPTY, " " }, + { PAWN, "P" }, + { LANCE, "L" }, + { KNIGHT, "N" }, + { SILVER, "S" }, + { GOLD, "G" }, + { BISHOP, "B" }, + { ROOK, "R" }, + { BLACK_PAWN, " P" }, + { BLACK_LANCE, " L" }, + { BLACK_KNIGHT, " N" }, + { BLACK_SILVER, " S" }, + { BLACK_GOLD, " G" }, + { BLACK_BISHOP, " B" }, + { BLACK_ROOK, " R" }, + { BLACK_KING, " K" }, + { BLACK_PAWN_PROMOTE, "+P" }, + { BLACK_LANCE_PROMOTE, "+L" }, + { BLACK_KNIGHT_PROMOTE, "+N" }, + { BLACK_SILVER_PROMOTE, "+S" }, + { BLACK_BISHOP_PROMOTE, "+B" }, + { BLACK_ROOK_PROMOTE, "+R" }, + { WHITE_PAWN, " p" }, + { WHITE_LANCE, " l" }, + { WHITE_KNIGHT, " n" }, + { WHITE_SILVER, " s" }, + { WHITE_GOLD, " g" }, + { WHITE_BISHOP, " b" }, + { WHITE_ROOK, " r" }, + { WHITE_KING, " k" }, + { WHITE_PAWN_PROMOTE, "+p" }, + { WHITE_LANCE_PROMOTE, "+l" }, + { WHITE_KNIGHT_PROMOTE, "+n" }, + { WHITE_SILVER_PROMOTE, "+s" }, + { WHITE_BISHOP_PROMOTE, "+b" }, + { WHITE_ROOK_PROMOTE, "+r" }, +}; + +const std::array PieceList{ + BLACK_PAWN, + BLACK_LANCE, + BLACK_KNIGHT, + BLACK_SILVER, + BLACK_GOLD, + BLACK_BISHOP, + BLACK_ROOK, + BLACK_KING, + BLACK_PAWN_PROMOTE, + BLACK_LANCE_PROMOTE, + BLACK_KNIGHT_PROMOTE, + BLACK_SILVER_PROMOTE, + BLACK_BISHOP_PROMOTE, + BLACK_ROOK_PROMOTE, + WHITE_PAWN, + WHITE_LANCE, + WHITE_KNIGHT, + WHITE_SILVER, + WHITE_GOLD, + WHITE_BISHOP, + WHITE_ROOK, + WHITE_KING, + WHITE_PAWN_PROMOTE, + WHITE_LANCE_PROMOTE, + WHITE_KNIGHT_PROMOTE, + WHITE_SILVER_PROMOTE, + WHITE_BISHOP_PROMOTE, + WHITE_ROOK_PROMOTE, +}; + +const std::array, 2> ColoredPieceList{ { + { + BLACK_PAWN, + BLACK_LANCE, + BLACK_KNIGHT, + BLACK_SILVER, + BLACK_GOLD, + BLACK_BISHOP, + BLACK_ROOK, + BLACK_KING, + BLACK_PAWN_PROMOTE, + BLACK_LANCE_PROMOTE, + BLACK_KNIGHT_PROMOTE, + BLACK_SILVER_PROMOTE, + BLACK_BISHOP_PROMOTE, + BLACK_ROOK_PROMOTE + }, + { + WHITE_PAWN, + WHITE_LANCE, + WHITE_KNIGHT, + WHITE_SILVER, + WHITE_GOLD, + WHITE_BISHOP, + WHITE_ROOK, + WHITE_KING, + WHITE_PAWN_PROMOTE, + WHITE_LANCE_PROMOTE, + WHITE_KNIGHT_PROMOTE, + WHITE_SILVER_PROMOTE, + WHITE_BISHOP_PROMOTE, + WHITE_ROOK_PROMOTE + } +}}; + +const std::array, 2> ColoredJumpPieceList {{ + { BLACK_LANCE, BLACK_BISHOP, BLACK_ROOK }, + { WHITE_LANCE, WHITE_BISHOP, WHITE_ROOK } +}}; + +std::ostream& operator<<(std::ostream& os, const Piece piece) { + os << PieceToStr[piece]; + return os; +} \ No newline at end of file diff --git a/kaitei_WCSC28/piece.hpp b/kaitei_WCSC28/piece.hpp new file mode 100644 index 0000000..0348d9a --- /dev/null +++ b/kaitei_WCSC28/piece.hpp @@ -0,0 +1,118 @@ +#pragma once + +#ifndef PIECE_HPP +#define PIECE_HPP + +#include +#include +#include +#include +#include +#include +#include"types.hpp" + +enum Piece{ + PROMOTE_BIT = 5, + BLACK_BIT = 6, + WHITE_BIT = 7, + WALL_BIT = 8, + + EMPTY = 0, //0000 0000 + PAWN = 1, //0000 0001 + LANCE = 2, //0000 0010 + KNIGHT = 3, //0000 0011 + SILVER = 4, //0000 0100 + GOLD = 5, //0000 0101 + BISHOP = 6, //0000 0110 + ROOK = 7, //0000 0111 + KING = 8, //0000 1000 + PIECE_KIND_MASK = 15, //0000 1111 + PROMOTE = 1 << (PROMOTE_BIT - 1), //0001 0000 16 + PAWN_PROMOTE = PAWN + PROMOTE, //0001 0001 17 + LANCE_PROMOTE = LANCE + PROMOTE, //0001 0010 18 + KNIGHT_PROMOTE = KNIGHT + PROMOTE, //0001 0011 19 + SILVER_PROMOTE = SILVER + PROMOTE, //0001 0100 20 + BISHOP_PROMOTE = BISHOP + PROMOTE, //0001 0110 22 + ROOK_PROMOTE = ROOK + PROMOTE, //0001 0111 23 + BLACK_FLAG = 1 << (BLACK_BIT - 1), //0010 0000 32 + BLACK_PAWN = PAWN + BLACK_FLAG, //0010 0001 33 + BLACK_LANCE = LANCE + BLACK_FLAG, //0010 0010 34 + BLACK_KNIGHT = KNIGHT + BLACK_FLAG, //0010 0011 35 + BLACK_SILVER = SILVER + BLACK_FLAG, //0010 0100 36 + BLACK_GOLD = GOLD + BLACK_FLAG, //0010 0101 37 + BLACK_BISHOP = BISHOP + BLACK_FLAG, //0010 0110 38 + BLACK_ROOK = ROOK + BLACK_FLAG, //0010 0111 39 + BLACK_KING = KING + BLACK_FLAG, //0010 1000 40 + BLACK_PAWN_PROMOTE = PAWN_PROMOTE + BLACK_FLAG, //0011 0001 49 + BLACK_LANCE_PROMOTE = LANCE_PROMOTE + BLACK_FLAG, //0011 0010 50 + BLACK_KNIGHT_PROMOTE = KNIGHT_PROMOTE + BLACK_FLAG, //0011 0011 51 + BLACK_SILVER_PROMOTE = SILVER_PROMOTE + BLACK_FLAG, //0011 0100 52 + BLACK_BISHOP_PROMOTE = BISHOP_PROMOTE + BLACK_FLAG, //0011 0110 54 + BLACK_ROOK_PROMOTE = ROOK_PROMOTE + BLACK_FLAG, //0011 0111 55 + WHITE_FLAG = 1 << (WHITE_BIT - 1), //0100 0000 64 + WHITE_PAWN = PAWN + WHITE_FLAG, //0100 0001 65 + WHITE_LANCE = LANCE + WHITE_FLAG, //0100 0010 66 + WHITE_KNIGHT = KNIGHT + WHITE_FLAG, //0100 0011 67 + WHITE_SILVER = SILVER + WHITE_FLAG, //0100 0100 68 + WHITE_GOLD = GOLD + WHITE_FLAG, //0100 0101 69 + WHITE_BISHOP = BISHOP + WHITE_FLAG, //0100 0110 70 + WHITE_ROOK = ROOK + WHITE_FLAG, //0100 0111 71 + WHITE_KING = KING + WHITE_FLAG, //0100 1000 72 + WHITE_PAWN_PROMOTE = PAWN_PROMOTE + WHITE_FLAG, //0101 0001 81 + WHITE_LANCE_PROMOTE = LANCE_PROMOTE + WHITE_FLAG, //0101 0010 82 + WHITE_KNIGHT_PROMOTE = KNIGHT_PROMOTE + WHITE_FLAG, //0101 0011 83 + WHITE_SILVER_PROMOTE = SILVER_PROMOTE + WHITE_FLAG, //0101 0100 84 + WHITE_BISHOP_PROMOTE = BISHOP_PROMOTE + WHITE_FLAG, //0101 0110 86 + WHITE_ROOK_PROMOTE = ROOK_PROMOTE + WHITE_FLAG, //0101 0111 87 + PieceNum, + WALL = 1 << (WALL_BIT), //1000 0000 +}; + +extern std::unordered_map PieceToStr; +extern std::unordered_map PieceToSfenStr; + +inline Piece operator++(Piece &p, int) { return p = static_cast(p + 1); } +inline Piece operator|(Piece lhs, Piece rhs) { return Piece(int(lhs) | int(rhs)); } +inline int operator<<(Piece p, int shift) { return static_cast(p) << shift; } + +static Piece ColorToFlag[ColorNum] = { BLACK_FLAG, WHITE_FLAG }; + +inline static Color operator~(Color c) { return (c == BLACK) ? WHITE : BLACK; } + +extern const std::array PieceList; +extern const std::array, 2> ColoredPieceList; +extern const std::array, 2> ColoredJumpPieceList; + +inline Piece kind(Piece p) { return static_cast(p & PIECE_KIND_MASK); } +inline Piece promote(Piece p) { return static_cast(p | PROMOTE); } +inline Color pieceToColor(Piece p) { if (p & BLACK_FLAG) return BLACK; else if (p & WHITE_FLAG) return WHITE; else return ColorNum; } +inline Piece toBlack(Piece p) { return static_cast(p | BLACK_FLAG); } +inline Piece toWhite(Piece p) { return static_cast(p | WHITE_FLAG); } +inline Piece coloredPiece(Color c, Piece p) { + return (c == BLACK ? toBlack(p) : toWhite(p)); +} +inline bool isJumper(Piece p) { + return (kind(p) == LANCE && !(p & PROMOTE_BIT)) || kind(p) == BISHOP || kind(p) == ROOK; +} + +inline Piece oppositeColor(Piece p) { + int result(p); + if (pieceToColor(p) == BLACK) { + //BLACKのフラグを消して + result &= ~BLACK_FLAG; + //WHITEのフラグを立てる + result |= WHITE_FLAG; + } else if (pieceToColor(p) == WHITE) { + //BLACKのフラグを消して + result &= ~WHITE_FLAG; + //WHITEのフラグを立てる + result |= BLACK_FLAG; + } else { + assert(false); + } + return Piece(result); +} + +std::ostream& operator<<(std::ostream&, const Piece piece); + +#endif \ No newline at end of file diff --git a/kaitei_WCSC28/piece_state.cpp b/kaitei_WCSC28/piece_state.cpp new file mode 100644 index 0000000..12839c9 --- /dev/null +++ b/kaitei_WCSC28/piece_state.cpp @@ -0,0 +1,213 @@ +#include"piece_state.hpp" +#include"square.hpp" + +PieceState PieceToStateIndex[PieceNum]; +PieceState invPieceStateIndex[PieceStateNum]; + +//PieceStateIndex[piece] = そのpieceのSQ11におけるPieceStateが返るように初期化 +void initPieceToStateIndex() { + PieceToStateIndex[PAWN] = black_hand_pawn; + PieceToStateIndex[LANCE] = black_hand_lance; + PieceToStateIndex[KNIGHT] = black_hand_knight; + PieceToStateIndex[SILVER] = black_hand_silver; + PieceToStateIndex[GOLD] = black_hand_gold; + PieceToStateIndex[BISHOP] = black_hand_bishop; + PieceToStateIndex[ROOK] = black_hand_rook; + PieceToStateIndex[BLACK_PAWN] = black_pawn; + PieceToStateIndex[BLACK_LANCE] = black_lance; + PieceToStateIndex[BLACK_KNIGHT] = black_knight; + PieceToStateIndex[BLACK_SILVER] = black_silver; + PieceToStateIndex[BLACK_GOLD] = black_gold; + PieceToStateIndex[BLACK_BISHOP] = black_bishop; + PieceToStateIndex[BLACK_ROOK] = black_rook; + PieceToStateIndex[BLACK_PAWN_PROMOTE] = black_gold; + PieceToStateIndex[BLACK_LANCE_PROMOTE] = black_gold; + PieceToStateIndex[BLACK_KNIGHT_PROMOTE] = black_gold; + PieceToStateIndex[BLACK_SILVER_PROMOTE] = black_gold; + PieceToStateIndex[BLACK_BISHOP_PROMOTE] = black_horse; + PieceToStateIndex[BLACK_ROOK_PROMOTE] = black_dragon; + PieceToStateIndex[WHITE_PAWN] = white_pawn; + PieceToStateIndex[WHITE_LANCE] = white_lance; + PieceToStateIndex[WHITE_KNIGHT] = white_knight; + PieceToStateIndex[WHITE_SILVER] = white_silver; + PieceToStateIndex[WHITE_GOLD] = white_gold; + PieceToStateIndex[WHITE_BISHOP] = white_bishop; + PieceToStateIndex[WHITE_ROOK] = white_rook; + PieceToStateIndex[WHITE_PAWN_PROMOTE] = white_gold; + PieceToStateIndex[WHITE_LANCE_PROMOTE] = white_gold; + PieceToStateIndex[WHITE_KNIGHT_PROMOTE] = white_gold; + PieceToStateIndex[WHITE_SILVER_PROMOTE] = white_gold; + PieceToStateIndex[WHITE_BISHOP_PROMOTE] = white_horse; + PieceToStateIndex[WHITE_ROOK_PROMOTE] = white_dragon; +} + +PieceState PieceStateIndex[] = { + black_hand_pawn, + white_hand_pawn, + black_hand_lance, + white_hand_lance, + black_hand_knight, + white_hand_knight, + black_hand_silver, + white_hand_silver, + black_hand_gold, + white_hand_gold, + black_hand_bishop, + white_hand_bishop, + black_hand_rook, + white_hand_rook, + black_pawn, + white_pawn, + black_lance, + white_lance, + black_knight, + white_knight, + black_silver, + white_silver, + black_gold, + white_gold, + black_bishop, + white_bishop, + black_rook, + white_rook, + black_horse, + white_horse, + black_dragon, + white_dragon, + square_end, + PieceStateNum, +}; + +void initInvPieceState() { + for (int i = 0; i < 18; ++i) { + invPieceStateIndex[black_hand_pawn + i] = PieceState(white_hand_pawn + i); + invPieceStateIndex[white_hand_pawn + i] = PieceState(black_hand_pawn + i); + } + + for (int i = 0; i < 4; ++i) { + invPieceStateIndex[black_hand_lance + i] = PieceState(white_hand_lance + i); + invPieceStateIndex[black_hand_knight + i] = PieceState(white_hand_knight + i); + invPieceStateIndex[black_hand_silver + i] = PieceState(white_hand_silver + i); + invPieceStateIndex[black_hand_gold + i] = PieceState(white_hand_gold + i); + + invPieceStateIndex[white_hand_lance + i] = PieceState(black_hand_lance + i); + invPieceStateIndex[white_hand_knight + i] = PieceState(black_hand_knight + i); + invPieceStateIndex[white_hand_silver + i] = PieceState(black_hand_silver + i); + invPieceStateIndex[white_hand_gold + i] = PieceState(black_hand_gold + i); + } + + for (int i = 0; i < 2; ++i) { + invPieceStateIndex[black_hand_bishop + i] = PieceState(white_hand_bishop + i); + invPieceStateIndex[black_hand_rook + i] = PieceState(white_hand_rook + i); + + invPieceStateIndex[white_hand_bishop + i] = PieceState(black_hand_bishop + i); + invPieceStateIndex[white_hand_rook + i] = PieceState(black_hand_rook + i); + } + + for (int i = 0; i < 81; ++i) { + invPieceStateIndex[black_pawn + i] = PieceState(white_pawn + 80 - i); + invPieceStateIndex[black_lance + i] = PieceState(white_lance + 80 - i); + invPieceStateIndex[black_knight + i] = PieceState(white_knight + 80 - i); + invPieceStateIndex[black_silver + i] = PieceState(white_silver + 80 - i); + invPieceStateIndex[black_gold + i] = PieceState(white_gold + 80 - i); + invPieceStateIndex[black_bishop + i] = PieceState(white_bishop + 80 - i); + invPieceStateIndex[black_rook + i] = PieceState(white_rook + 80 - i); + invPieceStateIndex[black_horse + i] = PieceState(white_horse + 80 - i); + invPieceStateIndex[black_dragon + i] = PieceState(white_dragon + 80 - i); + + invPieceStateIndex[white_pawn + i] = PieceState(black_pawn + 80 - i); + invPieceStateIndex[white_lance + i] = PieceState(black_lance + 80 - i); + invPieceStateIndex[white_knight + i] = PieceState(black_knight + 80 - i); + invPieceStateIndex[white_silver + i] = PieceState(black_silver + 80 - i); + invPieceStateIndex[white_gold + i] = PieceState(black_gold + 80 - i); + invPieceStateIndex[white_bishop + i] = PieceState(black_bishop + 80 - i); + invPieceStateIndex[white_rook + i] = PieceState(black_rook + 80 - i); + invPieceStateIndex[white_horse + i] = PieceState(black_horse + 80 - i); + invPieceStateIndex[white_dragon + i] = PieceState(black_dragon + 80 - i); + } +} + +std::ostream & operator<<(std::ostream & os, const PieceState ps) { + static std::unordered_map dictionary = { + { black_hand_pawn, "先手持ち歩" }, + { black_hand_lance, "先手持ち香" }, + { black_hand_knight, "先手持ち桂" }, + { black_hand_silver, "先手持ち銀" }, + { black_hand_gold, "先手持ち金" }, + { black_hand_bishop, "先手持ち角" }, + { black_hand_rook, "先手持ち飛" }, + { white_hand_pawn, "後手持ち歩" }, + { white_hand_lance, "後手持ち香" }, + { white_hand_knight, "後手持ち桂" }, + { white_hand_silver, "後手持ち銀" }, + { white_hand_gold, "後手持ち金" }, + { white_hand_bishop, "後手持ち角" }, + { white_hand_rook, "後手持ち飛" }, + { black_pawn, "先手歩" }, + { black_lance, "先手香" }, + { black_knight, "先手桂" }, + { black_silver, "先手銀" }, + { black_gold, "先手金" }, + { black_bishop, "先手角" }, + { black_rook, "先手飛" }, + { black_horse, "先手馬" }, + { black_dragon, "先手竜" }, + { white_pawn, "後手歩" }, + { white_lance, "後手香" }, + { white_knight, "後手桂" }, + { white_silver, "後手銀" }, + { white_gold, "後手金" }, + { white_bishop, "後手角" }, + { white_rook, "後手飛" }, + { white_horse, "後手馬" }, + { white_dragon, "後手竜" }, + }; + + int ps_index; + for (int i = 0; i < 34 - 1; i++) { + if (PieceStateIndex[i] <= ps && ps < PieceStateIndex[i + 1]) { + ps_index = PieceStateIndex[i]; + break; + } + } + + //ps < hand_endなら枚数 - 1を示すし、そうでないならマスの位置を示す + int offset = ps - ps_index; + + if (ps < hand_end) { + os << dictionary[PieceState(ps_index)] << ":" << offset + 1; + } else { + os << offset / 9 + 1 << offset % 9 + 1 << dictionary[PieceState(ps_index)]; + } + return os; +} + +PieceState pieceState(const Piece p, int square_or_num, Color c) { + if (p <= KING) { //持ち駒 + assert(square_or_num >= 1); + //持ち駒 + --square_or_num; + + if (c == BLACK) { + return PieceState(PieceToStateIndex[p] + square_or_num); + } else { + //後手の分だけずらす + int ps = (PieceToStateIndex[p] + square_or_num); + switch (p) { + case PAWN: + ps += 18; break; + case LANCE: + case KNIGHT: + case SILVER: + case GOLD: + ps += 4; break; + case BISHOP: + case ROOK: + ps += 2; break; + } + return PieceState(ps); + } + } else { //盤上の駒 + return PieceState(PieceToStateIndex[p] + SquareToNum[square_or_num]); + } +} \ No newline at end of file diff --git a/kaitei_WCSC28/piece_state.hpp b/kaitei_WCSC28/piece_state.hpp new file mode 100644 index 0000000..b8f09c4 --- /dev/null +++ b/kaitei_WCSC28/piece_state.hpp @@ -0,0 +1,55 @@ +#pragma once + +#include"piece.hpp" + +enum PieceState { + black_hand_pawn = 0, + white_hand_pawn = black_hand_pawn + 18, + black_hand_lance = white_hand_pawn + 18, + white_hand_lance = black_hand_lance + 4, + black_hand_knight = white_hand_lance + 4, + white_hand_knight = black_hand_knight + 4, + black_hand_silver = white_hand_knight + 4, + white_hand_silver = black_hand_silver + 4, + black_hand_gold = white_hand_silver + 4, + white_hand_gold = black_hand_gold + 4, + black_hand_bishop = white_hand_gold + 4, + white_hand_bishop = black_hand_bishop + 2, + black_hand_rook = white_hand_bishop + 2, + white_hand_rook = black_hand_rook + 2, + hand_end = white_hand_rook + 2, + + black_pawn = hand_end, + white_pawn = black_pawn + 81, + black_lance = white_pawn + 81, + white_lance = black_lance + 81, + black_knight = white_lance + 81, + white_knight = black_knight + 81, + black_silver = white_knight + 81, + white_silver = black_silver + 81, + black_gold = white_silver + 81, + white_gold = black_gold + 81, + black_bishop = white_gold + 81, + white_bishop = black_bishop + 81, + black_rook = white_bishop + 81, + white_rook = black_rook + 81, + black_horse = white_rook + 81, + white_horse = black_horse + 81, + black_dragon = white_horse + 81, + white_dragon = black_dragon + 81, + square_end = white_dragon + 81, + PieceStateNum = square_end, +}; + +extern PieceState PieceToStateIndex[PieceNum]; +extern PieceState PieceStateIndex[]; +extern PieceState invPieceStateIndex[PieceStateNum]; + +void initPieceToStateIndex(); +void initInvPieceState(); +inline PieceState invPieceState(PieceState ps) { + return invPieceStateIndex[ps]; +} + +std::ostream& operator<<(std::ostream& os, const PieceState ps); +PieceState pieceState(const Piece p, int square_or_num, Color c = BLACK); \ No newline at end of file diff --git a/kaitei_WCSC28/position.cpp b/kaitei_WCSC28/position.cpp new file mode 100644 index 0000000..7a4a14f --- /dev/null +++ b/kaitei_WCSC28/position.cpp @@ -0,0 +1,1561 @@ +#include"position.hpp" +#include"piece.hpp" +#include"move.hpp" +#include"common.hpp" +#include"piece_state.hpp" +#include +#include +#include +#include +#include +#include +#include +#include + +Position::Position() { + //盤上の初期化 + for (int i = 0; i < SquareNum; i++) board_[i] = WALL; + for (Square sq : SquareList) board_[sq] = EMPTY; + + //後手の駒 + board_[SQ11] = WHITE_LANCE; + board_[SQ21] = WHITE_KNIGHT; + board_[SQ31] = WHITE_SILVER; + board_[SQ41] = WHITE_GOLD; + board_[SQ51] = WHITE_KING; + board_[SQ61] = WHITE_GOLD; + board_[SQ71] = WHITE_SILVER; + board_[SQ81] = WHITE_KNIGHT; + board_[SQ91] = WHITE_LANCE; + board_[SQ22] = WHITE_BISHOP; + board_[SQ82] = WHITE_ROOK; + board_[SQ13] = WHITE_PAWN; + board_[SQ23] = WHITE_PAWN; + board_[SQ33] = WHITE_PAWN; + board_[SQ43] = WHITE_PAWN; + board_[SQ53] = WHITE_PAWN; + board_[SQ63] = WHITE_PAWN; + board_[SQ73] = WHITE_PAWN; + board_[SQ83] = WHITE_PAWN; + board_[SQ93] = WHITE_PAWN; + + //先手の駒 + board_[SQ19] = BLACK_LANCE; + board_[SQ29] = BLACK_KNIGHT; + board_[SQ39] = BLACK_SILVER; + board_[SQ49] = BLACK_GOLD; + board_[SQ59] = BLACK_KING; + board_[SQ69] = BLACK_GOLD; + board_[SQ79] = BLACK_SILVER; + board_[SQ89] = BLACK_KNIGHT; + board_[SQ99] = BLACK_LANCE; + board_[SQ88] = BLACK_BISHOP; + board_[SQ28] = BLACK_ROOK; + board_[SQ17] = BLACK_PAWN; + board_[SQ27] = BLACK_PAWN; + board_[SQ37] = BLACK_PAWN; + board_[SQ47] = BLACK_PAWN; + board_[SQ57] = BLACK_PAWN; + board_[SQ67] = BLACK_PAWN; + board_[SQ77] = BLACK_PAWN; + board_[SQ87] = BLACK_PAWN; + board_[SQ97] = BLACK_PAWN; + + //持ち駒 + hand_[BLACK].clear(); + hand_[WHITE].clear(); + + //手番 + color_ = BLACK; + + //手数 + turn_number_ = 0; + + //玉の位置 + king_sq_[BLACK] = SQ59; + king_sq_[WHITE] = SQ51; + + //局面の評価値 + initScore(); + + //ハッシュ値 + hashInit(); + hash_value_ = 0; + board_hash_ = 0; + hand_hash_ = 0; + for (auto sq : SquareList) { + board_hash_ ^= HashSeed[board_[sq]][sq]; + } + for (Piece piece = PAWN; piece <= ROOK; piece++) { + hand_hash_ ^= HandHashSeed[BLACK][piece][hand_[BLACK].num(piece)]; + hand_hash_ ^= HandHashSeed[WHITE][piece][hand_[WHITE].num(piece)]; + } + hash_value_ = board_hash_ ^ hand_hash_; + hash_value_ &= ~1; //これで1bit目が0になる(先手番を表す) + + ee_ = makeEvalElements(); + + stack_.reserve(512); + kifu_.reserve(512); + + //Bitboard + occupied_bb_[BLACK] = Bitboard(0, 0); + occupied_bb_[WHITE] = Bitboard(0, 0); + for (int p = 0; p < PieceNum; ++p) { + pieces_bb_[p] = Bitboard(0, 0); + } + for (Square sq : SquareList) { + if (board_[sq] != EMPTY) { + pieces_bb_[board_[sq]] |= SQUARE_BB[sq]; + occupied_bb_[pieceToColor(board_[sq])] |= SQUARE_BB[sq]; + } + } + occupied_all_ = occupied_bb_[BLACK] | occupied_bb_[WHITE]; + + //pinners + computePinners(); + + + //利きを設定 + initControl(); + + isChecked_ = false; +} + +void Position::print() const { + //盤上 + std::printf("987654321\n"); + std::printf("------------------\n"); + for (int r = Rank1; r <= Rank9; r++) { + for (int f = File9; f >= File1; f--) { + std::cout << PieceToSfenStr[board_[FRToSquare[f][r]]]; + } + printf("|%d\n", r); + } + + //先手の持ち駒 + printf("持ち駒\n"); + printf("先手:"); + hand_[BLACK].print(); + + //後手の持ち駒 + printf("後手:"); + hand_[WHITE].print(); + + //手番 + printf("手番:"); + if (color_ == BLACK) printf("先手\n"); + else printf("後手\n"); + + //手数 + printf("手数:%d\n", turn_number_); + + //最後の手 + if (!kifu_.empty()) { + printf("最後の手:"); + lastMove().print(); + } + + //評価値 + printf("駒割評価値:%d\n", piece_score_); + printf("合計評価値:%d\n", score()); + + printf("ハッシュ値:%lld\n", hash_value_); + + printControl(); +} + +void Position::printAllMoves() const { + std::vector moves = generateAllMoves(); + printf("全合法手の数:%zu\n", moves.size()); + for (Move move : moves) move.print(); +} + +void Position::printHistory() const { + printf("print history\n"); + for (Move move : kifu_) move.print(); + printf("\n"); +} + +void Position::printForDebug() const { + print(); + printHistory(); + //printAllMoves(); + //std::cout << control_bb_(BLACK) << std::endl; + //std::cout << control_bb_(WHITE) << std::endl; +} + +void Position::doMove(const Move move) { + extern int piece_value[]; + +#if DEBUG + if (!isLegalMove(move)) { + printForDebug(); + std::cout << "違法だった手:"; + move.print(); + isLegalMove(move); + assert(false); + } +#endif + + //動かす前の状態を残しておく + stack_.emplace_back(*this); + + //実際に動かす + if (move.isDrop()) { //持ち駒を打つ手 + + //持ち駒を減らす + hand_[color_].sub(kind(move.subject())); + + //移動先にsubjectを設置 + board_[move.to()] = move.subject(); + + //ハッシュ値の更新 + //打つ前のHandHashとXORして消す + hand_hash_ ^= HandHashSeed[color_][kind(move.subject())][hand_[color_].num(kind(move.subject())) + 1]; + //打った後のHandHashとXOR + hand_hash_ ^= HandHashSeed[color_][kind(move.subject())][hand_[color_].num(kind(move.subject()))]; + //打った後の分をXOR + board_hash_ ^= HashSeed[move.subject()][move.to()]; + + //評価値の更新 + piece_score_ -= (color_ == BLACK ? piece_value[kind(move.subject())] : -piece_value[kind(move.subject())]); + piece_score_ += piece_value[move.subject()]; + + //Bitboard更新 + pieces_bb_[move.subject()] |= SQUARE_BB[move.to()]; + occupied_bb_[color_] |= SQUARE_BB[move.to()]; + + //利きの更新 + putPiece(move.subject(), move.to()); + + } else { //盤上の駒を動かす手 + + //移動する駒を消す + board_[move.from()] = EMPTY; + pieces_bb_[move.subject()] &= ~SQUARE_BB[move.from()]; + occupied_bb_[color_] &= ~SQUARE_BB[move.from()]; + + //利きを消す + removePiece(move.subject(), move.from()); + + //取った駒があるならその駒を消し、持ち駒を増やす + if (move.capture() != EMPTY) { + //取った駒を消す + board_[move.to()] = EMPTY; + pieces_bb_[move.capture()] &= ~SQUARE_BB[move.to()]; + occupied_bb_[~color_] &= ~SQUARE_BB[move.to()]; + + //利きを消す + removePiece(move.capture(), move.to()); + + //持ち駒を増やす + hand_[color_].add(kind(move.capture())); + + //ハッシュ値の更新 + //取った駒分のハッシュをXOR + board_hash_ ^= HashSeed[move.capture()][move.to()]; + //増える前の持ち駒の分をXORして消す + hand_hash_ ^= HandHashSeed[color_][kind(move.capture())][hand_[color_].num(kind(move.capture())) - 1]; + //増えた後の持ち駒の分XOR + hand_hash_ ^= HandHashSeed[color_][kind(move.capture())][hand_[color_].num(kind(move.capture()))]; + + //評価値の更新 + piece_score_ += (color_ == BLACK ? piece_value[kind(move.capture())] : -piece_value[kind(move.capture())]); + piece_score_ -= piece_value[move.capture()]; + } + + //成る手ならsubjectに成りのフラグを立てて,そうでないならsubjectをそのまま移動先に設置 + if (move.isPromote()) { + board_[move.to()] = promote(move.subject()); + + //利きの更新 + putPiece(promote(move.subject()), move.to()); + + //評価値の更新 + piece_score_ += piece_value[promote(move.subject())]; + piece_score_ -= piece_value[move.subject()]; + + //Bitboard更新 + pieces_bb_[promote(move.subject())] |= SQUARE_BB[move.to()]; + } else { + board_[move.to()] = move.subject(); + + //利きの更新 + putPiece(move.subject(), move.to()); + + //Bitboard更新 + pieces_bb_[move.subject()] |= SQUARE_BB[move.to()]; + } + //occupiedは成か不成かによらない + occupied_bb_[color_] |= SQUARE_BB[move.to()]; + + //ハッシュ値の更新 + //移動前の分をXORして消す + board_hash_ ^= HashSeed[move.subject()][move.from()]; + //移動後の分をXOR + if (move.isPromote()) + board_hash_ ^= HashSeed[promote(move.subject())][move.to()]; + else + board_hash_ ^= HashSeed[move.subject()][move.to()]; + } + + //玉を動かす手ならblack_king_pos,white_king_posに反映 + if (kind(move.subject()) == KING) { + king_sq_[color_] = move.to(); + if (color_ == BLACK) { + ee_.black_king_sq = SquareToNum[move.to()]; + } else { + ee_.white_king_sq_reversed = SquareToNum[InvSquare[move.to()]]; + } + } + + //occupied_all_を更新 + occupied_all_ = occupied_bb_[BLACK] | occupied_bb_[WHITE]; + + //手番の更新 + color_ = ~color_; + + //Bitboard関連 + //王手 + isChecked_ = isThereControl(~color_, king_sq_[color_]); + + //pinners + computePinners(); + + //手数の更新 + turn_number_++; + + //棋譜に指し手を追加 + kifu_.push_back(move); + + //hashの手番要素を更新 + hash_value_ = board_hash_ ^ hand_hash_; + //1bit目を0にする + hash_value_ &= ~1; + //手番が先手だったら1bitは0のまま,後手だったら1bit目は1になる + hash_value_ |= color_; + + calcScoreDiff(); +} + +void Position::undo() { + //駒割のテーブル読み込み + extern int piece_value[]; + + const Move last_move = kifu_.back(); + kifu_.pop_back(); + + //手番を戻す(このタイミングでいいかな?) + color_ = ~color_; + + //動かした駒を消す + board_[last_move.to()] = EMPTY; + occupied_bb_[color_] &= ~SQUARE_BB[last_move.to()]; + + //盤の状態を戻す + if (last_move.isDrop()) { //打つ手 + + //持ち駒を増やす + hand_[color_].add(kind(last_move.subject())); + + //ハッシュ値の巻き戻し + //戻す前のHandHashとXOR + hand_hash_ ^= HandHashSeed[color_][kind(last_move.subject())][hand_[color_].num(kind(last_move.subject())) - 1]; + //戻す前の分をXORして消す + board_hash_ ^= HashSeed[last_move.subject()][last_move.to()]; + //戻した後のHandHashとXOR + hand_hash_ ^= HandHashSeed[color_][kind(last_move.subject())][hand_[color_].num(kind(last_move.subject()))]; + + //評価値の更新 + piece_score_ += (color_ == BLACK ? piece_value[kind(last_move.subject())] : -piece_value[kind(last_move.subject())]); + piece_score_ -= piece_value[last_move.subject()]; + + //Bitboard更新 + pieces_bb_[last_move.subject()] &= ~SQUARE_BB[last_move.to()]; + + //利きの更新 + removePiece(last_move.subject(), last_move.to()); + } else { //盤上の駒を動かす手 + //動いた駒の利きを削除する + removePiece(last_move.isPromote() ? promote(last_move.subject()) : last_move.subject(), last_move.to()); + + //取る手だったらtoに取った駒を戻し、持ち駒を減らす + if (last_move.capture() != EMPTY) { + board_[last_move.to()] = last_move.capture(); + hand_[color_].sub(kind(last_move.capture())); + + //利きの更新 + putPiece(last_move.capture(), last_move.to()); + + //Bitboard更新 + pieces_bb_[last_move.capture()] |= SQUARE_BB[last_move.to()]; + occupied_bb_[~color_] |= SQUARE_BB[last_move.to()]; + + //ハッシュ値の巻き戻し + //取る前の分のハッシュをXOR + board_hash_ ^= HashSeed[last_move.capture()][last_move.to()]; + //増える前の持ち駒の分 + hand_hash_ ^= HandHashSeed[color_][last_move.capture() & PIECE_KIND_MASK][hand_[color_].num(kind(last_move.capture()))]; + //増えた後の持ち駒の分XORして消す + hand_hash_ ^= HandHashSeed[color_][last_move.capture() & PIECE_KIND_MASK][hand_[color_].num(kind(last_move.capture())) + 1]; + + //評価値の更新 + piece_score_ -= (color_ == BLACK ? piece_value[kind(last_move.capture())] : -piece_value[kind(last_move.capture())]); + piece_score_ += piece_value[last_move.capture()]; + } + + //動いた駒をfromに戻す + board_[last_move.from()] = last_move.subject(); + //利きを戻る + putPiece(last_move.subject(), last_move.from()); + //Bitboard更新 + pieces_bb_[last_move.subject()] |= SQUARE_BB[last_move.from()]; + occupied_bb_[color_] |= SQUARE_BB[last_move.from()]; + + //ハッシュ値の巻き戻し + //移動前の分をXOR + board_hash_ ^= HashSeed[last_move.subject()][last_move.from()]; + //移動後の分をXORして消す + if (last_move.isPromote()) + board_hash_ ^= HashSeed[promote(last_move.subject())][last_move.to()]; + else + board_hash_ ^= HashSeed[last_move.subject()][last_move.to()]; + + //評価値とBitboardの更新 + if (last_move.isPromote()) { + //評価値の更新 + piece_score_ -= piece_value[promote(last_move.subject())]; + piece_score_ += piece_value[last_move.subject()]; + + pieces_bb_[promote(last_move.subject())] &= ~SQUARE_BB[last_move.to()]; + } else { + pieces_bb_[last_move.subject()] &= ~SQUARE_BB[last_move.to()]; + } + } + + //玉を動かす手ならking_sq_に反映 + if (kind(last_move.subject()) == KING) king_sq_[color_] = last_move.from(); + + //occupied_all_を更新 + occupied_all_ = occupied_bb_[BLACK] | occupied_bb_[WHITE]; + + //王手は自玉へのattackers + //checkers_ = attackersTo(~color_, king_sq_[color_]); + + //ハッシュの更新 + hash_value_ = board_hash_ ^ hand_hash_; + //一番右のbitを0にする + hash_value_ &= ~1; + //一番右のbitが先手番だったら0のまま、後手番だったら1になる + hash_value_ |= color_; + + //手数 + turn_number_--; + + //計算が面倒なものはコピーで戻してみる + piece_score_ = stack_.back().piece_score; + kp_score_ = stack_.back().kp_score; + pp_score_ = stack_.back().pp_score; + ee_ = stack_.back().features; + pinners_ = stack_.back().pinners; + isChecked_ = stack_.back().isChecked; + + //Stack更新 + stack_.pop_back(); +} + +void Position::doNullMove() { + stack_.emplace_back(*this); + + //手番の更新 + color_ = ~color_; + + //手数の更新 + turn_number_++; + + //hashの手番要素を更新 + //1bit目を0にする + hash_value_ &= ~1; + //手番が先手だったら1bitは0のまま,後手だったら1bit目は1になる + hash_value_ |= color_; + + kifu_.push_back(NULL_MOVE); + + //pinnersとpinned + computePinners(); +} + +void Position::undoNullMove() { + kifu_.pop_back(); + + //手番を戻す(このタイミングでいいかな?) + color_ = ~color_; + + //ハッシュの更新(手番分) + //一番右のbitを0にする + hash_value_ &= ~1; + //一番右のbitが先手番だったら0のまま、後手番だったら1になる + hash_value_ |= color_; + + //手数 + turn_number_--; + + //計算が面倒なものはコピーで戻してみる + piece_score_ = stack_.back().piece_score; + kp_score_ = stack_.back().kp_score; + pp_score_ = stack_.back().pp_score; + ee_ = stack_.back().features; + pinners_ = stack_.back().pinners; + isChecked_ = stack_.back().isChecked; + + //Stack更新 + stack_.pop_back(); +} + +bool Position::isLegalMove(const Move move) const { + //違法の場合だけ早くfalseで返す.合法手は一番最後の行でtrueが返る + + //NULL_MOVEだけは先に判定するか…… + if (move == NULL_MOVE) { + return false; + } + + //打つ手と動かす手両方に共通するもの + //移動先のチェック + if (!isOnBoard(move.to())) { +#if DEBUG + std::cout << "移動先が盤上ではありません" << std::endl; +#endif + return false; + } + if (color_ == BLACK && pieceToColor(board_[move.to()]) == BLACK) { +#if DEBUG + std::cout << "先手の移動先に先手の駒があります" << std::endl; +#endif + return false; + } + if (color_ == WHITE && pieceToColor(board_[move.to()]) == WHITE) { +#if DEBUG + std::cout << "後手の移動先に後手の駒があります" << std::endl; +#endif + return false; + } + + if (!move.isDrop() && move.subject() != board_[move.from()]) { +#if DEBUG + std::cout << "動かす駒が違います" << std::endl; +#endif + return false; + } + + if (move.subject() == EMPTY) { +#if DEBUG + std::cout << "無を動かしています" << std::endl; +#endif + return false; + } + + //手番と動かす駒の対応チェック + if (color_ == BLACK && (pieceToColor(move.subject()) == WHITE)) { +#if DEBUG + std::cout << "先手の指し手で後手の駒を動かしています" << std::endl; +#endif + return false; + } + if (color_ == WHITE && (pieceToColor(move.subject()) == BLACK)) { +#if DEBUG + std::cout << "後手の指し手で先手の駒を動かしています" << std::endl; +#endif + return false; + } + + //取る駒の対応 + if (move.capture() != board_[move.to()]) { +#if DEBUG + std::cout << "captureとboard_[move.to()]が食い違っています" << std::endl; +#endif + return false; + } + + //成りのチェック + if (move.isPromote() && color_ == BLACK) { + if (SquareToRank[move.to()] > Rank3 && SquareToRank[move.from()] > Rank3) { +#if DEBUG + std::cout << "自陣で成っています" << std::endl; +#endif + return false; + } + } + if (move.isPromote() && color_ == WHITE) { + if (SquareToRank[move.to()] < Rank7 && SquareToRank[move.from()] < Rank7) { +#if DEBUG + std::cout << "自陣で成っています" << std::endl; +#endif + return false; + } + } + if (move.isDrop()) { //駒を打つ手特有の判定 + //打つ先が空になってるか + if (board_[move.to()] != EMPTY) { +#if DEBUG + std::cout << "打つ先に駒があります" << std::endl; +#endif + return false; + } + //打つ駒はあるか + if (hand_[color_].num(kind(move.subject())) <= 0) { +#if DEBUG + std::cout << "打つ駒がありません" << std::endl; +#endif + return false; + } + + //二歩になっていないか + if (move.subject() == BLACK_PAWN && !canDropPawn(move.to())) { +#if DEBUG + std::cout << "二歩または打ち歩詰めです" << std::endl; +#endif + return false; + } + if (move.subject() == WHITE_PAWN && !canDropPawn(move.to())) { +#if DEBUG + std::cout << "二歩または打ち歩詰めです" << std::endl; +#endif + return false; + } + //歩を最奥段に打っていないか + if (move.subject() == BLACK_PAWN && SquareToRank[move.to()] == Rank1) { +#if DEBUG + std::cout << "一段目に歩を打っています" << std::endl; +#endif + return false; + } + if (move.subject() == WHITE_PAWN && SquareToRank[move.to()] == Rank9) { +#if DEBUG + std::cout << "九段目に歩を打っています" << std::endl; +#endif + return false; + } + //香を最奥段に打っていないか + if (move.subject() == BLACK_LANCE && SquareToRank[move.to()] == Rank1) { +#if DEBUG + std::cout << "一段目に香を打っています" << std::endl; +#endif + return false; + } + if (move.subject() == WHITE_LANCE && SquareToRank[move.to()] == Rank9) { +#if DEBUG + std::cout << "九段目に香を打っています" << std::endl; +#endif + return false; + } + //桂を最奥段に打っていないか + if (move.subject() == BLACK_KNIGHT && SquareToRank[move.to()] == Rank1) { +#if DEBUG + std::cout << "一段目に桂を打っています" << std::endl; +#endif + return false; + } + if (move.subject() == WHITE_KNIGHT && SquareToRank[move.to()] == Rank9) { +#if DEBUG + std::cout << "九段目に桂を打っています" << std::endl; +#endif + return false; + } + //桂を奥から二段目に打っていないか + if (move.subject() == BLACK_KNIGHT && SquareToRank[move.to()] == Rank2) { +#if DEBUG + std::cout << "二段目に香を打っています" << std::endl; +#endif + return false; + } + if (move.subject() == WHITE_KNIGHT && SquareToRank[move.to()] == Rank8) { +#if DEBUG + std::cout << "八段目に香を打っています" << std::endl; +#endif + return false; + } + } else { //盤上の駒を動かす手の判定 + //各駒に合わせた動きになっているか + bool flag = false; + for (auto delta : CanMove[board_[move.from()]]) { + if (move.to() == move.from() + delta) + flag = true; + } + + if (BETWEEN_BB[move.from()][move.to()] & occupied_all_) { +#if DEBUG + std::cout << "駒を飛び越えています" << std::endl; +#endif + return false; + } + } + + //自殺手 + if (kind(move.subject()) == KING && isThereControl(~color_, move.to())) { +#if DEBUG + std::cout << "自殺手" << std::endl; +#endif + return false; + } + + bool flag = true; + pinners_.forEach([&](const Square pinner_sq) { + if (BETWEEN_BB[pinner_sq][king_sq_[color_]] & SQUARE_BB[move.from()]) { //fromがbetween,すなわちピンされている + if ((BETWEEN_BB[pinner_sq][king_sq_[color_]] | SQUARE_BB[pinner_sq]) & SQUARE_BB[move.to()]) { + //toがbetween内及びpinner_sqだったらOK + } else { + flag = false; + } + } + }); + if (!flag) { +#if DEBUG + std::cout << "ピンされた駒が違法な動き" << std::endl; +#endif + return false; + } + + //玉を取る手がなぜか発生する場合 + if (kind(board_[move.to()]) == KING) { +#if DEBUG + std::cout << "玉を取る手" << std::endl; +#endif + return false; + } + return true; +} + +bool Position::canDropPawn(const Square to) const { + //2歩の判定を入れる + if (FILE_BB[SquareToFile[to]] & pieces_bb_[color_ == BLACK ? toBlack(PAWN) : toWhite(PAWN)]) { + return false; + } + + //打ち歩詰めの判定 + //冷静に考えなきゃ + //打ち歩詰めの条件は + //(1)玉の前に打っている + //(2)それを取れない + //(2)-a:玉で取れない(打った歩に紐が付いている) + //(2)-b:他の駒で取れない(利きがない) + //(3)逃げ場もない + //かな + if (controlBB(king_sq_[~color_], (color_ == BLACK ? toWhite(PAWN) : toBlack(PAWN)), occupied_all_) & SQUARE_BB[to] //(1) + && isThereControl(color_, to) //(2)-a + && controls_[~color_][to].number <= 1 //(2)-b 利きが1(相手玉自身の利きだけ) + ) { //これで(1),(2)が満たされたので逃げ場を探す + bool can_evasion = false; + Bitboard evasion_to_bb = controlBB(king_sq_[~color_], board_[king_sq_[~color_]], occupied_all_) & ~occupied_bb_[~color_]; + evasion_to_bb.forEach([&](const Square evasion_to) { + if (!attackersTo(color_, evasion_to, occupied_all_ | SQUARE_BB[to])) { + can_evasion = true; + } + }); + if (!can_evasion) { + return false; + } + } + return true; +} + +bool Position::isMoreControl(const Square to) const { + return controls_[color_][to].number > controls_[~color_][to].number; + //return attackersTo(color_, to).pop_count() > attackersTo(~color_, to).pop_count(); +} + +void Position::load_sfen(std::string sfen) { + //初期化 + for (int i = 0; i < SquareNum; i++) board_[i] = WALL; + for (Square sq : SquareList) board_[sq] = EMPTY; + + //テーブル用意しておいたほうがシュッと書ける + static std::unordered_map CharToPiece = { + { 'P', BLACK_PAWN }, + { 'L', BLACK_LANCE }, + { 'N', BLACK_KNIGHT }, + { 'S', BLACK_SILVER }, + { 'G', BLACK_GOLD }, + { 'B', BLACK_BISHOP }, + { 'R', BLACK_ROOK }, + { 'K', BLACK_KING }, + { 'p', WHITE_PAWN }, + { 'l', WHITE_LANCE }, + { 'n', WHITE_KNIGHT }, + { 's', WHITE_SILVER }, + { 'g', WHITE_GOLD }, + { 'b', WHITE_BISHOP }, + { 'r', WHITE_ROOK }, + { 'k', WHITE_KING }, + }; + + //sfen文字列を走査するイテレータ(ダサいやり方な気がするけどパッと思いつくのはこれくらい) + unsigned int i; + + //盤上の設定 + int r = Rank1, f = File9; + for (i = 0; i < sfen.size(); i++) { + if (sfen[i] == '/') { + //次の段へ移る + f = File9; + r++; + } else if (sfen[i] == ' ') { + //手番の設定へ + break; + } else if ('1' <= sfen[i] && sfen[i] <= '9') { + //空マス分飛ばす + f -= sfen[i] - '0'; + } else if (sfen[i] == '+') { + //次の文字が示す駒を成らせてboard_に設置 + board_[FRToSquare[f--][r]] = promote(CharToPiece[sfen[++i]]); + } else { + //玉だったらking_sq_を設定 + if (CharToPiece[sfen[i]] == BLACK_KING) king_sq_[BLACK] = FRToSquare[f][r]; + else if (CharToPiece[sfen[i]] == WHITE_KING) king_sq_[WHITE] = FRToSquare[f][r]; + //文字が示す駒をboard_に設置 + board_[FRToSquare[f--][r]] = CharToPiece[sfen[i]]; + } + } + + //手番の設定 + if (sfen[++i] == 'b') color_ = BLACK; + else color_ = WHITE; + + //美しくない操作だ…… + i += 2; + + //持ち駒 + hand_[BLACK].clear(); + hand_[WHITE].clear(); + int num = 1; + while (sfen[i] != ' ') { + if ('1' <= sfen[i] && sfen[i] <= '9') { //数字なら枚数の取得 + if ('0' <= sfen[i + 1] && sfen[i + 1] <= '9') { + //次の文字も数字の場合が一応あるはず(歩が10枚以上) + num = 10 * (sfen[i] - '0') + sfen[i + 1] - '0'; + i += 2; + } else { + //次が数字じゃないなら普通に取る + num = sfen[i++] - '0'; + } + } else { //駒なら持ち駒を変更 + Piece piece = CharToPiece[sfen[i++]]; + hand_[pieceToColor(piece)].set(kind(piece), num); + + //枚数を1に戻す + num = 1; + } + } + + //手数 + turn_number_ = 0; + + //局面の評価値 + initScore(); + + //ハッシュ値 + hashInit(); + hash_value_ = 0; + board_hash_ = 0; + hand_hash_ = 0; + for (auto sq : SquareList) { + board_hash_ ^= HashSeed[board_[sq]][sq]; + } + for (Piece piece = PAWN; piece <= ROOK; piece++) { + hand_hash_ ^= HandHashSeed[BLACK][piece][hand_[BLACK].num(piece)]; + hand_hash_ ^= HandHashSeed[WHITE][piece][hand_[WHITE].num(piece)]; + } + hash_value_ = board_hash_ ^ hand_hash_; + hash_value_ &= ~1; //これで1bit目が0になる(先手番を表す) + + ee_ = makeEvalElements(); + + stack_.reserve(256); + + //Bitboard + occupied_bb_[BLACK] = Bitboard(0, 0); + occupied_bb_[WHITE] = Bitboard(0, 0); + for (int p = 0; p < PieceNum; ++p) { + pieces_bb_[p] = Bitboard(0, 0); + } + for (Square sq : SquareList) { + if (board_[sq] != EMPTY) { + pieces_bb_[board_[sq]] |= SQUARE_BB[sq]; + occupied_bb_[pieceToColor(board_[sq])] |= SQUARE_BB[sq]; + } + } + occupied_all_ = occupied_bb_[BLACK] | occupied_bb_[WHITE]; + + //pinners + computePinners(); +} + +void Position::hashInit() { + std::mt19937_64 rnd((int)time(0)); + for (int piece = BLACK_PAWN; piece <= WHITE_ROOK_PROMOTE; piece++) { + for (Square sq : SquareList) { + HashSeed[piece][sq] = rnd(); + } + } + for (int color = BLACK; color <= WHITE; color++) { + for (int piece = PAWN; piece <= ROOK; piece++) { + for (int num = 0; num <= 18; num++) { + HandHashSeed[color][piece][num] = rnd(); + } + } + } +} + +void Position::generateEvasionMovesBB(Move *& move_ptr) const { + //王手への対応は(a)玉が逃げる、(b)王手してきた駒を取る、(c)飛び利きの王手には合駒 + + //手番のほうの玉の位置を設定 + Square evasion_from = king_sq_[color_]; + + //(a)逃げる手 + //隣接王手を玉で取り返せる場合はここに含む + Bitboard king_to_bb = controlBB(evasion_from, board_[evasion_from], occupied_all_); + + //味方の駒がいる位置にはいけないので除く + king_to_bb &= ~occupied_bb_[color_]; + + if (controls_[~color_][evasion_from].direction != 0) { + //長い利きがある反対方向には逃げられない + for (auto long_control : LongControlList) { + if (long_control & controls_[~color_][evasion_from].direction) { + Dir d = ConDirToOppositeDir[long_control]; + king_to_bb &= ~SQUARE_BB[evasion_from + d]; + } + } + } + + king_to_bb.forEach([&](const Square to) { + //Move evasion_move(to, evasion_from, false, false, board_[evasion_from], board_[to]); + ////長い利きが残ってる方に逃げてしまうのでoccupiedから玉のfromを抜く + //if (!attackersTo(~color_, to, occupied_all_ & ~SQUARE_BB[evasion_from])) + // *(move_ptr++) = evasion_move; + if (controls_[~color_][to].number == 0) { //相手の利きがないときだけそこに動ける + *(move_ptr++) = Move(to, evasion_from, false, false, board_[evasion_from], board_[to]); + } + }); + + Bitboard checkers = attackersTo(~color_, king_sq_[color_]); + + //(b),(c)は両王手だったら無理 + if (checkers.pop_count() != 1) + return; + + //王手してきた駒の位置 + //関数をconst化しているのでchecker_自体は変更できないからコンストラクタで包まないといけない + const Square checker_sq = checkers.pop(); + + //(b)王手してきた駒を玉以外で取る手 + //ピンされた駒を先に動かす + Bitboard pinned_piece(0, 0); + + pinners_.forEach([&](const Square pinner_sq) { + //pinnerと自玉の間にあるのがpinされている駒 + Bitboard pinned = BETWEEN_BB[pinner_sq][king_sq_[color_]] & occupied_bb_[color_]; + + pinned.forEach([&](const Square from) { + //取る手なのでpinnerを取る手、かつそこがchecker_sqでないとしかダメ + Bitboard to_bb = controlBB(from, board_[from], occupied_all_) & SQUARE_BB[pinner_sq] & SQUARE_BB[checker_sq]; + + if (to_bb) { + pushMove(Move(pinner_sq, from, false, false, board_[from], board_[pinner_sq]), move_ptr); + } + + //使ったマスを記録しておく + pinned_piece |= SQUARE_BB[from]; + }); + }); + + //王手してきた駒を取れる駒の候補 + Bitboard romovers = attackersTo(color_, checker_sq) & ~pinned_piece & ~SQUARE_BB[king_sq_[color_]]; + + romovers.forEach([&](Square from) { + pushMove(Move(checker_sq, from, false, false, board_[from], board_[checker_sq]), move_ptr); + }); + + //(c)合駒 + //王手してきた駒と自玉の間を示すBitboard + Bitboard between = BETWEEN_BB[checker_sq][king_sq_[color_]]; + + //(c)-1 移動合 + Bitboard from_bb = occupied_bb_[color_] & ~pinned_piece & ~SQUARE_BB[king_sq_[color_]]; + from_bb.forEach([&](const Square from) { + Bitboard to_bb = controlBB(from, board_[from], occupied_all_) & between; + to_bb.forEach([&](const Square to) { + pushMove(Move(to, from, false, false, board_[from], board_[to]), move_ptr); + }); + }); + + //(c)-2 打つ合 + //王手されているのでbetweenの示すマスは駒がないはず + generateDropMoveBB(between, move_ptr); +} + +void Position::generateCaptureMovesBB(Move *& move_ptr) const { + //ピンされた駒を先に動かす + Bitboard pinned_piece(0, 0); + + pinners_.forEach([&](const Square pinner_sq) { + //pinnerと自玉の間にあるのがpinされている駒 + Bitboard pinned = BETWEEN_BB[pinner_sq][king_sq_[color_]] & occupied_bb_[color_]; + + pinned.forEach([&](const Square from) { + //取る手なのでpinnerを取る手しかダメ + Bitboard to_bb = controlBB(from, board_[from], occupied_all_) & SQUARE_BB[pinner_sq]; + + if (to_bb) { + pushMove(Move(pinner_sq, from, false, false, board_[from], board_[pinner_sq]), move_ptr); + } + + //使ったマスを記録しておく + pinned_piece |= SQUARE_BB[from]; + }); + }); + + //玉は別に処理する + Bitboard king_to_bb = controlBB(king_sq_[color_], board_[king_sq_[color_]], occupied_all_) + & occupied_bb_[~color_]; + king_to_bb.forEach([&](const Square to) { + if (!isThereControl(~color_, to)) { + *(move_ptr++) = Move(to, king_sq_[color_], false, false, board_[king_sq_[color_]], board_[to]); + } + }); + + //自分の駒がいる位置から、ピンされた駒と玉は先に処理したので除く + Bitboard from_bb = occupied_bb_[color_] & ~pinned_piece & ~SQUARE_BB[king_sq_[color_]]; + + from_bb.forEach([&](const Square from) { + //駒を取る手なので利きの先に相手の駒がないといけない + Bitboard to_bb = controlBB(from, board_[from], occupied_all_) & occupied_bb_[~color_]; + + to_bb.forEach([&](const Square to) { + pushMove(Move(to, from, false, false, board_[from], board_[to]), move_ptr); + }); + }); +} + +void Position::generateNonCaptureMovesBB(Move *& move_ptr) const { + //ピンされた駒を先に動かす + Bitboard pinned_piece(0, 0); + + pinners_.forEach([&](const Square pinner_sq) { + //pinnerと自玉の間にあるのがpinされている駒 + Bitboard pinned = BETWEEN_BB[pinner_sq][king_sq_[color_]] & occupied_bb_[color_]; + + pinned.forEach([&](const Square from) { + //取らない手なのでbetween上を動く手しかダメ(ピンされているのでbetween上に他の駒はない) + Bitboard to_bb = controlBB(from, board_[from], occupied_all_) & BETWEEN_BB[pinner_sq][king_sq_[color_]]; + + to_bb.forEach([&](const Square to) { + pushMove(Move(to, from, false, false, board_[from], board_[to]), move_ptr); + }); + + //使ったマスを記録しておく + pinned_piece |= SQUARE_BB[from]; + }); + }); + + //王の処理 + Bitboard king_to_bb = controlBB(king_sq_[color_], board_[king_sq_[color_]], occupied_all_) + & ~occupied_all_; + king_to_bb.forEach([&](const Square to) { + //相手の利きがなければそこへいく動きを生成できる + if (!isThereControl(~color_, to)) { + *(move_ptr++) = Move(to, king_sq_[color_], false, false, board_[king_sq_[color_]], board_[to]); + } + }); + + //ピンされた駒と玉は先に処理したので除く + Bitboard from_bb = occupied_bb_[color_] & ~pinned_piece & ~SQUARE_BB[king_sq_[color_]]; + from_bb.forEach([&](const Square from) { + //sqにある駒の利き + Bitboard to_bb = controlBB(from, board_[from], occupied_all_) & ~occupied_all_; + to_bb.forEach([&](const Square to) { + pushMove(Move(to, from, false, false, board_[from], board_[to]), move_ptr); + }); + }); + + //駒を打つ手 + Bitboard drop_to_bb = (~occupied_all_ & BOARD_BB); + generateDropMoveBB(drop_to_bb, move_ptr); +} + +void Position::generateRecaptureMovesToBB(const Square to, Move *& move_ptr) const { + //ピンされた駒がダメな方向へ動く、または自玉が相手の利きに飛び込む違法手に注意しなければならない + + //ピンされた駒を先に動かす + Bitboard pinned_piece(0, 0); + + pinners_.forEach([&](const Square pinner_sq) { + //pinnerと自玉の間にあるのがpinされている駒 + Bitboard pinned = BETWEEN_BB[pinner_sq][king_sq_[color_]] & occupied_bb_[color_]; + + pinned.forEach([&](const Square from) { + //取る手なのでpinnerを取る手しかダメ + Bitboard to_bb = controlBB(from, board_[from], occupied_all_) & SQUARE_BB[pinner_sq] & SQUARE_BB[to]; + + if (to_bb) { + pushMove(Move(pinner_sq, from, false, false, board_[from], board_[pinner_sq]), move_ptr); + } + + //使ったマスを記録しておく + pinned_piece |= SQUARE_BB[from]; + }); + }); + + //玉は別に処理する + Bitboard king_to_bb = controlBB(king_sq_[color_], board_[king_sq_[color_]], occupied_all_); + if (king_to_bb & SQUARE_BB[to] && !isThereControl(~color_, to)) { //利きがtoに届いていて、かつそこに相手の利きがない場合動かせる + *(move_ptr++) = Move(to, king_sq_[color_], false, false, board_[king_sq_[color_]], board_[to]); + } + + //ピンされた駒と玉は先に処理したので除く + Bitboard from_bb = attackersTo(color_, to) & ~pinned_piece & ~SQUARE_BB[king_sq_[color_]]; + from_bb.forEach([&](const Square from) { + pushMove(Move(to, from, false, false, board_[from], board_[to]), move_ptr); + }); +} + +void Position::generatePsuedoCaptureMoves(Move *& move_ptr) const { + //疑似合法手なので玉が取られても良いとする + Bitboard from_bb = occupied_bb_[color_]; + from_bb.forEach([&](const Square from) { + //駒を取る手なので利きの先に相手の駒がないといけない + Bitboard to_bb = controlBB(from, board_[from], occupied_all_) & occupied_bb_[~color_]; + + to_bb.forEach([&](const Square to) { + pushMove(Move(to, from, false, false, board_[from], board_[to]) , move_ptr); + }); + }); +} + +void Position::generatePsuedoNonCaptureMoves(Move *& move_ptr) const { + Bitboard from_bb = occupied_bb_[color_]; + from_bb.forEach([&](const Square from) { + Bitboard to_bb = controlBB(from, board_[from], occupied_all_) & ~occupied_all_; + to_bb.forEach([&](const Square to) { + pushMove(Move(to, from, false, false, board_[from], board_[to]), move_ptr); + }); + }); + + //駒を打つ手 + Bitboard drop_to_bb = (~occupied_all_ & BOARD_BB); + generateDropMoveBB(drop_to_bb, move_ptr); +} + +void Position::generatePsuedoRecaptureMovesTo(const Square to, Move *& move_ptr) const { + //ピンされた駒と玉は先に処理したので除く + Bitboard from_bb = attackersTo(color_, to); + from_bb.forEach([&](const Square from) { + pushMove(Move(to, from, false, false, board_[from], board_[to]), move_ptr); + }); +} + +inline bool Position::isPsuedoLegalMove(const Move move) const { + //動き方は駒の種類に即していることを前提に、いくつか簡易的なチェックをするだけ + + //移動先が盤内じゃなかったらダメ + if (!isOnBoard(move.to())) return false; + + //移動先に手番側の駒があったらダメ + if (pieceToColor(board_[move.to()]) == color_) return false; + + //移動元が玉位置(つまり玉を動かす手)であり、移動先に相手側の利きがあったらダメ + if (move.from() == king_sq_[color_] && isThereControl(~color_, move.to())) return false; + + return true; +} + +inline bool Position::canPromote(Move move) const { + //打つ手だったらダメ + if (move.isDrop()) return false; + + //すでに成っている駒を動かす手だったらダメ + if (board_[move.from()] & PROMOTE) return false; + + //動かす駒が金だったらダメ + if (kind(board_[move.from()]) == GOLD) return false; + + //動かす駒が玉だったらダメ + if (kind(board_[move.from()]) == KING) return false; + + //位置関係 + if (color_ == BLACK) { + return ((Rank1 <= SquareToRank[move.to()] && SquareToRank[move.to()] <= Rank3) || (Rank1 <= SquareToRank[move.from()] && SquareToRank[move.from()] <= Rank3)); + } else if (color_ == WHITE) { + return ((Rank7 <= SquareToRank[move.to()] && SquareToRank[move.to()] <= Rank9) || (Rank7 <= SquareToRank[move.from()] && SquareToRank[move.from()] <= Rank9)); + } + return false; +} + +void Position::pushMove(const Move move, Move*& move_ptr) const { + //成る手が可能だったら先に生成 + if (canPromote(move)) { + *(move_ptr++) = promotiveMove(move); + switch (kind(board_[move.from()])) { + //歩、角、飛は成る手しか生成しなくていいだろう + case PAWN: + case BISHOP: + case ROOK: + return; + //香、桂は位置によっては成る手しか不可 + //香の2段目への不成は可能だけど意味がないので生成しない + case LANCE: + case KNIGHT: + if (color_ == BLACK && SquareToRank[move.to()] <= Rank2) return; + if (color_ == WHITE && SquareToRank[move.to()] >= Rank8) return; + break; + } + } + //成らない手 + *(move_ptr++) = move; +} + +void Position::pushCheckMove(const Move move, Move*& move_ptr) { + //簡易合法判定をする + if (!isPsuedoLegalMove(move)) return; + + //成る手が可能だったら先に生成 + if (canPromote(move)) { + Move promotive_move = promotiveMove(move); + doMove(promotive_move); + if (isChecked_) { + *(move_ptr++) = promotive_move; + } + undo(); + switch (kind(board_[move.from()])) { + //歩、角、飛は成る手しか生成しなくていいだろう + case PAWN: + case BISHOP: + case ROOK: + return; + //香、桂は位置によっては成る手しか不可 + case LANCE: + if (color_ == BLACK && SquareToRank[move.to()] == Rank1) return; + if (color_ == WHITE && SquareToRank[move.to()] == Rank9) return; + break; + case KNIGHT: + if (color_ == BLACK && SquareToRank[move.to()] <= Rank2) return; + if (color_ == WHITE && SquareToRank[move.to()] >= Rank8) return; + break; + } + } + //成らない手 + doMove(move); + if (isChecked_) { + *(move_ptr++) = move; + } + undo(); +} + +void Position::generateDropMoveBB(const Bitboard& to_bb, Move *& move_ptr) const { + //歩を打つ手 + //最奥の段は除外する + if (hand_[color_].num(PAWN) > 0) { + (to_bb & ~farRank1FromColor(color_)).forEach([&](Square to) { + if (canDropPawn(to)) { + *(move_ptr++) = dropMove(to, Piece(PAWN | ColorToFlag[color_])); + } + }); + } + + //香車 + //最奥の段は除外する + if (hand_[color_].num(LANCE) > 0) { + (to_bb & ~farRank1FromColor(color_)).forEach([&](Square to) { + *(move_ptr++) = dropMove(to, Piece(LANCE | ColorToFlag[color_])); + }); + } + + //桂馬 + //奥の2段は除外する + if (hand_[color_].num(KNIGHT) > 0) { + (to_bb & ~(farRank1FromColor(color_) | farRank2FromColor(color_))).forEach([&](Square to) { + *(move_ptr++) = dropMove(to, Piece(KNIGHT | ColorToFlag[color_])); + }); + } + + //その他 + for (Piece p : { SILVER, GOLD, BISHOP, ROOK }) { + if (hand_[color_].num(p) > 0) { + (to_bb).forEach([&](Square to) { + *(move_ptr++) = dropMove(to, Piece(p | ColorToFlag[color_])); + }); + } + } +} + +Bitboard Position::attackersTo(const Color c, const Square sq) const { + //sqへ利きを持っている駒の位置を示すbitboardを返す + //sqから相手側の駒として利きを求めてみて、その範囲にc側の駒があるなら利きがある + Bitboard result(0, 0); + for (Piece p : ColoredPieceList[c]) { + result |= (controlBB(sq, oppositeColor(p), occupied_all_) & pieces_bb_[p]); + } + return result; +} + +Bitboard Position::attackersTo(const Color c, const Square sq, const Bitboard & occupied) const { + //sqへ利きを持っている駒の位置を示すbitboardを返す + //sqから相手側の駒として利きを求めてみて、その範囲にc側の駒があるなら利きがある + Bitboard result(0, 0); + for (Piece p : ColoredPieceList[c]) { + result |= (controlBB(sq, oppositeColor(p), occupied) & pieces_bb_[p]); + } + return result; +} + +void Position::computePinners() { + //pinners + pinners_ = Bitboard(0, 0); + //香・角(馬)・飛車(竜)に対してピンのチェック + for (Piece jumper : ColoredJumpPieceList[~color_]) { + //自玉からこっちの香・角・飛として利きを駒を最大まで伸ばして + //そこに相手の香・角(馬)・飛(竜)があったらそれはpinnerになりうる + //香だったらそれだけ,角飛だったらなったものについて存在位置を取る + Bitboard jumper_bb = (kind(jumper) == LANCE ? pieces_bb_[jumper] : pieces_bb_[jumper] | pieces_bb_[promote(jumper)]); + Bitboard pinner_candidate = controlBB(king_sq_[color_], oppositeColor(jumper), Bitboard(0, 0)) & jumper_bb; + + //各pinnerの候補についてking_sq_との間を見ていく + pinner_candidate.forEach([&](const Square jump_piece_sq) { + Bitboard between = BETWEEN_BB[king_sq_[color_]][jump_piece_sq] & occupied_all_; + if (between.pop_count() == 1 && (between & occupied_bb_[color_])) { //betweenに駒が1個だけかつ駒が手番側のものだったらピンされている + //ピンしている駒を記録しておけばピンされた駒は自玉との間でbetween見れば復元できる + pinners_ |= SQUARE_BB[jump_piece_sq]; + } + }); + } +} + +std::vector Position::generateAllMoves() const { + Move move_buf[MAX_MOVE_LIST_SIZE]; + Move* move_ptr = move_buf; + //手番側に王手がかかっていたら逃れる手だけを生成 + if (isChecked_) { + generateEvasionMovesBB(move_ptr); + } else { + generateCaptureMovesBB(move_ptr); + generateNonCaptureMovesBB(move_ptr); + } + + std::vector move_list; + for (move_ptr--; move_buf <= move_ptr; move_ptr--) { + move_list.push_back(*move_ptr); + } + return move_list; +} + +void Position::generateCheckMoveBB(Move *& move_ptr) const { + auto start_ptr = move_ptr; + + if (isChecked_) { + //自玉に王手がかかっていたら逆王手するしかない + //王手を解除する手はさほど多くないだろうから全部生成して動かしてみて王手になってるものだけを追加する + Move buf[MAX_MOVE_LIST_SIZE]; + Move* tmp_ptr = buf; + generateEvasionMovesBB(tmp_ptr); + for (Move* itr = buf; itr < tmp_ptr; itr++) { + Position p(*this); + //doMove内で評価値を差分計算しているけど使わないので、抜いたdoMoveが欲しいなぁ + //あるいはMoveを与えたらそれが王手になっているかどうか確認する関数でもいいのか.むしろそのほうが普通かな + p.doMove(*itr); + if (p.isKingChecked()) { + *(move_ptr++) = *itr; + } + p.undo(); + } + return; + } + + Bitboard moved_pieces(0, 0); + //pinnerを先に処理する + for (Piece jumper : ColoredJumpPieceList[color_]) { + //敵玉から相手の香・角・飛として利きを駒を最大まで伸ばして + //そこに相手の香・角(馬)・飛(竜)があったらそれはpinnerになりうる + //香だったらそれだけ,角飛だったらなったものについて存在位置を取る + Bitboard jumper_bb = (kind(jumper) == LANCE ? pieces_bb_[jumper] : pieces_bb_[jumper] | pieces_bb_[promote(jumper)]); + + Bitboard pinner_candidate = controlBB(king_sq_[~color_], oppositeColor(jumper), Bitboard(0, 0)) & jumper_bb; + + //先に処理した駒を記録しておく + moved_pieces |= pinner_candidate; + + //各pinnerの候補についてking_sq_との間を見ていく + pinner_candidate.forEach([&](const Square jump_piece_sq) { + Bitboard between = BETWEEN_BB[king_sq_[color_]][jump_piece_sq] & occupied_all_; + if (between.pop_count() == 1) { //betweenに駒が1個だけだったらなんらかの王手がかけられるかも + if (between & occupied_bb_[color_]) { //間にあるのが自分の駒 + //開き王手or普通に押していく王手ができる可能性がある + //間にある駒が同じ方向への飛び駒である可能性はない(それだったら相手の玉を取れている) + const Square from = between.pop(); + const Piece piece = board_[from]; + + //間にあった駒も動かすので記録しておく + moved_pieces |= SQUARE_BB[from]; + + //開き王手(jump_pieceと相手玉のbetween以外への移動) + //両王手を含む + if (kind(piece) == KING) { + //間にある駒が自玉だったら自殺手に気を付ける + Bitboard to_bb = controlBB(from, piece, occupied_all_) & ~BETWEEN_BB[jump_piece_sq][king_sq_[~color_]]; + to_bb.forEach([&](const Square to) { + if (!attackersTo(~color_, to, occupied_all_ & ~SQUARE_BB[from])) { + pushMove(Move(to, from, false, false, piece, board_[to]), move_ptr); + } + }); + return; + } else { + Bitboard to_bb = controlBB(from, piece, occupied_all_) & ~BETWEEN_BB[jump_piece_sq][king_sq_[~color_]]; + to_bb.forEach([&](const Square to) { + pushMove(Move(to, from, false, false, piece, board_[to]), move_ptr); + }); + } + + //between内で移動する王手 + //飛車先の歩を進める王手みたいなイメージ + //不成 + //相手玉の位置から進める駒の手番を逆にして利きを求め、そこへ利きがあり、between内であり、自駒がなければそこへ行く手が王手 + Bitboard non_promotion_to_bb = controlBB(king_sq_[~color_], oppositeColor(piece), occupied_all_) + & controlBB(from, piece, occupied_all_) & BETWEEN_BB[jump_piece_sq][king_sq_[~color_]] & ~occupied_bb_[color_]; + non_promotion_to_bb.forEach([&](const Square to) { + *(move_ptr++) = Move(to, from, false, false, piece, board_[to]); + }); + + //成 + if (!(piece & PROMOTE) && kind(piece) != GOLD) { + Bitboard promotion_to_bb = controlBB(king_sq_[~color_], oppositeColor(piece) | PROMOTE, occupied_all_) + & controlBB(from, piece, occupied_all_) & BETWEEN_BB[jump_piece_sq][king_sq_[~color_]] & ~occupied_bb_[color_]; + non_promotion_to_bb.forEach([&](const Square to) { + if ((SQUARE_BB[from] | SQUARE_BB[to]) | PROMOTION_ZONE_BB[color_]) { + *(move_ptr++) = Move(to, from, false, true, piece, board_[to]); + } + }); + } + } else if (between & occupied_bb_[color_]) { //間にあるのが相手の駒 + //その駒を取る王手がありえる + const Square to = between.pop(); + pushMove(Move(to, jump_piece_sq, false, false, board_[jump_piece_sq], board_[to]), move_ptr); + } + } + }); + } + + //自駒の中からすでに動かした駒は排除 + Bitboard from_bb = occupied_bb_[color_] & ~moved_pieces; + from_bb.forEach([&](const Square from) { + const Piece piece = board_[from]; + if (kind(piece) == ROOK || kind(piece) == BISHOP || (kind(piece) == LANCE && !(piece & PROMOTE))) { //飛び駒 + //不成の王手 + //相手玉から相手の駒として利きを求め、それと今見ている駒の利きが被ったところ、かつそこに自駒がない位置へ行く手が王手 + Bitboard non_promotion_to_bb = controlBB(king_sq_[~color_], oppositeColor(piece), occupied_all_) & controlBB(from, piece, occupied_all_) & ~occupied_bb_[color_]; + non_promotion_to_bb.forEach([&](const Square to) { + *(move_ptr++) = Move(to, from, false, false, board_[from], board_[to]); + }); + + if (!(piece & PROMOTE)) { + //まだ成ってない駒だったら成の王手 + Bitboard promotion_to_bb = controlBB(king_sq_[~color_], oppositeColor(piece), occupied_all_) & controlBB(from, piece, occupied_all_) & ~occupied_bb_[color_]; + promotion_to_bb.forEach([&](const Square to) { + if ((SQUARE_BB[to] | SQUARE_BB[from]) & PROMOTION_ZONE_BB[color_]) { + *(move_ptr++) = Move(to, from, false, true, board_[from], board_[to]); + } + }); + } + + } else { //飛び駒でない + //不成の王手 + //相手玉から相手の駒として利きを求め、それと今見ている駒の利きが被ったところ、かつそこに自駒がない位置へ行く手が王手 + Bitboard non_promotion_to_bb = controlBB(king_sq_[~color_], oppositeColor(piece), occupied_all_) & controlBB(from, piece, occupied_all_) & ~occupied_bb_[color_]; + non_promotion_to_bb.forEach([&](const Square to) { + *(move_ptr++) = Move(to, from, false, false, board_[from], board_[to]); + }); + + if (!(piece & PROMOTE) && (kind(piece) != GOLD) &&(kind(piece) != KING)) { + //まだ成ってない駒かつ金でないかつ玉でない場合、成りながらの王手がありえる + //相手玉の位置から成った後の駒を相手の駒として利きを求めて、そこへ成らずにいけるかつ、自駒がそこにない場合そこへ成る手が王手 + Bitboard promotion_to_bb = controlBB(king_sq_[~color_], oppositeColor(piece) | PROMOTE, occupied_all_) & controlBB(from, piece, occupied_all_) & ~occupied_bb_[color_]; + promotion_to_bb.forEach([&](const Square to) { + if ((SQUARE_BB[to] | SQUARE_BB[from]) & PROMOTION_ZONE_BB[color_]) { + *(move_ptr++) = Move(to, from, false, true, board_[from], board_[to]); + } + }); + } + } + }); + + //駒打ちの王手 + for (Piece piece : { PAWN, LANCE, KNIGHT, SILVER, GOLD, BISHOP, ROOK }) { + if (hand_[color_].num(piece)) { + Bitboard to_bb = controlBB(king_sq_[~color_], coloredPiece(~color_, piece), occupied_all_) & ~occupied_all_; + to_bb.forEach([&](const Square to) { + if (piece == PAWN) { + //駒が歩だったら二歩と打ち歩詰めの確認 + if (canDropPawn(to)) { + *(move_ptr++) = Move(to, WALL00, true, false, coloredPiece(color_, piece), board_[to]); + } + } else { + *(move_ptr++) = Move(to, WALL00, true, false, coloredPiece(color_, piece), board_[to]); + } + }); + } + } + +#if DEBUG + print(); + std::cout << "この局面の王手" << std::endl; + for (; start_ptr < move_ptr; start_ptr++) { + start_ptr->print(); + Position p(*this); + p.doMove(*start_ptr); + if (!p.isKingChecked()) { + //王手になっていないものがあったらおかしい + assert(false); + } + p.undo(); + } + + std::cout << "以下確認" << std::endl; + std::vector check_moves; + std::vector all_moves = generateAllMoves(); + for (Move m : all_moves) { + //動かしてみて王手がかかる手だけ加える + //TODO:最悪な実装なのでなんとか高速化する.せめて評価値を差分計算しないdoMoveを作ってそっちを利用する + Position p(*this); + p.doMove(m); + if (p.isKingChecked()) { + check_moves.push_back(m); + } + p.undo(); + } + std::cout << "check_moves.size() = " << check_moves.size() << std::endl; +#endif +} + +bool Position::isRepeating(Score& score) { + //千日手or連続王手の千日手だったらtrueを返してscoreに適切な値を入れる(技巧と似た実装) + + if (turn_number_ <= 4) { + return false; + } + + //現局面のindex + auto index = stack_.size(); + + if (board_hash_ == stack_[index - 4].board_hash //局面が一致 + && hand_hash_ == stack_[index - 4].hand_hash //手駒も一致 + ) { //4手前と局面が一致する + //連続王手かどうか + if (isChecked_ && stack_[index - 2].isChecked) { + score = MAX_SCORE; + } else { + score = DRAW_SCORE; + } + return true; + } + return false; +} \ No newline at end of file diff --git a/kaitei_WCSC28/position.hpp b/kaitei_WCSC28/position.hpp new file mode 100644 index 0000000..8226079 --- /dev/null +++ b/kaitei_WCSC28/position.hpp @@ -0,0 +1,154 @@ +#ifndef POSITION_HPP +#define POSITION_HPP + +#include"square.hpp" +#include"piece.hpp" +#include"move.hpp" +#include"hand.hpp" +#include"eval_elements.hpp" +#include"types.hpp" +#include"eval_elements.hpp" +#include"bitboard.hpp" +#include"control.hpp" +#include +#include +#include + +const int MAX_MOVE_LIST_SIZE = 593; + +class Position { +public: + //コンストラクタ + Position(); + + //入出力 + void print() const; + void printAllMoves() const; + void printHistory() const; + void printForDebug() const; + void printScoreDetail() const; + + //動かす戻す + void doMove(const Move move); + void undo(); + void doNullMove(); + void undoNullMove(); + + //合法性に関するもの + bool isLegalMove(const Move move) const; + bool canDropPawn(const Square to) const; + bool isPsuedoLegalMove(const Move move) const; + + //王手,詰み関連 + inline bool isKingChecked() const { return isChecked_; } + inline bool isCheckmated() { return (generateAllMoves().size() == 0); } + inline bool isThereControl(const Color c, const Square sq) const { return controls_[c][sq].number > 0; } + bool isRepeating(Score& score); + + //評価値計算 + virtual Score initScore(); + virtual Score calcScoreDiff(); + void testPieceValue(); + + //特徴量作成 + Features makeEvalElements() const; + Features makeEvalElements(std::vector& pv, Color& leaf_color); + + //Moveを完全にする + Move transformValidMove(const Move move); + + Bitboard attackersTo(const Color c, const Square sq) const; + Bitboard attackersTo(const Color c, const Square sq, const Bitboard& occupied) const; + + void computePinners(); + + //合法手生成 + //ステージングに関わるもの + std::vector generateAllMoves() const; + void generateCheckMoveBB(Move*& move_ptr) const; + void generateEvasionMovesBB(Move*& move_ptr) const; + void generateCaptureMovesBB(Move*& move_ptr) const; + void generateNonCaptureMovesBB(Move*& move_ptr) const; + void generateRecaptureMovesToBB(const Square to, Move*& move_ptr) const; + + void generatePsuedoCaptureMoves(Move*& move_ptr) const; + void generatePsuedoNonCaptureMoves(Move*& move_ptr) const; + void generatePsuedoRecaptureMovesTo(const Square to, Move*& move_ptr) const; + + //部分に関わるもの + bool canPromote(const Move move) const; + void pushMove(const Move move, Move*& move_ptr) const; + void pushCheckMove(const Move move, Move*& move_ptr); + + void generateDropMoveBB(const Bitboard& to_bb, Move*& move_ptr) const; + + bool isMoreControl(const Square to) const; + + //sfen読み込み + void load_sfen(std::string sfen); + + //ハッシュ + void hashInit(); + + //getter + Move lastMove() const { return kifu_.back(); } + unsigned int turn_number() const { return turn_number_; } + Color color() const { return color_; } + long long hash_value() const { return hash_value_; } + Square kingSquare(Color color) const { return king_sq_[color]; } + Score score() const; + Piece on(const Square sq) const { return board_[sq]; } +protected: + //内部メソッド + //controlsを操作するメソッド + void putPiece(Piece p,Square sq); + void removePiece(Piece p,Square sq); + void changeControl(Piece p, Square sq, int diff); + void changeLongControl(Square sq, ControlDir cd, Color c, int extend_or_block); + void initControl(); + void checkControl(); + void printControl() const; + + //インスタンス変数類 + //手番 + Color color_; + Piece board_[SquareNum]; + Hand hand_[ColorNum]; + unsigned int turn_number_; + Score piece_score_, kp_score_, pp_score_; + Square king_sq_[ColorNum]; + std::vector kifu_; + long long HashSeed[PieceNum][SquareNum]; + long long HandHashSeed[ColorNum][PieceNum][19]; + long long hash_value_, board_hash_, hand_hash_; + bool isChecked_; + + struct StateInfo { + //千日手判定用に必要な情報 + long long board_hash, hand_hash; + bool isChecked; + + //undoでコピーするための情報 + Score piece_score, kp_score, pp_score; + Features features; + + Bitboard pinners; + + StateInfo(Position& pos) : + board_hash(pos.board_hash_), hand_hash(pos.hand_hash_), isChecked(pos.isChecked_), piece_score(pos.piece_score_), + kp_score(pos.kp_score_), pp_score(pos.pp_score_), features(pos.ee_), pinners(pos.pinners_) { + } + }; + std::vector stack_; + + Bitboard occupied_all_; + Bitboard occupied_bb_[ColorNum]; + Bitboard pieces_bb_[PieceNum]; + Bitboard pinners_; + + Control controls_[ColorNum][SquareNum]; + + Features ee_; +}; + +#endif \ No newline at end of file diff --git a/kaitei_WCSC28/position_for_learn.h b/kaitei_WCSC28/position_for_learn.h new file mode 100644 index 0000000..ee5b796 --- /dev/null +++ b/kaitei_WCSC28/position_for_learn.h @@ -0,0 +1,282 @@ +#pragma once +#include"position.hpp" +#include"eval_params.hpp" + +enum { + PAWN_VALUE = 100, + LANCE_VALUE = 267, + KNIGHT_VALUE = 295, + SILVER_VALUE = 424, + GOLD_VALUE = 510, + BISHOP_VALUE = 654, + ROOK_VALUE = 738, + PAWN_PROMOTE_VALUE = 614, + LANCE_PROMOTE_VALUE = 562, + KNIGHT_PROMOTE_VALUE = 586, + SILVER_PROMOTE_VALUE = 569, + BISHOP_PROMOTE_VALUE = 951, + ROOK_PROMOTE_VALUE = 1086, +}; + + +static int piece_value[] = { + 0, static_cast(PAWN_VALUE * 1.05), static_cast(LANCE_VALUE * 1.05), static_cast(KNIGHT_VALUE * 1.05), static_cast(SILVER_VALUE * 1.05), + static_cast(GOLD_VALUE * 1.05), static_cast(BISHOP_VALUE * 1.05), static_cast(ROOK_VALUE * 1.05), 0, 0, //0~9 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //10~19 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //20~29 + 0, 0, 0, PAWN_VALUE, LANCE_VALUE, KNIGHT_VALUE, SILVER_VALUE, GOLD_VALUE, BISHOP_VALUE, ROOK_VALUE, //30~39 + 0, 0, 0, 0, 0, 0, 0, 0, 0, PAWN_PROMOTE_VALUE, //40~49 + LANCE_PROMOTE_VALUE, KNIGHT_PROMOTE_VALUE, SILVER_PROMOTE_VALUE, 0, BISHOP_PROMOTE_VALUE, ROOK_PROMOTE_VALUE, 0, 0, 0, 0, //50~59 + 0, 0, 0, 0, 0, -PAWN_VALUE, -LANCE_VALUE, -KNIGHT_VALUE, -SILVER_VALUE, -GOLD_VALUE, //60~69 + -BISHOP_VALUE, -ROOK_VALUE, 0, 0, 0, 0, 0, 0, 0, 0, //70~79 + 0, -PAWN_PROMOTE_VALUE, -LANCE_PROMOTE_VALUE, -KNIGHT_PROMOTE_VALUE, -SILVER_PROMOTE_VALUE, 0, -BISHOP_PROMOTE_VALUE, -ROOK_PROMOTE_VALUE, 0, 0 //80~89 +}; + +//ǂ̕]֐gCX^XƂɕ邽߂PositionNX +class PositionForLearn : public Position { +public: + std::unique_ptr> ptr; + Score initScore(); + Score calcScoreDiff(); +}; + +Score PositionForLearn::initScore() { + piece_score_ = Score(0); + + //Տɂ̉l + for (Square sq : SquareList) { + if (0 > board_[sq] || PieceNum < board_[sq]) { + print(); + printHistory(); + assert(false); + } + piece_score_ += piece_value[board_[sq]]; + } + + //̉l + for (Piece p = PAWN; p <= ROOK; p++) { + piece_score_ += piece_value[p] * hand_[BLACK].num(p); + piece_score_ -= piece_value[p] * hand_[WHITE].num(p); + } + kp_score_ = Score(0); + pp_score_ = Score(0); + + if (eval_params == nullptr) { + return Score(0); + } + + auto ee = makeEvalElements(); + for (unsigned int i = 0; i < PIECE_STATE_LIST_SIZE; i++) { + kp_score_ += ptr->kp[ee.black_king_sq][ee.piece_state_list[i]]; + kp_score_ -= ptr->kp[ee.white_king_sq_reversed][invPieceState(ee.piece_state_list[i])]; + for (unsigned int j = i; j < PIECE_STATE_LIST_SIZE; j++) { + pp_score_ += ptr->pp[ee.piece_state_list[i]][ee.piece_state_list[j]]; + } + } + + return piece_score_; +} + +Score PositionForLearn::calcScoreDiff() { + Move move = lastMove(); + //ړʂǂAcaptureǂ4ʂŏꍇ킯 + if (kind(move.subject()) == KING) { + if (move.capture() == EMPTY) { //ʂ𓮂A͎Ȃ + if (pieceToColor(move.subject()) == BLACK) { //ʂ𓮂ꍇ + for (int i = 0; i < PIECE_STATE_LIST_SIZE; i++) { + kp_score_ -= ptr->kp[SquareToNum[move.from()]][ee_.piece_state_list[i]]; + kp_score_ += ptr->kp[SquareToNum[move.to()]][ee_.piece_state_list[i]]; + } + } else { //ʂ𓮂ꍇ + for (int i = 0; i < PIECE_STATE_LIST_SIZE; i++) { + kp_score_ += ptr->kp[SquareToNum[InvSquare[move.from()]]][invPieceState(ee_.piece_state_list[i])]; + kp_score_ -= ptr->kp[SquareToNum[InvSquare[move.to()]]][invPieceState(ee_.piece_state_list[i])]; + } + } + } else { //ʂ𓮂A + Color move_color = pieceToColor(move.subject()); + if (move_color == BLACK) { //ʂ𓮂ꍇ + PieceState captured = pieceState(move.capture(), move.to(), ~move_color); + int change_index = -1; + //captureꂽPɂ‚KP, PP + for (int i = 0; i < PIECE_STATE_LIST_SIZE; i++) { + kp_score_ -= ptr->kp[SquareToNum[move.from()]][ee_.piece_state_list[i]]; + pp_score_ -= ptr->pp[captured][ee_.piece_state_list[i]]; + if (captured == ee_.piece_state_list[i]) { + change_index = i; + } + } + //GʂƂKP + kp_score_ += ptr->kp[ee_.white_king_sq_reversed][invPieceState(captured)]; + + assert(change_index != -1); + + PieceState add = pieceState(kind(move.capture()), hand_[move_color].num(kind(move.capture())), move_color); + + //ljPɂ‚KP,PP𑫂 + for (int i = 0; i < PIECE_STATE_LIST_SIZE; i++) { + if (i == change_index) { + ee_.piece_state_list[i] = add; + } + kp_score_ += ptr->kp[SquareToNum[move.to()]][ee_.piece_state_list[i]]; + pp_score_ += ptr->pp[add][ee_.piece_state_list[i]]; + } + //GʂƂKP + kp_score_ -= ptr->kp[ee_.white_king_sq_reversed][invPieceState(add)]; + + } else { //ʂ𓮂ꍇ + PieceState captured = pieceState(move.capture(), move.to(), ~move_color); + int change_index = -1; + //captureꂽPɂ‚KP, PP + for (int i = 0; i < PIECE_STATE_LIST_SIZE; i++) { + kp_score_ += ptr->kp[SquareToNum[InvSquare[move.from()]]][invPieceState(ee_.piece_state_list[i])]; + pp_score_ -= ptr->pp[captured][ee_.piece_state_list[i]]; + if (captured == ee_.piece_state_list[i]) { + change_index = i; + } + } + //GʂƂKP + kp_score_ -= ptr->kp[ee_.black_king_sq][captured]; + + assert(change_index != -1); + + PieceState add = pieceState(kind(move.capture()), hand_[move_color].num(kind(move.capture())), move_color); + + //ljPɂ‚KP,PP𑫂 + for (int i = 0; i < PIECE_STATE_LIST_SIZE; i++) { + if (i == change_index) { + ee_.piece_state_list[i] = add; + } + kp_score_ -= ptr->kp[SquareToNum[InvSquare[move.to()]]][invPieceState(ee_.piece_state_list[i])]; + pp_score_ += ptr->pp[add][ee_.piece_state_list[i]]; + } + + //GʂƂKP + kp_score_ += ptr->kp[ee_.black_king_sq][add]; + } + } + } else if (move.isDrop()) { //łŽ + //ω̂KP, PP + //ł‚߂ɏKP,PP + PieceState dropped_from = pieceState(kind(move.subject()), hand_[pieceToColor(move.subject())].num(move.subject()) + 1, pieceToColor(move.subject())); + int change_index = -1; + //KP + kp_score_ -= ptr->kp[ee_.black_king_sq][dropped_from]; + kp_score_ += ptr->kp[ee_.white_king_sq_reversed][invPieceState(dropped_from)]; + //PP + for (int i = 0; i < PIECE_STATE_LIST_SIZE; i++) { + pp_score_ -= ptr->pp[dropped_from][ee_.piece_state_list[i]]; + if (dropped_from == ee_.piece_state_list[i]) { + change_index = i; + } + } + + assert(change_index != -1); + + PieceState dropped_to = pieceState(move.subject(), move.to(), pieceToColor(move.subject())); + + //KP + kp_score_ += ptr->kp[ee_.black_king_sq][dropped_to]; + kp_score_ -= ptr->kp[ee_.white_king_sq_reversed][invPieceState(dropped_to)]; + //PP + for (int i = 0; i < PIECE_STATE_LIST_SIZE; i++) { + if (i == change_index) { + ee_.piece_state_list[i] = dropped_to; + } + pp_score_ += ptr->pp[dropped_to][ee_.piece_state_list[i]]; + } + } else { + if (move.capture() == EMPTY) { + //ω̂KP, PP + //ł‚߂ɏKP,PP + PieceState removed_from = pieceState(move.subject(), move.from(), pieceToColor(move.subject())); + int change_index = -1; + //KP + kp_score_ -= ptr->kp[ee_.black_king_sq][removed_from]; + kp_score_ += ptr->kp[ee_.white_king_sq_reversed][invPieceState(removed_from)]; + //PP + for (int i = 0; i < PIECE_STATE_LIST_SIZE; i++) { + pp_score_ -= ptr->pp[removed_from][ee_.piece_state_list[i]]; + if (removed_from == ee_.piece_state_list[i]) { + change_index = i; + } + } + + assert(change_index != -1); + + PieceState added_to = pieceState(move.isPromote() ? promote(move.subject()) : move.subject(), move.to(), pieceToColor(move.subject())); + + //KP + kp_score_ += ptr->kp[ee_.black_king_sq][added_to]; + kp_score_ -= ptr->kp[ee_.white_king_sq_reversed][invPieceState(added_to)]; + //PP + for (int i = 0; i < PIECE_STATE_LIST_SIZE; i++) { + if (i == change_index) { + ee_.piece_state_list[i] = added_to; + } + pp_score_ += ptr->pp[added_to][ee_.piece_state_list[i]]; + } + } else { + //2P2P. + PieceState removed1 = pieceState(move.subject(), move.from(), pieceToColor(move.subject())); + PieceState removed2 = pieceState(move.capture(), move.to(), pieceToColor(move.capture())); + int change_index1 = -1, change_index2 = -1; + //KP + kp_score_ -= ptr->kp[ee_.black_king_sq][removed1]; + kp_score_ += ptr->kp[ee_.white_king_sq_reversed][invPieceState(removed1)]; + kp_score_ -= ptr->kp[ee_.black_king_sq][removed2]; + kp_score_ += ptr->kp[ee_.white_king_sq_reversed][invPieceState(removed2)]; + //PP + for (int i = 0; i < PIECE_STATE_LIST_SIZE; i++) { + pp_score_ -= ptr->pp[removed1][ee_.piece_state_list[i]]; + pp_score_ -= ptr->pp[removed2][ee_.piece_state_list[i]]; + if (removed1 == ee_.piece_state_list[i]) { + change_index1 = i; + } + if (removed2 == ee_.piece_state_list[i]) { + change_index2 = i; + } + } + //̂2Ă̂ŕ␳ + pp_score_ += ptr->pp[removed1][removed2]; + + assert(change_index1 != -1 && change_index2 != -1); + + PieceState added1 = pieceState(move.isPromote() ? promote(move.subject()) : move.subject(), move.to(), pieceToColor(move.subject())); + PieceState added2 = pieceState(kind(move.capture()), hand_[pieceToColor(move.subject())].num(move.capture()), pieceToColor(move.subject())); + + //KP + kp_score_ += ptr->kp[ee_.black_king_sq][added1]; + kp_score_ -= ptr->kp[ee_.white_king_sq_reversed][invPieceState(added1)]; + kp_score_ += ptr->kp[ee_.black_king_sq][added2]; + kp_score_ -= ptr->kp[ee_.white_king_sq_reversed][invPieceState(added2)]; + + //PP + for (int i = 0; i < PIECE_STATE_LIST_SIZE; i++) { + if (i == change_index1) { + ee_.piece_state_list[i] = added1; + } + if (i == change_index2) { + ee_.piece_state_list[i] = added2; + } + + pp_score_ += ptr->pp[added1][ee_.piece_state_list[i]]; + pp_score_ += ptr->pp[added2][ee_.piece_state_list[i]]; + } + //̂2񑫂Ă̂ŕ␳ + pp_score_ -= ptr->pp[added1][added2]; + } + } + +#ifdef DEBUG + int old_kp = kp_score_, old_pp = pp_score_; + initScore(); + if (old_kp != kp_score_ || old_pp != pp_score_) { + print(); + move.print(); + assert(false); + } +#endif + + return piece_score_ + (kp_score_ + pp_score_) / 32; +} \ No newline at end of file diff --git a/kaitei_WCSC28/pv_table.hpp b/kaitei_WCSC28/pv_table.hpp new file mode 100644 index 0000000..41778e1 --- /dev/null +++ b/kaitei_WCSC28/pv_table.hpp @@ -0,0 +1,69 @@ +#pragma once +#ifndef PV_TABLE_HPP +#define PV_TABLE_HPP + +#include"types.hpp" +#include"move.hpp" +#include + +class PVTable { +private: + Move pv_[DEPTH_MAX + 1][DEPTH_MAX + 1]; + int pv_length_[DEPTH_MAX + 1]; + +public: + PVTable() { + pv_length_[0] = 0; + } + + Move operator[](int ply) const { + return pv_[0][ply]; + } + + size_t size() const { + return pv_length_[0]; + } + + const Move* begin() const { + return &pv_[0][0]; + } + + const Move* end() const { + return begin() + size(); + } + + void closePV(int ply) { + pv_length_[ply] = ply; + } + + void clear() { + for (int i = 0; i <= DEPTH_MAX; ++i) { + pv_length_[i] = INT_MAX; + for (int j = 0; j <= DEPTH_MAX; ++j) { + pv_[i][j] = NULL_MOVE; + } + } + } + + void reset() { + //_IȃNA + pv_length_[0] = 0; + } + + void update(const Move& move, int distance_from_root) { + assert(0 <= distance_from_root && distance_from_root <= DEPTH_MAX); + + int length = pv_length_[distance_from_root + 1]; + + assert(0 <= length && length <= DEPTH_MAX); + + pv_[distance_from_root][distance_from_root] = move; + pv_length_[distance_from_root] = length; + + for (int i = distance_from_root + 1; i < length; ++i) { + pv_[distance_from_root][i] = pv_[distance_from_root + 1][i]; + } + } +}; + +#endif // !PV_TABLE_HPP \ No newline at end of file diff --git a/kaitei_WCSC28/search_stack.hpp b/kaitei_WCSC28/search_stack.hpp new file mode 100644 index 0000000..27641d6 --- /dev/null +++ b/kaitei_WCSC28/search_stack.hpp @@ -0,0 +1,29 @@ +#pragma once +#ifndef SEARCH_STACK_HPP +#define SEARCH_STACK_HPP + +#include"move.hpp" +#include"position.hpp" +#include"history.hpp" + +struct SearchStack { + Move killers[2]; + bool can_null_move; + + void updateKillers(const Move& move) { + //cf. https://qiita.com/ak11/items/0c1d20753b1073788275 + if (killers[0] != move) { + killers[1] = killers[0]; + killers[0] = move; +//#if DEBUG +// std::cout << "killers are updated" << std::endl; +// std::cout << "killers[0] = "; +// killers[0].print(); +// std::cout << "killers[1] = "; +// killers[1].print(); +//#endif + } + } +}; + +#endif // !SEARCH_STACK_HPP \ No newline at end of file diff --git a/kaitei_WCSC28/searcher.cpp b/kaitei_WCSC28/searcher.cpp new file mode 100644 index 0000000..d131b77 --- /dev/null +++ b/kaitei_WCSC28/searcher.cpp @@ -0,0 +1,1217 @@ +#include"searcher.hpp" +#include"move.hpp" +#include"move_picker.hpp" +#include"usi_options.hpp" +#include"types.hpp" +#include"shared_data.hpp" +#include +#include +#include +#include +#include +#include + +extern USIOption usi_option; + +static const Score DEFAULT_ASPIRATION_WINDOW_SIZE = Score(512); + +struct SearchLog { + unsigned int hash_cut_num; + unsigned int razoring_num; + unsigned int futility_num; + unsigned int null_move_num; + void print() const { + printf("hash_cut_num = %u\n", hash_cut_num); + printf("razoring_num = %u\n", razoring_num); + printf("futility_num = %u\n", futility_num); + printf("null_move_num = %u\n", null_move_num); + } +}; + +static SearchLog search_log; + +template +Score Searcher::NegaAlphaBeta(Position &pos, Score alpha, Score beta, Depth depth, int distance_from_root) { + // nodeの種類 + bool isRootNode = (distance_from_root == 0); + + //深さ最大まで来ていたら静止探索する仕様 + //if (depth <= 0) return qsearch(pos, alpha, beta, Depth(0), distance_from_root); + + //----------------------------- + // Step1. 初期化 + //----------------------------- + + //assert類 + assert(isPVNode || (alpha + 1 == beta)); + assert(MIN_SCORE <= alpha && alpha < beta && beta <= MAX_SCORE); + + //fprintf(stderr, "start search(alpha = %5d, beta = %5d, depth = %3d, ply = %3d, %s)\n", alpha, beta, depth, ss->distance_from_root, isPVNode ? "PV" : "nonPV"); + + //探索局面数を増やす + ++node_number_; + + if (isPVNode) { + seldepth_ = std::max(seldepth_, static_cast(distance_from_root)); + } + + //----------------------------- + // RootNode以外での処理 + //----------------------------- + + //thinkの方で1手動かしているのでここにルートノードはない + //ルートノードも入れた方が効率いいんだろうか。多分そうなんだろうな + + if (!isRootNode) { + + //----------------------------- + // Step2. 探索の停止と引き分けの確認 + //----------------------------- + + //停止確認 + if (shouldStop()) { + return SCORE_ZERO; + } + + //引き分けの確認 + //現局面が4手前と一致し、現局面と2手前が王手かどうか見ているだけ + //これでうまくいっているか? + Score repeate_score; + if (pos.isRepeating(repeate_score)) { + pv_table_.closePV(distance_from_root); + return repeate_score; + } + + //----------------------------- + // Step3. Mate distance pruning + //----------------------------- + + //合ってるのか怪しいぞ + alpha = std::max(MIN_SCORE + static_cast(distance_from_root), alpha); + beta = std::min(MAX_SCORE - static_cast(distance_from_root + 1), beta); + if (alpha >= beta) { + return alpha; + } + } + + //----------------------------- + // SearchStackの初期化 + //----------------------------- + + SearchStack* ss = searchInfoAt(distance_from_root); + (ss + 1)->killers[0] = (ss + 1)->killers[1] = NULL_MOVE; + (ss + 1)->can_null_move = true; + + //----------------------------- + // Step4. 置換表を見る + //----------------------------- + + //除外する手(excluded_move)は設定していない + //Singular extensionで使うやつか + + //posKeyをわざわざ変数として取り出す意味はあるんだろうか + + Move tt_move = NULL_MOVE; + + //----------------------------- + // Step5. 局面の静的評価 + //----------------------------- + + Score static_score = (pos.color() == BLACK ? pos.score() : -pos.score()); + + //王手がかかっているときは下の枝刈りはしない + if (pos.isKingChecked()) { + goto moves_loop; + } + + //----------------------------- + // static_scoreによる枝刈り + //----------------------------- + + //----------------------------- + // Step6. Razoring + //----------------------------- + if (!isPVNode + && depth < 4 * PLY + && static_score + depthMargin(depth) <= alpha) { + if (depth <= 1 * PLY) { + search_log.razoring_num++; + return qsearch(pos, alpha, beta, Depth(0), distance_from_root); + } + + Score ralpha = alpha - depthMargin(depth); + Score v = qsearch(pos, ralpha, ralpha + 1, Depth(0), distance_from_root); + if (v <= ralpha) { + search_log.razoring_num++; + return v; + } + } + //----------------------------- + // Step7. Futility pruning + //----------------------------- + + //子ノードでのっていうのはどういうことなんだろう + //上のRazoringと対応関係にあると思うんだけど、マージンとかはそろえるべきなのか + if (depth < 7 * PLY && (static_score - depthMargin(depth) >= beta)) { + search_log.futility_num++; + return static_score; + } + + + //----------------------------- + // Step8. Null Move Pruning + //----------------------------- + + //うーん、弱くなるなぁ。なぜだかよくわからない + + if (!isPVNode + && static_score >= beta + && depth >= 2 * PLY + && ss->can_null_move) { + Depth rdepth = depth / 2; + + //2回連続null_moveになるのを避ける + (ss + 1)->can_null_move = false; + pos.doNullMove(); + Score null_score = -search(pos, -beta, -beta + 1, rdepth, distance_from_root + 1); + pos.undoNullMove(); + (ss + 1)->can_null_move = true; + if (null_score >= beta) { + search_log.null_move_num++; + return null_score; + } + } + +moves_loop: + + //Counter Move Historyやらなにやら + //Gainとか + + std::vector quiet_moves; + quiet_moves.reserve(600); + + int move_count = 0; + + //指し手を生成 + MovePicker mp(pos, tt_move, depth, history_, ss->killers); + Score best_score = MIN_SCORE + distance_from_root; + Move best_move = NULL_MOVE; + + //----------------------------- + // Step11. Loop through moves + //----------------------------- + + for (Move current_move = mp.nextMove(); current_move != NULL_MOVE; current_move = mp.nextMove()) { + //ルートノードでしか参照されない + std::vector::iterator root_move_itr; + + if (isRootNode) { + root_move_itr = std::find(root_moves_.begin(), root_moves_.end(), current_move); + if (root_move_itr == root_moves_.end()) { + //root_moves_に存在しなかったらおかしいので次の手へ + continue; + } + } + + ++move_count; + + //----------------------------- + // extension + //----------------------------- + + //ない + + //----------------------------- + // Step12. Singular延長と王手延長 + //----------------------------- + + //----------------------------- + // 1手進める前の枝刈り + //----------------------------- + + //探索深さの増減もここで決めたり + //Depth new_depth = depth >= 2 ? depth - 1 : depth; + + //----------------------------- + // Step13. 浅い深さでの枝刈り + //----------------------------- + + //Historyとかmove_countに基づいていろいろやるらしい + //浅い深さというかMoveの性質に依存した枝刈り? + + //----------------------------- + // Step14. 1手進める + //----------------------------- + + //合法性判定は必要かどうか + //今のところ合法手しかこないはずだけど + +#if DEBUG + if (!pos.isLegalMove(current_move)) { + pos.isLegalMove(current_move); + current_move.print(); + pos.printForDebug(); + assert(false); + } +#endif + pos.doMove(current_move); + + if (isPVNode) { + pv_table_.closePV(distance_from_root + 1); + } + + Score score; + bool shouldSearchFullDepth = true; + + //----------------------------- + // Step15. Move countに応じてdepthを減らした探索(Late Move Reduction) + //----------------------------- + + //これめちゃくちゃバグってるっぽい + if (depth >= PLY * 4 && move_count >= 5) { + //alphaを更新しそうか確認 + //単に-2(普通はマイナス1だからそれよりも1手浅いということ) + //雑なreductionにもほどがある + Depth new_depth = depth - PLY - move_count * PLY / 30; + score = (new_depth < PLY ? -qsearch(pos, -alpha - 1, -alpha, Depth(0), distance_from_root + 1) + : -NegaAlphaBeta(pos, -alpha - 1, -alpha, new_depth - PLY, distance_from_root + 1)); + shouldSearchFullDepth = (score > alpha); + } + + //----------------------------- + // Step16. Full Depth Search + //----------------------------- + + if (shouldSearchFullDepth) { + //Null Window Searchでalphaを超えそうか確認 + //これ入れた方がいいのかなぁ + score = (depth < PLY ? -qsearch(pos, -alpha - 1, -alpha, Depth(0), distance_from_root + 1) + : -NegaAlphaBeta(pos, -alpha - 1, -alpha, depth - PLY, distance_from_root + 1)); + + if (alpha < score && score < beta) { + //isPVNodeって条件いるかな + //nonPVならalpha + 1 == betaとなっているはずだからいらない気がする + + //いい感じのスコアだったので再探索 + score = (depth < PLY ? -qsearch(pos, -beta, -alpha, Depth(0), distance_from_root + 1) + : -NegaAlphaBeta(pos, -beta, -alpha, depth - PLY, distance_from_root + 1)); + } + //#endif + + } + + //----------------------------- + // Step17. 1手戻す + //----------------------------- + pos.undo(); + + //----------------------------- + // Step18. 停止確認 + //----------------------------- + + //停止確認 + if (shouldStop()) { + return Score(0); + } + + //----------------------------- + // 探索された値によるalpha更新 + //----------------------------- + if (score > best_score) { + if (isRootNode) { + //ルートノードならスコアを更新しておく + root_move_itr->score = score; + } + + best_score = score; + best_move = current_move; + pv_table_.update(best_move, distance_from_root); + + if (score >= beta) { + //fail-high + break; //betaカット + } else if (score > alpha) { + alpha = std::max(alpha, best_score); + } + } + if (current_move.isQuiet()) { + quiet_moves.push_back(current_move); + } + } + + //----------------------------- + // Step20. 詰みの確認 + //----------------------------- + + //打ち歩の処理はここでやった方がいい + //↑ほんとか? + + if (move_count == 0) { + //詰みということ + return MIN_SCORE + distance_from_root; + } + + if (best_move == NULL_MOVE) { + //fail-low + //なにかやるべきことはあるか + } else if (best_move.isQuiet()) { + history_.updateBetaCutMove(best_move, depth); + ss->updateKillers(best_move); + + for (auto quiet_move : quiet_moves) { + history_.updateNonBetaCutMove(quiet_move, depth); + } + } + + return best_score; +} + +void Searcher::think() { + //思考開始時間をセット + start_ = std::chrono::steady_clock::now(); + + //コピーして使う + Position root = shared_data.root; + + if (role_ == MATE) { + search_mate(root); + return; + } + + //思考する局面の表示 + if (role_ == MAIN) { + root.print(); + } + + //History初期化 + history_.clear(); + + resetPVTable(); + + //ルート局面の合法手を設定 + root_moves_ = root.generateAllMoves(); + + SearchStack* ss = searchInfoAt(0); + ss->killers[0] = ss->killers[1] = NULL_MOVE; + ss->can_null_move = true; + + //合法手が0だったら投了 + if (root_moves_.size() == 0) { + if (role_ == MAIN) { + std::cout << "bestmove resign" << std::endl; + } + return; + } + + //合法手が1つだったらすぐ送る + //これ別にいらないよな + //これもUSIオプション化した方が良いか + //if (root_moves_.size() == 1) { + // if (role_ == MAIN) { + // std::cout << "bestmove " << root_moves_[0] << std::endl; + // } + // return; + //} + + //指定された手数まで完全ランダムに指す + static std::random_device rd; + if (root.turn_number() + 1 <= usi_option.random_turn) { + if (role_ == MAIN) { + int rnd = rd() % root_moves_.size(); + std::cout << "bestmove " << root_moves_[rnd] << std::endl; + } + return; + } + + //探索局面数を初期化 + node_number_ = 0; + + //探索 + //反復深化 + static const Score DEFAULT_ASPIRATION_WINDOW_SIZE = Score(64); + Score aspiration_window_size = DEFAULT_ASPIRATION_WINDOW_SIZE; + Score best_score, alpha, beta, previous_best_score; + + for (Depth depth = PLY * 1; depth <= DEPTH_MAX; depth += PLY) { + if (role_ == SLAVE) { //Svaleスレッドは探索深さを深くする + static std::mt19937 mt(rd()); + static std::uniform_int_distribution<> distribution(0, 2); + depth += distribution(mt) * PLY; + } + + //seldepth_の初期化 + seldepth_ = depth; + + //探索窓の設定 + if (depth <= 4 * PLY) { //深さ4まではASPIRATION_WINDOWを使わずフルで探索する + alpha = MIN_SCORE; + beta = MAX_SCORE; + } else { + alpha = std::max(previous_best_score - aspiration_window_size, MIN_SCORE); + beta = std::min(previous_best_score + aspiration_window_size, MAX_SCORE); + } + + while (!shouldStop()) { //exactな評価値が返ってくるまでウィンドウを広げつつ探索 + //指し手のスコアを最小にしておかないと変になる + for (auto& root_move : root_moves_) { + root_move.score = MIN_SCORE; + } + + best_score = search(root, alpha, beta, depth, 0); + + //history_.print(); + + //詰んでいたら抜ける + if (isMatedScore(best_score) || shouldStop()) { + break; + } + + if (best_score <= alpha) { + //fail-low + if (role_ == MAIN) { + printf("aspiration fail-low, alpha = %d\n", alpha); + sendInfo(depth, "cp", best_score, UPPER_BOUND); + } + + beta = (alpha + beta) / 2; + alpha -= aspiration_window_size; + aspiration_window_size *= 3; + } else if (best_score >= beta) { + //fail-high + if (role_ == MAIN) { + printf("aspiration fail-high, beta = %d\n", beta); + sendInfo(depth, "cp", best_score, LOWER_BOUND); + } + + alpha = (alpha + beta) / 2; + beta += aspiration_window_size; + aspiration_window_size *= 3; + } else { + aspiration_window_size = DEFAULT_ASPIRATION_WINDOW_SIZE; + break; + } + } + + //停止確認してダメだったら保存せずループを抜ける + if (shouldStop()) { + break; + } + + //指し手の並び替え + std::stable_sort(root_moves_.begin(), root_moves_.end(), std::greater()); + + //GUIへ読みの情報を送る + if (role_ == MAIN) { + if (MATE_SCORE_UPPER_BOUND < root_moves_[0].score && root_moves_[0].score < MATE_SCORE_LOWER_BOUND) { + //詰みなし + sendInfo(depth, "cp", root_moves_[0].score, EXACT_BOUND); + } else { + //詰みあり + Score mate_num = MAX_SCORE - std::abs(root_moves_[0].score); + mate_num *= (mate_num % 2 == 0 ? -1 : 1); + sendInfo(depth, "mate", mate_num, EXACT_BOUND); + } + } + + //置換表への保存 + shared_data.hash_table.save(root.hash_value(), root_moves_[0], root_moves_[0].score, depth); + + //詰みがあったらすぐ返す + if (isMatedScore(root_moves_[0].score)) { + break; + } + + //今回のイテレーションにおけるスコアを記録 + previous_best_score = best_score; + + //PVtableをリセットしていいよな? + resetPVTable(); + } + + //GUIへBestMoveの情報を送る + if (role_ == MAIN) { + std::cout << "bestmove " << root_moves_[0] << std::endl; + + //ログを出力 + search_log.print(); + } +} + +Move Searcher::thinkForGenerateLearnData(Position& root, Depth fixed_depth, unsigned int random_turn) { + //思考開始時間をセット + start_ = std::chrono::steady_clock::now(); + + //History初期化 + history_.clear(); + + //PV初期化 + resetPVTable(); + + //ルート局面の合法手を設定 + root_moves_ = root.generateAllMoves(); + + SearchStack* ss = searchInfoAt(0); + ss->killers[0] = ss->killers[1] = NULL_MOVE; + ss->can_null_move = true; + + //合法手が0だったら投了 + if (root_moves_.size() == 0) { + return NULL_MOVE; + } + + //指定された手数まで完全ランダムに指す + static std::random_device rd; + if (root.turn_number() + 1 <= random_turn) { + int rnd = rd() % root_moves_.size(); + root_moves_[rnd].score = MIN_SCORE; + return root_moves_[rnd]; + } + + //探索局面数を初期化 + node_number_ = 0; + + for (auto& root_move : root_moves_) { + root_move.score = MIN_SCORE; + } + + //Score best_score = search(root, MIN_SCORE, MAX_SCORE, fixed_depth * (int)PLY, 0); + Score best_score = (fixed_depth < PLY ? qsearch(root, MIN_SCORE, MAX_SCORE, Depth(0), 0) : NegaAlphaBeta(root, MIN_SCORE, MAX_SCORE, fixed_depth, 0)); + + return *std::max_element(root_moves_.begin(), root_moves_.end()); +} + +template +Score Searcher::qsearch(Position &pos, Score alpha, Score beta, Depth depth, int distance_from_root) { + + // ----------------------- + // 最初のチェック + // ----------------------- + + pv_table_.closePV(distance_from_root); + + assert(isPVNode || alpha + 1 == beta); + assert(0 <= distance_from_root && distance_from_root <= DEPTH_MAX); + + // ----------------------- + // 変数宣言とかnodeの初期化 + // ----------------------- + + Move best_move = NULL_MOVE; + Score old_alpha = alpha; + + SearchStack* ss = searchInfoAt(distance_from_root); + + //探索局面数を増やす + node_number_++; + + seldepth_ = std::max(seldepth_, static_cast(distance_from_root)); + //fprintf(stderr, "start qsearch(alpha = %5d, beta = %5d, depth = %3d, ply = %3d, %s)\n", alpha, beta, depth, ss->distance_from_root, isPVNode ? "PV" : "nonPV"); + + // ----------------------- + // 停止確認 + // ----------------------- + + //ここでやる必要があるのかはわからない + if (shouldStop()) return SCORE_ZERO; + + // ----------------------- + // 置換表を見る + // ----------------------- + auto hash_entry = shared_data.hash_table.find(pos.hash_value()); + Score tt_score = hash_entry ? hash_entry->best_move_.score : MIN_SCORE; + + //tt_moveの合法性判定はここでやらなくてもMovePickerで確認するはず + Move tt_move = hash_entry ? hash_entry->best_move_ : NULL_MOVE; + Depth tt_depth = hash_entry ? hash_entry->depth_ : Depth(0); + + //置換表による枝刈り + if (!pos.isKingChecked()) { //この条件いる? + //置換表での枝刈り + if (!isPVNode + && hash_entry + && tt_depth >= depth + && tt_score >= beta) { + search_log.hash_cut_num++; + return tt_score; + } + } + + //指し手を生成 + MovePicker mp(pos, tt_move, depth, history_); + Score best_score = (pos.isKingChecked() ? MIN_SCORE + distance_from_root //王手がかかっていたら最低点から始める + : pos.color() == BLACK ? pos.score() : -pos.score()); //そうでなかったら評価関数を呼び出した値から始める + int move_count = 0; + for (Move current_move = mp.nextMove(); current_move != NULL_MOVE; current_move = mp.nextMove()) { + //停止確認 + if (shouldStop()) { + return Score(0); + } + + //Null Window Searchはした方がいいんだろうか.今はしてない + + pos.doMove(current_move); + Score score = -qsearch(pos, -beta, -alpha, depth - 1, distance_from_root + 1); + + if (score > best_score) { + best_score = score; + best_move = current_move; + + if (best_score >= beta) { + //fail-high + pos.undo(); + //静止探索でQuietな指し手になるわけがなくない? + //fail-highとして置換表にsaveするべき? + return best_score; //betaカット + } + + pv_table_.update(best_move, distance_from_root); + + alpha = std::max(alpha, best_score); + } + + pos.undo(); + + ++move_count; + } + + if (best_move != NULL_MOVE) { + //fail-lowではない + //置換表への保存 todo:old_alphaと比較して値の信頼性を付け加えるべき + //静止探索の結果なんて保存しても仕方なくない? + //抜いてみる + //shared_data.hash_table.save(pos.hash_value(), best_move, best_score, Depth(0)); + } + + return best_score; +} + +bool Searcher::search_check(Position &pos, Depth depth) { + //時間を確認 + if (shouldStop()) return false; + + //局面数を増やす + node_number_++; + + //王手を生成 + Move check_moves[MAX_MOVE_LIST_SIZE]; + Move* end_ptr = check_moves; + pos.generateCheckMoveBB(end_ptr); + + //可能な王手の数が0だったら詰まない + if (end_ptr == check_moves) return false; + + //深さ最大まで来ていてまだ合法手があるなら不詰み + //王手側で深さ最大になることはないはずだけど一応 + if (depth <= 0) return false; + + //全ての王手について1手読みを深める + for (Move* start_ptr = check_moves; start_ptr < end_ptr; start_ptr++) { + pos.doMove(*start_ptr); + if (search_evasion(pos, depth - 1)) { //詰み + //PV更新 + pv_table_.update(*start_ptr, depth); + + //局面を戻す + pos.undo(); + + return true; + } + pos.undo(); + } + + return false; +} + +bool Searcher::search_evasion(Position &pos, Depth depth) { + //時間を確認 + if (shouldStop()) return false; + + //局面数を増やす + node_number_++; + + //指し手を全て生成 + std::vector move_list; + Move evasion_moves[MAX_MOVE_LIST_SIZE]; + Move* move_ptr = evasion_moves; + pos.generateEvasionMovesBB(move_ptr); + + //番兵を設置 + *move_ptr = NULL_MOVE; + + //可能な指し手の数が0だったらtrueを返す + if (evasion_moves[0] == NULL_MOVE) return true; + + //深さ最大まで来ていてまだ合法手があるなら不詰み + if (depth <= 0) return false; + + //全ての指し手について1手読みを深める + for (Move* current_move = &evasion_moves[0]; *current_move != NULL_MOVE; ++current_move) { + pos.doMove(*current_move); + if (!search_check(pos, depth - 1)) { //詰まない逃れ方がある + pos.undo(); + return false; + } + pos.undo(); + } + + //どの逃げ方でも詰んでいたら詰み + return true; +} + +void Searcher::sendInfo(Depth depth, std::string cp_or_mate, Score score, Bound bound) { + auto now_time = std::chrono::steady_clock::now(); + auto elapsed = std::chrono::duration_cast(now_time - start_); + std::cout << "info time " << elapsed.count() << " "; + + //GUIへ読み途中の情報を返す + std::cout << "depth " << depth / PLY << " "; + std::cout << "seldepth " << seldepth_ / PLY << " "; + std::cout << "nodes " << node_number_ << " "; + std::cout << "score " << cp_or_mate << " " << score << " "; + if (bound == UPPER_BOUND) std::cout << "upperbound "; + else if (bound == LOWER_BOUND) std::cout << "lowerbound "; + std::cout << "pv "; + if (pv_table_.size() == 0) { + pv_table_.update(root_moves_[0], 0); + } + for (auto move : pv_table_) { + std::cout << move << " "; + } + std::cout << std::endl; + std::cout << "info nps " << static_cast(node_number_) / elapsed.count() * 1000.0 << "\n"; + std::cout << "info hashfull " << shared_data.hash_table.hashfull() << "\n"; +} + +void Searcher::search_mate(Position& root) { + for (Depth depth = Depth(1); ; depth += 2) { + if (shouldStop()) { + break; + } + + if (search_check(root, depth)) { + std::cout << "詰み" << std::endl; + + //詰みを発見したので他に動いているスレッドを止める + shared_data.stop_signal = true; + + //詰みを発見したというシグナルそのものも必要そうな気がする + //shared_data.find_mate = true; + + //GUIに読み筋を送る + sendInfo(depth, "mate", static_cast(depth), EXACT_BOUND); + + //GUIにbest_moveを送る + //std::cout << "bestmove " << mate_move << std::endl; + } + } +} + +#define OMIT_PRUNINGS + +template +Score Searcher::search(Position &pos, Score alpha, Score beta, Depth depth, int distance_from_root) { + // nodeの種類 + bool isRootNode = (distance_from_root == 0); + + //----------------------------- + // Step1. 初期化 + //----------------------------- + + //assert類 + assert(isPVNode || (alpha + 1 == beta)); + assert(MIN_SCORE <= alpha && alpha < beta && beta <= MAX_SCORE); + + //fprintf(stderr, "start search(alpha = %5d, beta = %5d, depth = %3d, ply = %3d, %s)\n", alpha, beta, depth, ss->distance_from_root, isPVNode ? "PV" : "nonPV"); + + //探索局面数を増やす + ++node_number_; + + if (isPVNode) { + seldepth_ = std::max(seldepth_, static_cast(distance_from_root)); + } + + //----------------------------- + // RootNode以外での処理 + //----------------------------- + + //thinkの方で1手動かしているのでここにルートノードはない + //ルートノードも入れた方が効率いいんだろうか。多分そうなんだろうな + + if (!isRootNode) { + + //----------------------------- + // Step2. 探索の停止と引き分けの確認 + //----------------------------- + + //停止確認 + if (shouldStop()) { + return SCORE_ZERO; + } + + //引き分けの確認 + //現局面が4手前と一致し、現局面と2手前が王手かどうか見ているだけ + //これでうまくいっているか? + Score repeate_score; + if (pos.isRepeating(repeate_score)) { + pv_table_.closePV(distance_from_root); + return repeate_score; + } + + //----------------------------- + // Step3. Mate distance pruning + //----------------------------- + + //合ってるのか怪しいぞ + alpha = std::max(MIN_SCORE + static_cast(distance_from_root), alpha); + beta = std::min(MAX_SCORE - static_cast(distance_from_root + 1), beta); + if (alpha >= beta) { + return alpha; + } + } + + //----------------------------- + // SearchStackの初期化 + //----------------------------- + + SearchStack* ss = searchInfoAt(distance_from_root); + (ss + 1)->killers[0] = (ss + 1)->killers[1] = NULL_MOVE; + (ss + 1)->can_null_move = true; + + //----------------------------- + // Step4. 置換表を見る + //----------------------------- + + //除外する手(excluded_move)は設定していない + //Singular extensionで使うやつか + + auto hash_entry = shared_data.hash_table.find(pos.hash_value()); + Score tt_score = hash_entry ? hash_entry->best_move_.score : MIN_SCORE; + + //tt_moveの合法性判定はここでやらなくてもMovePickerで確認するはず + Move tt_move = hash_entry ? hash_entry->best_move_ : NULL_MOVE; + Depth tt_depth = hash_entry ? hash_entry->depth_ : Depth(0); + + //置換表の値による枝刈り + if (!isPVNode + && hash_entry + && tt_depth >= depth + && tt_score >= beta) { + + //tt_moveがちゃんとしたMoveならこれでHistory更新 + if (tt_move != NULL_MOVE) { + history_.updateBetaCutMove(tt_move, depth); + } + search_log.hash_cut_num++; + return tt_score; + } + + //----------------------------- + // 宣言勝ち + //----------------------------- + + //まだない + + //----------------------------- + // 1手詰み判定 + //----------------------------- + + //まだない + + //----------------------------- + // Step5. 局面の静的評価 + //----------------------------- + + Score static_score = (hash_entry ? tt_score : (pos.color() == BLACK ? pos.score() : -pos.score())); + + //王手がかかっているときは下の枝刈りはしない + if (pos.isKingChecked()) { + goto moves_loop; + } + + //----------------------------- + // static_scoreによる枝刈り + //----------------------------- + + //----------------------------- + // Step6. Razoring + //----------------------------- + if (!isPVNode + && depth < 4 * PLY + && static_score + depthMargin(depth) <= alpha) { + if (depth <= 1 * PLY) { + search_log.razoring_num++; + return qsearch(pos, alpha, beta, Depth(0), distance_from_root); + } + + Score ralpha = alpha - depthMargin(depth); + Score v = qsearch(pos, ralpha, ralpha + 1, Depth(0), distance_from_root); + if (v <= ralpha) { + search_log.razoring_num++; + return v; + } + } + //----------------------------- + // Step7. Futility pruning + //----------------------------- + + //子ノードでのっていうのはどういうことなんだろう + //上のRazoringと対応関係にあると思うんだけど、マージンとかはそろえるべきなのか + if (depth < 7 * PLY && (static_score - depthMargin(depth) >= beta)) { + search_log.futility_num++; + return static_score; + } + + + //----------------------------- + // Step8. Null Move Pruning + //----------------------------- + + //うーん、弱くなるなぁ。なぜだかよくわからない + + if (!isPVNode + && static_score >= beta + && depth >= 2 * PLY + && ss->can_null_move) { + Depth rdepth = depth / 2; + + //2回連続null_moveになるのを避ける + (ss + 1)->can_null_move = false; + pos.doNullMove(); + Score null_score = -search(pos, -beta, -beta + 1, rdepth, distance_from_root + 1); + pos.undoNullMove(); + (ss + 1)->can_null_move = true; + if (null_score >= beta) { + search_log.null_move_num++; + return null_score; + } + } + +#ifndef OMIT_PRUNINGS + //----------------------------- + // Step9. ProbCut + //----------------------------- + + if (!isPVNode + && depth >= 5 * PLY) { + Score rbeta = std::min(beta + 300, MAX_SCORE); + Depth rdepth = depth - 4; + MovePicker mp(pos, tt_move, rdepth, history_, ss->killers); + for (Move move = mp.nextMove(); move != NULL_MOVE; move = mp.nextMove()) { + pos.doMove(move); + Score score = -search(pos, -rbeta, -rbeta + 1, rdepth, distance_from_root + 1); + pos.undo(); + if (score >= rbeta) { + return score; + } + } + } +#endif + + //----------------------------- + // Step10. 多重反復深化 + //----------------------------- + + //まだない + //あまり必要性も感じない + //いや、本質的なのでは? + if (depth >= 7 * PLY + && tt_move == NULL_MOVE + && (isPVNode || static_score + 256 >= beta)) { + Depth d = (depth * 3 / 4) - 2 * PLY; + ss->can_null_move = false; + search(pos, alpha, beta, d, distance_from_root); + ss->can_null_move = true; + + hash_entry = shared_data.hash_table.find(pos.hash_value()); + tt_move = hash_entry ? hash_entry->best_move_ : NULL_MOVE; + } + +moves_loop: + + //Counter Move Historyやらなにやら + //Gainとか + + std::vector quiet_moves; + quiet_moves.reserve(600); + + int move_count = 0; + + //指し手を生成 + MovePicker mp(pos, tt_move, depth, history_, ss->killers); + Score best_score = MIN_SCORE + distance_from_root; + Move best_move = NULL_MOVE; + + //----------------------------- + // Step11. Loop through moves + //----------------------------- + + for (Move current_move = mp.nextMove(); current_move != NULL_MOVE; current_move = mp.nextMove()) { + //ルートノードでしか参照されない + std::vector::iterator root_move_itr; + + if (isRootNode) { + root_move_itr = std::find(root_moves_.begin(), root_moves_.end(), current_move); + if (root_move_itr == root_moves_.end()) { + //root_moves_に存在しなかったらおかしいので次の手へ + continue; + } + } + + ++move_count; + + //----------------------------- + // extension + //----------------------------- + + //ない + + //----------------------------- + // Step12. Singular延長と王手延長 + //----------------------------- + + //----------------------------- + // 1手進める前の枝刈り + //----------------------------- + + //探索深さの増減もここで決めたり + //Depth new_depth = depth >= 2 ? depth - 1 : depth; + + //----------------------------- + // Step13. 浅い深さでの枝刈り + //----------------------------- + + //Historyとかmove_countに基づいていろいろやるらしい + //浅い深さというかMoveの性質に依存した枝刈り? + + //----------------------------- + // Step14. 1手進める + //----------------------------- + + //合法性判定は必要かどうか + //今のところ合法手しかこないはずだけど + +#if DEBUG + if (!pos.isLegalMove(current_move)) { + pos.isLegalMove(current_move); + current_move.print(); + pos.printForDebug(); + assert(false); + } +#endif + + pos.doMove(current_move); + + if (isPVNode) { + pv_table_.closePV(distance_from_root + 1); + } + + Score score; + bool shouldSearchFullDepth = true; + + //----------------------------- + // Step15. Move countに応じてdepthを減らした探索(Late Move Reduction) + //----------------------------- + + //これめちゃくちゃバグってるっぽい + if (depth >= PLY * 4 && move_count >= 5) { + //alphaを更新しそうか確認 + //雑なreductionにもほどがある + Depth new_depth = depth - move_count * PLY / 30; + score = (new_depth < PLY ? -qsearch(pos, -alpha - 1, -alpha, Depth(0), distance_from_root + 1) + : -search(pos, -alpha - 1, -alpha, new_depth - PLY, distance_from_root + 1)); + shouldSearchFullDepth = (score > alpha); + } + + //----------------------------- + // Step16. Full Depth Search + //----------------------------- + + if (shouldSearchFullDepth) { + //Null Window Searchでalphaを超えそうか確認 + //これ入れた方がいいのかなぁ + score = (depth < PLY ? -qsearch(pos, -alpha - 1, -alpha, Depth(0), distance_from_root + 1) + : -search(pos, -alpha - 1, -alpha, depth - PLY, distance_from_root + 1)); + + if (alpha < score && score < beta) { + //isPVNodeって条件いるかな + //nonPVならalpha + 1 == betaとなっているはずだからいらない気がする + + //いい感じのスコアだったので再探索 + score = (depth < PLY ? -qsearch(pos, -beta, -alpha, Depth(0), distance_from_root + 1) + : -search(pos, -beta, -alpha, depth - PLY, distance_from_root + 1)); + } + //#endif + + } + + //----------------------------- + // Step17. 1手戻す + //----------------------------- + pos.undo(); + + //----------------------------- + // Step18. 停止確認 + //----------------------------- + + //停止確認 + if (shouldStop()) { + return Score(0); + } + + //----------------------------- + // 探索された値によるalpha更新 + //----------------------------- + if (score > best_score) { + if (isRootNode) { + //ルートノードならスコアを更新しておく + root_move_itr->score = score; + } + + best_score = score; + best_move = current_move; + pv_table_.update(best_move, distance_from_root); + + if (score >= beta) { + //fail-high + break; //betaカット + } else if (score > alpha) { + alpha = std::max(alpha, best_score); + } + } + if (current_move.isQuiet()) { + quiet_moves.push_back(current_move); + } + } + + //----------------------------- + // Step20. 詰みの確認 + //----------------------------- + + //打ち歩の処理はここでやった方がいい + //↑ほんとか? + + if (move_count == 0) { + //詰みということ + //置換表に保存しても仕方ないのでさっさと値を返す + return MIN_SCORE + distance_from_root; + } + + if (best_move == NULL_MOVE) { + //fail-low + //なにかやるべきことはあるか + } else if (best_move.isQuiet()) { + history_.updateBetaCutMove(best_move, depth); + ss->updateKillers(best_move); + + for (auto quiet_move : quiet_moves) { + history_.updateNonBetaCutMove(quiet_move, depth); + } + } + + //----------------------------- + // 置換表に保存 + //----------------------------- + shared_data.hash_table.save(pos.hash_value(), best_move, best_score, depth); + + return best_score; +} \ No newline at end of file diff --git a/kaitei_WCSC28/searcher.hpp b/kaitei_WCSC28/searcher.hpp new file mode 100644 index 0000000..9e1688a --- /dev/null +++ b/kaitei_WCSC28/searcher.hpp @@ -0,0 +1,141 @@ +#ifndef SEARCH_HPP +#define SEARCH_HPP + +#include"position.hpp" +#include"move.hpp" +#include"hash_table.hpp" +#include"usi_options.hpp" +#include"shared_data.hpp" +#include"search_stack.hpp" +#include"pv_table.hpp" +#include + +class Searcher { +public: + //役割 + enum Role { + MAIN, SLAVE, MATE + }; + + Searcher(Role r) : node_number_(0), start_(std::chrono::steady_clock::now()), role_(r) {} + + void think(); + Move thinkForGenerateLearnData(Position &root, Depth fixed_depth, unsigned int random_turn); + + //単純なAlphaBeta.学習に使う + template + Score NegaAlphaBeta(Position &pos, Score alpha, Score beta, Depth depth, int distance_from_root); + + //探索で用いる関数 + template + Score search(Position &pos, Score alpha, Score beta, Depth depth, int distance_from_root); + + //詰み探索:今は使っていない + void search_mate(Position& root); + bool search_check(Position &pos, Depth depth); + bool search_evasion(Position &pos, Depth depth); + + //静止探索 + template + Score qsearch(Position &pos, Score alpha, Score beta, Depth depth, int distance_from_root); + + //GUIへの情報 + void sendInfo(Depth depth, std::string cp_or_mate, Score score, Bound bound); + + //時間を見る.TimeManagerのclassを作るべきなのかはよくわからない. + inline bool isTimeOver() { + auto now_time = std::chrono::steady_clock::now(); + auto elapsed = std::chrono::duration_cast(now_time - start_); + return (elapsed.count() >= shared_data.limit_msec - usi_option.byoyomi_margin); + } + + void setRole(Role r) { + role_ = r; + } + + inline bool shouldStop() { + //TODO : 探索深さの制限も加えるべき + if (role_ == MAIN) { //MainThreadなら時間の確認と停止信号の確認 + if (isTimeOver() || shared_data.stop_signal) { + //停止信号をオンにする + shared_data.stop_signal = true; + return true; + } + } else { //SlaveThreadなら停止信号だけを見る + if (shared_data.stop_signal) { + return true; + } + } + return false; + } + + SearchStack* searchInfoAt(int distance_from_root) { + //深さ0でも前二つが参照できるようにずらしておかなければならない + return &stack_[distance_from_root + 2]; + } + + std::vector pv() { + std::vector pv; + for (Move m : pv_table_) + pv.push_back(m); + +#if DEBUG + Position pos = root_; + for (Move m : pv) { + if (pos.isLegalMove(m)) { + pos.doMove(m); + } else { + std::cout << "root" << std::endl; + root_.print(); + std::cout << "pos" << std::endl; + pos.print(); + std::cout << "pv" << std::endl; + for (Move s : pv) { + s.print(); + } + assert(false); + } + } +#endif // DEBUG + + return pv; + } + + void setRoot(const Position& pos) { + root_ = pos; + root_moves_ = pos.generateAllMoves(); + } + + void resetPVTable() { + pv_table_.reset(); + } + + inline static int depthMargin(int depth) { return 64 + 32 * depth / PLY; } + +private: + //探索局面数 + long long node_number_; + + //時間 + std::chrono::steady_clock::time_point start_; + + //役割 + Role role_; + + //History + History history_; + + //root_moves_は持っておいた方が良さそう + std::vector root_moves_; + + //Seldpth + Depth seldepth_; + + PVTable pv_table_; + + SearchStack stack_[DEPTH_MAX]; + + Position root_; +}; + +#endif \ No newline at end of file diff --git a/kaitei_WCSC28/shared_data.hpp b/kaitei_WCSC28/shared_data.hpp new file mode 100644 index 0000000..4db118f --- /dev/null +++ b/kaitei_WCSC28/shared_data.hpp @@ -0,0 +1,17 @@ +#pragma once +#ifndef SHARED_DATA_HPP +#define SHARED_DATA_HPP + +#include"hash_table.hpp" +#include + +struct SharedData { + std::atomic stop_signal; + HashTable hash_table; + Position root; + long long limit_msec; +}; + +extern SharedData shared_data; + +#endif // !SHARED_DATA_HPP \ No newline at end of file diff --git a/kaitei_WCSC28/square.cpp b/kaitei_WCSC28/square.cpp new file mode 100644 index 0000000..b2b55a8 --- /dev/null +++ b/kaitei_WCSC28/square.cpp @@ -0,0 +1,111 @@ +#include"square.hpp" +#include + +std::vector CanMove[WHITE_ROOK_PROMOTE + 1]; +Dir ConDirToOppositeDir[129]; + +void initCanMove() { + CanMove[BLACK_PAWN] = { U }; + CanMove[BLACK_LANCE] = {}; + CanMove[BLACK_KNIGHT] = { RUU, LUU }; + CanMove[BLACK_SILVER] = { U, RU, RD, LD, LU }; + CanMove[BLACK_GOLD] = { U, RU, R, D, L, LU }; + CanMove[BLACK_BISHOP] = {}; + CanMove[BLACK_ROOK] = {}; + CanMove[BLACK_KING] = { U, RU, R, RD, D, LD, L, LU }; + CanMove[BLACK_PAWN_PROMOTE] = { U, RU, R, D, L, LU }; + CanMove[BLACK_LANCE_PROMOTE] = { U, RU, R, D, L, LU }; + CanMove[BLACK_KNIGHT_PROMOTE] = { U, RU, R, D, L, LU }; + CanMove[BLACK_SILVER_PROMOTE] = { U, RU, R, D, L, LU }; + CanMove[BLACK_BISHOP_PROMOTE] = { U, R, D, L }; + CanMove[BLACK_ROOK_PROMOTE] = { RU, RD, LD, LU }; + CanMove[WHITE_PAWN] = { D }; + CanMove[WHITE_LANCE] = {}; + CanMove[WHITE_KNIGHT] = { RDD, LDD }; + CanMove[WHITE_SILVER] = { RU, RD, D, LD, LU }; + CanMove[WHITE_GOLD] = { U, R, RD, D, LD, L }; + CanMove[WHITE_BISHOP] = {}; + CanMove[WHITE_ROOK] = {}; + CanMove[WHITE_KING] = { U, RU, R, RD, D, LD, L, LU }; + CanMove[WHITE_PAWN_PROMOTE] = { U, R, RD, D, LD, L }; + CanMove[WHITE_LANCE_PROMOTE] = { U, R, RD, D, LD, L }; + CanMove[WHITE_KNIGHT_PROMOTE] = { U, R, RD, D, LD, L }; + CanMove[WHITE_SILVER_PROMOTE] = { U, R, RD, D, LD, L }; + CanMove[WHITE_BISHOP_PROMOTE] = { U, R, D, L }; + CanMove[WHITE_ROOK_PROMOTE] = { RU, RD, LD, LU }; +} + +std::vector CanJump[WHITE_ROOK_PROMOTE + 1]; +void initCanJump() { + CanJump[BLACK_PAWN] = {}; + CanJump[BLACK_LANCE] = { U }; + CanJump[BLACK_KNIGHT] = {}; + CanJump[BLACK_SILVER] = {}; + CanJump[BLACK_GOLD] = {}; + CanJump[BLACK_BISHOP] = { RU, RD, LD, LU }; + CanJump[BLACK_ROOK] = { U, R, D, L }; + CanJump[BLACK_PAWN_PROMOTE] = {}; + CanJump[BLACK_LANCE_PROMOTE] = {}; + CanJump[BLACK_KNIGHT_PROMOTE] = {}; + CanJump[BLACK_SILVER_PROMOTE] = {}; + CanJump[BLACK_BISHOP_PROMOTE] = { RU, RD, LD, LU }; + CanJump[BLACK_ROOK_PROMOTE] = { U, R, D, L }; + CanJump[WHITE_PAWN] = {}; + CanJump[WHITE_LANCE] = { D }; + CanJump[WHITE_KNIGHT] = {}; + CanJump[WHITE_SILVER] = {}; + CanJump[WHITE_GOLD] = {}; + CanJump[WHITE_BISHOP] = { RU, RD, LD, LU }; + CanJump[WHITE_ROOK] = { U, R, D, L }; + CanJump[WHITE_PAWN_PROMOTE] = {}; + CanJump[WHITE_LANCE_PROMOTE] = {}; + CanJump[WHITE_KNIGHT_PROMOTE] = {}; + CanJump[WHITE_SILVER_PROMOTE] = {}; + CanJump[WHITE_BISHOP_PROMOTE] = { RU, RD, LD, LU }; + CanJump[WHITE_ROOK_PROMOTE] = { U, R, D, L }; +} + +const std::array SquareList = { + SQ11, SQ12, SQ13, SQ14, SQ15, SQ16, SQ17, SQ18, SQ19, + SQ21, SQ22, SQ23, SQ24, SQ25, SQ26, SQ27, SQ28, SQ29, + SQ31, SQ32, SQ33, SQ34, SQ35, SQ36, SQ37, SQ38, SQ39, + SQ41, SQ42, SQ43, SQ44, SQ45, SQ46, SQ47, SQ48, SQ49, + SQ51, SQ52, SQ53, SQ54, SQ55, SQ56, SQ57, SQ58, SQ59, + SQ61, SQ62, SQ63, SQ64, SQ65, SQ66, SQ67, SQ68, SQ69, + SQ71, SQ72, SQ73, SQ74, SQ75, SQ76, SQ77, SQ78, SQ79, + SQ81, SQ82, SQ83, SQ84, SQ85, SQ86, SQ87, SQ88, SQ89, + SQ91, SQ92, SQ93, SQ94, SQ95, SQ96, SQ97, SQ98, SQ99 +}; + +const int SquareToNum[] = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, -1, + -1, 9, 10, 11, 12, 13, 14, 15, 16, 17, -1, + -1, 18, 19, 20, 21, 22, 23, 24, 25, 26, -1, + -1, 27, 28, 29, 30, 31, 32, 33, 34, 35, -1, + -1, 36, 37, 38, 39, 40, 41, 42, 43, 44, -1, + -1, 45, 46, 47, 48, 49, 50, 51, 52, 53, -1, + -1, 54, 55, 56, 57, 58, 59, 60, 61, 62, -1, + -1, 63, 64, 65, 66, 67, 68, 69, 70, 71, -1, + -1, 72, 73, 74, 75, 76, 77, 78, 79, 80, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, +}; + +const Square InvSquare[] = { + WALL00, WALL01, WALL02, WALL03, WALL04, WALL05, WALL06, WALL07, WALL08, WALL09, WALL0A, + WALL10, SQ99, SQ98, SQ97, SQ96, SQ95, SQ94, SQ93, SQ92, SQ91, WALL1A, + WALL20, SQ89, SQ88, SQ87, SQ86, SQ85, SQ84, SQ83, SQ82, SQ81, WALL2A, + WALL30, SQ79, SQ78, SQ77, SQ76, SQ75, SQ74, SQ73, SQ72, SQ71, WALL3A, + WALL40, SQ69, SQ68, SQ67, SQ66, SQ65, SQ64, SQ63, SQ62, SQ61, WALL4A, + WALL50, SQ59, SQ58, SQ57, SQ56, SQ55, SQ54, SQ53, SQ52, SQ51, WALL5A, + WALL60, SQ49, SQ48, SQ47, SQ46, SQ45, SQ44, SQ43, SQ42, SQ41, WALL6A, + WALL70, SQ39, SQ38, SQ37, SQ36, SQ35, SQ34, SQ33, SQ32, SQ31, WALL7A, + WALL80, SQ29, SQ28, SQ27, SQ26, SQ25, SQ24, SQ23, SQ22, SQ21, WALL8A, + WALL90, SQ19, SQ18, SQ17, SQ16, SQ15, SQ14, SQ13, SQ12, SQ11, WALL9A, + WALLA0, WALLA1, WALLA2, WALLA3, WALLA4, WALLA5, WALLA6, WALLA7, WALLA8, WALLA9, WALLAA, +}; + +std::ostream& operator<<(std::ostream& os, Square sq) { + os << SquareToFile[sq] << SquareToRank[sq]; + return os; +} \ No newline at end of file diff --git a/kaitei_WCSC28/square.hpp b/kaitei_WCSC28/square.hpp new file mode 100644 index 0000000..e5af662 --- /dev/null +++ b/kaitei_WCSC28/square.hpp @@ -0,0 +1,220 @@ +#ifndef SQUARE_HPP +#define SQUARE_HPP + +#include"piece.hpp" +#include +#include +#include +#include + +enum Square { + WALL00, WALL01, WALL02, WALL03, WALL04, WALL05, WALL06, WALL07, WALL08, WALL09, WALL0A, + WALL10, SQ11, SQ12, SQ13, SQ14, SQ15, SQ16, SQ17, SQ18, SQ19, WALL1A, + WALL20, SQ21, SQ22, SQ23, SQ24, SQ25, SQ26, SQ27, SQ28, SQ29, WALL2A, + WALL30, SQ31, SQ32, SQ33, SQ34, SQ35, SQ36, SQ37, SQ38, SQ39, WALL3A, + WALL40, SQ41, SQ42, SQ43, SQ44, SQ45, SQ46, SQ47, SQ48, SQ49, WALL4A, + WALL50, SQ51, SQ52, SQ53, SQ54, SQ55, SQ56, SQ57, SQ58, SQ59, WALL5A, + WALL60, SQ61, SQ62, SQ63, SQ64, SQ65, SQ66, SQ67, SQ68, SQ69, WALL6A, + WALL70, SQ71, SQ72, SQ73, SQ74, SQ75, SQ76, SQ77, SQ78, SQ79, WALL7A, + WALL80, SQ81, SQ82, SQ83, SQ84, SQ85, SQ86, SQ87, SQ88, SQ89, WALL8A, + WALL90, SQ91, SQ92, SQ93, SQ94, SQ95, SQ96, SQ97, SQ98, SQ99, WALL9A, + WALLA0, WALLA1, WALLA2, WALLA3, WALLA4, WALLA5, WALLA6, WALLA7, WALLA8, WALLA9, WALLAA, + SquareNum, +}; + +enum File { + File0, File1, File2, File3, File4, File5, File6, File7, File8, File9, FileA, FileNum, +}; + +enum Rank { + Rank0, Rank1, Rank2, Rank3, Rank4, Rank5, Rank6, Rank7, Rank8, Rank9, RankA, RankNum, +}; + +enum DiagR { + DiagR0, DiagR1, DiagR2, DiagR3, DiagR4, DiagR5, DiagR6, DiagR7, DiagR8, DiagR9, DiagRA, DiagRB, DiagRC, DiagRD, DiagRE, DiagRF, DiagRG, DiagRH, DiagRNum, +}; + +enum DiagL { + DiagL0, DiagL1, DiagL2, DiagL3, DiagL4, DiagL5, DiagL6, DiagL7, DiagL8, DiagL9, DiagLA, DiagLB, DiagLC, DiagLD, DiagLE, DiagLF, DiagLG, DiagLH, DiagLNum, +}; + +enum Dir { + H = 0, + U = -1, //上 + D = 1, //下 + R = -11, //右 + L = 11, //左 + RU = R + U, //右上 + RD = R + D, //右下 + LD = L + D, //左下 + LU = L + U, //左上 + RUU = RU + U, //右上上 + RDD = RD + D, //右下下 + LDD = LD + D, //左下下 + LUU = LU + U, //左上上 +}; + +enum ControlDir { + //利きの方向を表す定数 + //真ん中のマスに対してどこから利きが来ているのかをn番目のビットを立てて表す + /* + 812 + 7 3 + 654 + */ + // 87654321 + Con_U = 0b00000001, + Con_RU = 0b00000010, + Con_R = 0b00000100, + Con_RD = 0b00001000, + Con_D = 0b00010000, + Con_LD = 0b00100000, + Con_L = 0b01000000, + Con_LU = 0b10000000, +}; + +const Rank SquareToRank[SquareNum] = { + Rank0, Rank1, Rank2, Rank3, Rank4, Rank5, Rank6, Rank7, Rank8, Rank9, RankA, + Rank0, Rank1, Rank2, Rank3, Rank4, Rank5, Rank6, Rank7, Rank8, Rank9, RankA, + Rank0, Rank1, Rank2, Rank3, Rank4, Rank5, Rank6, Rank7, Rank8, Rank9, RankA, + Rank0, Rank1, Rank2, Rank3, Rank4, Rank5, Rank6, Rank7, Rank8, Rank9, RankA, + Rank0, Rank1, Rank2, Rank3, Rank4, Rank5, Rank6, Rank7, Rank8, Rank9, RankA, + Rank0, Rank1, Rank2, Rank3, Rank4, Rank5, Rank6, Rank7, Rank8, Rank9, RankA, + Rank0, Rank1, Rank2, Rank3, Rank4, Rank5, Rank6, Rank7, Rank8, Rank9, RankA, + Rank0, Rank1, Rank2, Rank3, Rank4, Rank5, Rank6, Rank7, Rank8, Rank9, RankA, + Rank0, Rank1, Rank2, Rank3, Rank4, Rank5, Rank6, Rank7, Rank8, Rank9, RankA, + Rank0, Rank1, Rank2, Rank3, Rank4, Rank5, Rank6, Rank7, Rank8, Rank9, RankA, + Rank0, Rank1, Rank2, Rank3, Rank4, Rank5, Rank6, Rank7, Rank8, Rank9, RankA, +}; + +const File SquareToFile[SquareNum] = { + File0, File0, File0, File0, File0, File0, File0, File0, File0, File0, File0, + File1, File1, File1, File1, File1, File1, File1, File1, File1, File1, File1, + File2, File2, File2, File2, File2, File2, File2, File2, File2, File2, File2, + File3, File3, File3, File3, File3, File3, File3, File3, File3, File3, File3, + File4, File4, File4, File4, File4, File4, File4, File4, File4, File4, File4, + File5, File5, File5, File5, File5, File5, File5, File5, File5, File5, File5, + File6, File6, File6, File6, File6, File6, File6, File6, File6, File6, File6, + File7, File7, File7, File7, File7, File7, File7, File7, File7, File7, File7, + File8, File8, File8, File8, File8, File8, File8, File8, File8, File8, File8, + File9, File9, File9, File9, File9, File9, File9, File9, File9, File9, File9, + FileA, FileA, FileA, FileA, FileA, FileA, FileA, FileA, FileA, FileA, FileA, +}; + +//斜め方向右上がり +const DiagR SquareToDiagR[SquareNum] = { + DiagR0, DiagR0, DiagR0, DiagR0, DiagR0, DiagR0, DiagR0, DiagR0, DiagR0, DiagR0, DiagR0, + DiagR0, DiagR1, DiagR2, DiagR3, DiagR4, DiagR5, DiagR6, DiagR7, DiagR8, DiagR9, DiagR0, + DiagR0, DiagR2, DiagR3, DiagR4, DiagR5, DiagR6, DiagR7, DiagR8, DiagR9, DiagRA, DiagR0, + DiagR0, DiagR3, DiagR4, DiagR5, DiagR6, DiagR7, DiagR8, DiagR9, DiagRA, DiagRB, DiagR0, + DiagR0, DiagR4, DiagR5, DiagR6, DiagR7, DiagR8, DiagR9, DiagRA, DiagRB, DiagRC, DiagR0, + DiagR0, DiagR5, DiagR6, DiagR7, DiagR8, DiagR9, DiagRA, DiagRB, DiagRC, DiagRD, DiagR0, + DiagR0, DiagR6, DiagR7, DiagR8, DiagR9, DiagRA, DiagRB, DiagRC, DiagRD, DiagRE, DiagR0, + DiagR0, DiagR7, DiagR8, DiagR9, DiagRA, DiagRB, DiagRC, DiagRD, DiagRE, DiagRF, DiagR0, + DiagR0, DiagR8, DiagR9, DiagRA, DiagRB, DiagRC, DiagRD, DiagRE, DiagRF, DiagRG, DiagR0, + DiagR0, DiagR9, DiagRA, DiagRB, DiagRC, DiagRD, DiagRE, DiagRF, DiagRG, DiagRH, DiagR0, + DiagR0, DiagR0, DiagR0, DiagR0, DiagR0, DiagR0, DiagR0, DiagR0, DiagR0, DiagR0, DiagR0, +}; + +//斜め方向左上がり +const DiagL SquareToDiagL[SquareNum] = { + DiagL0, DiagL0, DiagL0, DiagL0, DiagL0, DiagL0, DiagL0, DiagL0, DiagL0, DiagL0, DiagL0, + DiagL0, DiagL9, DiagL8, DiagL7, DiagL6, DiagL5, DiagL4, DiagL3, DiagL2, DiagL1, DiagL0, + DiagL0, DiagLA, DiagL9, DiagL8, DiagL7, DiagL6, DiagL5, DiagL4, DiagL3, DiagL2, DiagL0, + DiagL0, DiagLB, DiagLA, DiagL9, DiagL8, DiagL7, DiagL6, DiagL5, DiagL4, DiagL3, DiagL0, + DiagL0, DiagLC, DiagLB, DiagLA, DiagL9, DiagL8, DiagL7, DiagL6, DiagL5, DiagL4, DiagL0, + DiagL0, DiagLD, DiagLC, DiagLB, DiagLA, DiagL9, DiagL8, DiagL7, DiagL6, DiagL5, DiagL0, + DiagL0, DiagLE, DiagLD, DiagLC, DiagLB, DiagLA, DiagL9, DiagL8, DiagL7, DiagL6, DiagL0, + DiagL0, DiagLF, DiagLE, DiagLD, DiagLC, DiagLB, DiagLA, DiagL9, DiagL8, DiagL7, DiagL0, + DiagL0, DiagLG, DiagLF, DiagLE, DiagLD, DiagLC, DiagLB, DiagLA, DiagL9, DiagL8, DiagL0, + DiagL0, DiagLH, DiagLG, DiagLF, DiagLE, DiagLD, DiagLC, DiagLB, DiagLA, DiagL9, DiagL0, + DiagL0, DiagL0, DiagL0, DiagL0, DiagL0, DiagL0, DiagL0, DiagL0, DiagL0, DiagL0, DiagL0, +}; + +const Square FRToSquare[FileNum][RankNum] = { + {WALL00, WALL01, WALL02, WALL03, WALL04, WALL05, WALL06, WALL07, WALL08, WALL09, WALL0A}, + {WALL10, SQ11, SQ12, SQ13, SQ14, SQ15, SQ16, SQ17, SQ18, SQ19, WALL1A}, + {WALL20, SQ21, SQ22, SQ23, SQ24, SQ25, SQ26, SQ27, SQ28, SQ29, WALL2A}, + {WALL30, SQ31, SQ32, SQ33, SQ34, SQ35, SQ36, SQ37, SQ38, SQ39, WALL3A}, + {WALL40, SQ41, SQ42, SQ43, SQ44, SQ45, SQ46, SQ47, SQ48, SQ49, WALL4A}, + {WALL50, SQ51, SQ52, SQ53, SQ54, SQ55, SQ56, SQ57, SQ58, SQ59, WALL5A}, + {WALL60, SQ61, SQ62, SQ63, SQ64, SQ65, SQ66, SQ67, SQ68, SQ69, WALL6A}, + {WALL70, SQ71, SQ72, SQ73, SQ74, SQ75, SQ76, SQ77, SQ78, SQ79, WALL7A}, + {WALL80, SQ81, SQ82, SQ83, SQ84, SQ85, SQ86, SQ87, SQ88, SQ89, WALL8A}, + {WALL90, SQ91, SQ92, SQ93, SQ94, SQ95, SQ96, SQ97, SQ98, SQ99, WALL9A}, + {WALLA0, WALLA1, WALLA2, WALLA3, WALLA4, WALLA5, WALLA6, WALLA7, WALLA8, WALLA9, WALLAA}, +}; + +static inline bool isOnBoard(Square pos) { + return (Rank1 <= SquareToRank[pos] && SquareToRank[pos] <= Rank9 && File1 <= SquareToFile[pos] && SquareToFile[pos] <= File9); +} + +static Dir DirList[8] = { + //前から時計回りに + U, RU, R, RD, D, LD, L, LU +}; + +static ControlDir LongControlList[8] = { + //前から時計回りに + Con_U, Con_RU, Con_R, Con_RD, Con_D, Con_LD, Con_L, Con_LU, +}; + +inline static Dir oppositeDir(const Dir d) { + return static_cast(-d); +} + +extern Dir ConDirToOppositeDir[129]; +static void initConDirToOppositeDir() { + ConDirToOppositeDir[Con_U] = D; + ConDirToOppositeDir[Con_RU] = LD; + ConDirToOppositeDir[Con_R] = L; + ConDirToOppositeDir[Con_RD] = LU; + ConDirToOppositeDir[Con_D] = U; + ConDirToOppositeDir[Con_LD] = RU; + ConDirToOppositeDir[Con_L] = R; + ConDirToOppositeDir[Con_LU] = RD; +} + +inline Dir directionAtoB(Square A, Square B) { + //8方向のうちどれかか、あるいはどれでもないかだけ判定できればいい + //Aの位置を0とすると周囲8マスは + //10 -1 -12 + //11 0 -11 + //12 1 -10 + //だから差の正負と段、筋、斜めの一致具合で方向がわかるはず + if (A == B) return H; + else if (B - A > 0) { + if (SquareToRank[A] == SquareToRank[B]) return L; + if (SquareToFile[A] == SquareToFile[B]) return D; + if (SquareToDiagR[A] == SquareToDiagR[B]) return LU; + if (SquareToDiagL[A] == SquareToDiagL[B]) return LD; + } else { + if (SquareToRank[A] == SquareToRank[B]) return R; + if (SquareToFile[A] == SquareToFile[B]) return U; + if (SquareToDiagR[A] == SquareToDiagR[B]) return RD; + if (SquareToDiagL[A] == SquareToDiagL[B]) return RU; + } + return H; +} + +extern std::vector CanMove[WHITE_ROOK_PROMOTE + 1]; +void initCanMove(); + +extern std::vector CanJump[WHITE_ROOK_PROMOTE + 1]; +void initCanJump(); + +inline static Square operator+(Square sq, Dir diff) { + return static_cast(static_cast(sq) + static_cast(diff)); +} + +inline static int operator<<(Square sq, int shift) { + return static_cast(static_cast(sq) << shift); +} + +extern const std::array SquareList; +extern const int SquareToNum[]; +extern const Square InvSquare[]; + +std::ostream& operator<<(std::ostream&, Square sq); + +#endif \ No newline at end of file diff --git a/kaitei_WCSC28/test.cpp b/kaitei_WCSC28/test.cpp new file mode 100644 index 0000000..c0a475d --- /dev/null +++ b/kaitei_WCSC28/test.cpp @@ -0,0 +1,140 @@ +#include"test.hpp" +#include"usi.hpp" +#include"piece.hpp" +#include"square.hpp" +#include"position.hpp" +#include"hand.hpp" +#include"load_game.hpp" +#include + +void testInvPieceState() { + for (auto c : { BLACK, WHITE }) { + for (auto p : { PAWN, LANCE, KNIGHT, SILVER, GOLD, BISHOP, ROOK }) { + auto ps = pieceState(p, 3, c); + std::cout << ps << " の逆 " << invPieceState(ps) << std::endl; + } + } +} + +void testHand() { + Hand h; + h.set(PAWN, 10); + assert(h.num(PAWN) == 10); + h.set(LANCE, 3); + assert(h.num(LANCE) ==3); + h.set(KNIGHT, 2); + assert(h.num(KNIGHT) == 2); + h.set(SILVER, 1); + assert(h.num(SILVER) == 1); +} + +void testSFEN() { + std::string sfen1 = "1nsgsk2l/l8/1pppp1+R1n/p4pB1p/9/2P1P4/PP1P1PP1P/1B1S5/LN1GKGSNL w RG3P 1"; + std::cout << sfen1 << std::endl; + Position p; + p.load_sfen(sfen1); + p.printForDebug(); + std::string sfen2 = "lnsgkgsn1/1r5b1/ppppppppp/9/9/9/PPPPPPPPP/1B5R1/LNSGKGSNL w - 1"; + std::cout << sfen2 << std::endl; + p.load_sfen(sfen2); + p.printForDebug(); +} + +void testVectorSpeed() { + constexpr int N = 100000; + int normal_array[N]; + std::array st_array; + std::vector vec1(N), vec2; + + std::cout << "write" << std::endl; + auto start = std::chrono::steady_clock::now(); + for (int i = 0; i < N; ++i) { + normal_array[i] = i; + } + auto end = std::chrono::steady_clock::now(); + auto elapsed = end - start; + std::cout << "normal_array:" << elapsed.count() << std::endl; + + start = std::chrono::steady_clock::now(); + for (int i = 0; i < N; ++i) { + st_array[i] = i; + } + end = std::chrono::steady_clock::now(); + elapsed = end - start; + std::cout << "st_array[] :" << elapsed.count() << std::endl; + + start = std::chrono::steady_clock::now(); + for (int i = 0; i < N; ++i) { + st_array.at(i) = i; + } + end = std::chrono::steady_clock::now(); + elapsed = end - start; + std::cout << "st_arrayat :" << elapsed.count() << std::endl; + + start = std::chrono::steady_clock::now(); + for (int i = 0; i < N; ++i) { + vec1[i] = i; + } + end = std::chrono::steady_clock::now(); + elapsed = end - start; + std::cout << "vector :" << elapsed.count() << std::endl; + + start = std::chrono::steady_clock::now(); + for (int i = 0; i < N; ++i) { + vec2.push_back(i); + } + end = std::chrono::steady_clock::now(); + elapsed = end - start; + std::cout << "vector.push :" << elapsed.count() << std::endl; + + std::cout << "read" << std::endl; //------------------------------------------------------------------------------------- + long long sum = 0; + + int read_array[N]; + for (int i = 0; i < N; ++i) { + read_array[i] = i; + } + std::random_device seed_gen; + std::mt19937 engine(seed_gen()); + std::shuffle(read_array, read_array + N, engine); + + start = std::chrono::steady_clock::now(); + for (int i = 0; i < N; ++i) { + sum += normal_array[read_array[i]]; + } + end = std::chrono::steady_clock::now(); + elapsed = std::chrono::duration_cast(end - start); + std::cout << "normal_array:" << elapsed.count() << std::endl; + + sum = 0; + start = std::chrono::steady_clock::now(); + for (int i = 0; i < N; ++i) { + sum += st_array[read_array[i]]; + } + end = std::chrono::steady_clock::now(); + elapsed = std::chrono::duration_cast(end - start); + std::cout << "st_array[] :" << elapsed.count() << std::endl; + + sum = 0; + start = std::chrono::steady_clock::now(); + for (int i = 0; i < N; ++i) { + sum += st_array.at(read_array[i]); + } + end = std::chrono::steady_clock::now(); + elapsed = std::chrono::duration_cast(end - start); + std::cout << "st_arrayat :" << elapsed.count() << std::endl; + + sum = 0; + start = std::chrono::steady_clock::now(); + for (int i = 0; i < N; ++i) { + sum += vec1[read_array[i]]; + } + end = std::chrono::steady_clock::now(); + elapsed = std::chrono::duration_cast(end - start); + std::cout << "vector :" << elapsed.count() << std::endl; + + int size = 10; + int* a = new int[size]; + auto p = new std::array; + (*p)[5] = 5; +} \ No newline at end of file diff --git a/kaitei_WCSC28/test.hpp b/kaitei_WCSC28/test.hpp new file mode 100644 index 0000000..94d1a2d --- /dev/null +++ b/kaitei_WCSC28/test.hpp @@ -0,0 +1,13 @@ +#pragma once + +#ifndef TEST_HPP +#define TEST_HPP + +#include"eval_params.hpp" + +void testInvPieceState(); +void testHand(); +void testSFEN(); +void testVectorSpeed(); + +#endif // !TEST_HPP \ No newline at end of file diff --git a/kaitei_WCSC28/thread.cpp b/kaitei_WCSC28/thread.cpp new file mode 100644 index 0000000..c815d1c --- /dev/null +++ b/kaitei_WCSC28/thread.cpp @@ -0,0 +1,60 @@ +#include"thread.hpp" + +#include"position.hpp" +#include"types.hpp" +#include"move_picker.hpp" +#include"shared_data.hpp" +#include"usi_options.hpp" + +ThreadPool threads; +SharedData shared_data; + +void Thread::idleLoop() { + while (!exit_) { + std::unique_lock lock(mutex_); + + //exit_searching_trueɂȂ܂ŃX[v + sleep_condition_.wait(lock, [this]() { + return exit_ || searching_; + }); + + lock.unlock(); + if (exit_) + break; + + //ŒTJn + searcher_.think(); + + //T𔲂̂ŃtOfalse + mutex_.lock(); + searching_ = false; + sleep_condition_.notify_one(); + mutex_.unlock(); + } +} + +ThreadPool::ThreadPool() { + //CXbh + this->emplace_back(new MainThread(0)); +} + +void ThreadPool::init() { + //Main̕ƁAgȂMate̕ + int slave_num = (usi_option.do_use_mate_search ? usi_option.thread_num - 2 : usi_option.thread_num - 1); + + //CXbh + this->emplace_back(new MainThread(0)); + + //IvV̎wɉċlݒTXbh + if (usi_option.do_use_mate_search) { + this->emplace_back(new Thread(1)); + this->at(1)->setSeacherRole(Searcher::Role::MATE); + } + + //X[uXbh + int slave_thread_start = (usi_option.do_use_mate_search ? 2 : 1); + for (int i = 0; i < slave_num; ++i) { + this->emplace_back(new Thread(slave_thread_start + i)); + this->at(slave_thread_start + i)->setSeacherRole(Searcher::Role::SLAVE); + } +} \ No newline at end of file diff --git a/kaitei_WCSC28/thread.hpp b/kaitei_WCSC28/thread.hpp new file mode 100644 index 0000000..d213989 --- /dev/null +++ b/kaitei_WCSC28/thread.hpp @@ -0,0 +1,86 @@ +#pragma once + +#ifndef THREAD_HPP +#define THREAD_HPP + +#include +#include +#include +#include +#include +#include +#include"types.hpp" +#include"move.hpp" +#include"searcher.hpp" + +class Position; + +class Thread { +protected: + std::thread thread_; + unsigned int id_; + bool exit_, searching_; + std::mutex mutex_; + std::condition_variable sleep_condition_; + Searcher searcher_; +public: + Thread(unsigned int id) : id_(id), exit_(false), searching_(false), searcher_(Searcher::Role::SLAVE) { + thread_ = std::thread(&Thread::idleLoop, this); + } + ~Thread() { + mutex_.lock(); + exit_ = true; + sleep_condition_.notify_one(); + mutex_.unlock(); + thread_.join(); + } + + void idleLoop(); + + virtual void startSearch() { + std::unique_lock lock(mutex_); + searching_ = true; + sleep_condition_.notify_one(); + } + + void waitForFinishSearch() { + std::unique_lock lock(mutex_); + sleep_condition_.wait(lock, [this]() { + return !searching_; + }); + } + + unsigned int id() { + return id_; + } + + void setSeacherRole(Searcher::Role r) { + searcher_.setRole(r); + } +}; + +struct ThreadPool : public std::vector> { + ThreadPool(); + void init(); +}; +extern ThreadPool threads; + +class MainThread : public Thread { +private: + +public: + MainThread(unsigned int id) : Thread(id) { + searcher_.setRole(Searcher::Role::MAIN); + } + + virtual void startSearch() { + std::unique_lock lock(mutex_); + searching_ = true; + sleep_condition_.notify_one(); + for (int i = 1; i < threads.size(); ++i) { + threads[i]->startSearch(); + } + } +}; + +#endif // !THREAD_HPP \ No newline at end of file diff --git a/kaitei_WCSC28/types.cpp b/kaitei_WCSC28/types.cpp new file mode 100644 index 0000000..3649ae0 --- /dev/null +++ b/kaitei_WCSC28/types.cpp @@ -0,0 +1,19 @@ +#include"types.hpp" + +//Score +std::ostream& operator<<(std::ostream& os, const Score s) { + os << static_cast(s); + return os; +} + +//Depth +std::ostream& operator<<(std::ostream& os, const Depth d) { + os << static_cast(d); + return os; +} +std::istream& operator>>(std::istream& is, Depth& d) { + int tmp; + is >> tmp; + d = Depth(tmp); + return is; +} \ No newline at end of file diff --git a/kaitei_WCSC28/types.hpp b/kaitei_WCSC28/types.hpp new file mode 100644 index 0000000..07e7770 --- /dev/null +++ b/kaitei_WCSC28/types.hpp @@ -0,0 +1,85 @@ +#pragma once + +#ifndef TYPES_HPP +#define TYPES_HPP + +#include + +enum Color { + BLACK, WHITE, ColorNum, +}; + +enum Bound { + EXACT_BOUND, UPPER_BOUND, LOWER_BOUND +}; + +enum Depth { + PLY = 128, + DEPTH_MAX = 64 * PLY, + MATE_DEPTH_MAX = 128, +}; + +enum Score { + MAX_SCORE = 1000000, + SCORE_ZERO = 0, + DRAW_SCORE = 0, + MIN_SCORE = -MAX_SCORE, + MATE_SCORE_LOWER_BOUND = MAX_SCORE - static_cast(MATE_DEPTH_MAX), + MATE_SCORE_UPPER_BOUND = MIN_SCORE + static_cast(MATE_DEPTH_MAX), +}; + +inline bool isMatedScore(const Score score) { + return score <= MATE_SCORE_UPPER_BOUND || MATE_SCORE_LOWER_BOUND <= score; +} + +//ZqI[o[[hĂȂƕs +//Score +constexpr Score operator-(Score lhs) { return Score(-int(lhs)); } +constexpr Score operator+(Score lhs, Score rhs) { return Score(int(lhs) + int(rhs)); } +constexpr Score operator-(Score lhs, Score rhs) { return Score(int(lhs) - int(rhs)); } +constexpr Score operator+(Score lhs, int rhs) { return Score(int(lhs) + rhs); } +constexpr Score operator-(Score lhs, int rhs) { return Score(int(lhs) - rhs); } +constexpr Score operator+(int lhs, Score rhs) { return Score(lhs + int(rhs)); } +constexpr Score operator-(int lhs, Score rhs) { return Score(lhs - int(rhs)); } +inline Score& operator+=(Score& lhs, Score rhs) { return lhs = lhs + rhs; } +inline Score& operator-=(Score& lhs, Score rhs) { lhs = lhs - rhs; return lhs; } +inline Score& operator+=(Score& lhs, int rhs) { lhs = lhs + rhs; return lhs; } +inline Score& operator-=(Score& lhs, int rhs) { lhs = lhs - rhs; return lhs; } +inline Score& operator++(Score& lhs) { lhs = lhs + 1; return lhs; } +inline Score& operator--(Score& lhs) { lhs = lhs - 1; return lhs; } +inline Score operator++(Score& lhs, int) { Score t = lhs; lhs += 1; return t; } +inline Score operator--(Score& lhs, int) { Score t = lhs; lhs -= 1; return t; } + +constexpr Score operator*(Score lhs, int rhs) { return Score(int(lhs) * rhs); } +constexpr Score operator*(int lhs, Score rhs) { return Score(lhs * int(rhs)); } +constexpr Score operator/(Score lhs, int rhs) { return Score(int(lhs) / rhs); } +inline Score& operator*=(Score& lhs, int rhs) { lhs = lhs * rhs; return lhs; } +inline Score& operator/=(Score& lhs, int rhs) { lhs = lhs / rhs; return lhs; } +std::ostream& operator<<(std::ostream& os, const Score s); + +//Depth +constexpr Depth operator-(Depth lhs) { return Depth(-int(lhs)); } +constexpr Depth operator+(Depth lhs, Depth rhs) { return Depth(int(lhs) + int(rhs)); } +constexpr Depth operator-(Depth lhs, Depth rhs) { return Depth(int(lhs) - int(rhs)); } +constexpr Depth operator+(Depth lhs, int rhs) { return Depth(int(lhs) + rhs); } +constexpr Depth operator-(Depth lhs, int rhs) { return Depth(int(lhs) - rhs); } +constexpr Depth operator+(int lhs, Depth rhs) { return Depth(lhs + int(rhs)); } +constexpr Depth operator-(int lhs, Depth rhs) { return Depth(lhs - int(rhs)); } +inline Depth& operator+=(Depth& lhs, Depth rhs) { return lhs = lhs + rhs; } +inline Depth& operator-=(Depth& lhs, Depth rhs) { lhs = lhs - rhs; return lhs; } +inline Depth& operator+=(Depth& lhs, int rhs) { lhs = lhs + rhs; return lhs; } +inline Depth& operator-=(Depth& lhs, int rhs) { lhs = lhs - rhs; return lhs; } +inline Depth& operator++(Depth& lhs) { lhs = lhs + 1; return lhs; } +inline Depth& operator--(Depth& lhs) { lhs = lhs - 1; return lhs; } +inline Depth operator++(Depth& lhs, int) { Depth t = lhs; lhs += 1; return t; } +inline Depth operator--(Depth& lhs, int) { Depth t = lhs; lhs -= 1; return t; } + +constexpr Depth operator*(Depth lhs, int rhs) { return Depth(int(lhs) * rhs); } +constexpr Depth operator*(int lhs, Depth rhs) { return Depth(lhs * int(rhs)); } +constexpr Depth operator/(Depth lhs, int rhs) { return Depth(int(lhs) / rhs); } +inline Depth& operator*=(Depth& lhs, int rhs) { lhs = lhs * rhs; return lhs; } +inline Depth& operator/=(Depth& lhs, int rhs) { lhs = lhs / rhs; return lhs; } +std::ostream& operator<<(std::ostream& os, const Depth d); +std::istream& operator>>(std::istream& is, Depth& d); + +#endif // !TYPES_HPP \ No newline at end of file diff --git a/kaitei_WCSC28/usi.cpp b/kaitei_WCSC28/usi.cpp new file mode 100644 index 0000000..feaa65f --- /dev/null +++ b/kaitei_WCSC28/usi.cpp @@ -0,0 +1,225 @@ +#include"usi.hpp" +#include"move.hpp" +#include"position.hpp" +#include"searcher.hpp" +#include"usi_options.hpp" +#include"learn.hpp" +#include"shared_data.hpp" +#include"eval_params.hpp" +#include"load_game.hpp" +#include"learn_self.hpp" +#include +#include +#include + +USIOption usi_option; + +void USI::loop() { + std::string input; + while (true) { + std::cin >> input; + if (input == "usi") { + usi(); + } else if (input == "isready") { + isready(); + } else if (input == "setoption") { + setoption(); + } else if (input == "usinewgame") { + usinewgame(); + } else if (input == "position") { + position(); + } else if (input == "go") { + go(); + } else if (input == "stop") { + stop(); + } else if (input == "ponderhit") { + ponderhit(); + } else if (input == "quit") { + quit(); + } else if (input == "gameover") { + gameover(); + } else if (input == "prepareForLearn") { + eval_params->clear(); + eval_params->writeFile(); + } else if (input == "learnSelf") { + learnSelf(); + } else if (input == "BonanzaMethod") { + BonanzaMethod(); + } else { + std::cout << "Illegal input" << std::endl; + } + } +} + +void USI::usi() { + printf("id name kaitei_WCSC28\n"); + printf("id author Sakoda Shintaro\n"); + printf("option name byoyomi_margin type spin default 0 min 0 max 1000\n"); + printf("option name random_turn type spin default 0 min 0 max 100\n"); + printf("usiok\n"); +} + +void USI::isready() { + shared_data.hash_table.setSize(usi_option.USI_Hash); + eval_params->readFile(); + printf("readyok\n"); +} + +void USI::setoption() { + std::string input; + while (true) { + std::cin >> input; + if (input == "name") { + std::cin >> input; + //ここで処理 + if (input == "byoyomi_margin") { + std::cin >> input; //input == "value"となるなず + std::cin >> usi_option.byoyomi_margin; + return; + } else if (input == "random_turn") { + std::cin >> input; //input == "value"となるなず + std::cin >> usi_option.random_turn; + return; + } else if (input == "USI_Hash") { + std::cin >> input; //input == "value"となるはず + std::cin >> usi_option.USI_Hash; + return; + } else if (input == "USI_Ponder") { + std::cin >> input; //input == "value"となるなず + std::cin >> input; //特になにもしていない + return; + } + } + } +} + +void USI::usinewgame() { + //なにかやるべきことあるんだろうか + //置換表のリセットか + shared_data.hash_table.clear(); + shared_data.hash_table.setSize(usi_option.USI_Hash); + + //PositionのコンストラクタがBitboard類の初期化より前に呼ばれてしまうので + //ここで呼びなおさないとおかしくなる + shared_data.root = Position(); +} + +void USI::position() { + //rootを初期化 + shared_data.root = Position(); + + std::string input, sfen; + std::cin >> input; + if (input == "startpos") { + sfen = "lnsgkgsnl/1r5b1/ppppppppp/9/9/9/PPPPPPPPP/1B5R1/LNSGKGSNL b - 1"; + } else { + for (int i = 0; i < 4; i++) { + std::cin >> input; + sfen += input; + sfen += " "; + } + } + shared_data.root.load_sfen(sfen); + + std::cin >> input; //input == "moves" or "go"となる + if (input != "go") { + while (std::cin >> input) { + if (input == "go") { + break; + } + //inputをMoveに直して局面を動かす + Move move = stringToMove(input); + move = shared_data.root.transformValidMove(move); + shared_data.root.doMove(move); + } + } + + go(); +} + +void USI::go() { + shared_data.stop_signal = false; + std::string input; + long long btime, wtime, byoyomi = 0, binc = 0, winc = 0; + std::cin >> input; + if (input == "ponder") { + //ponderの処理 + } else if (input == "btime") { + std::cin >> input; + btime = atoi(input.c_str()); + std::cin >> input; //input == "wtime" となるはず + std::cin >> input; + wtime = atoi(input.c_str()); + std::cin >> input; //input == "byoyomi" or "binc"となるはず + if (input == "byoyomi") { + std::cin >> input; + byoyomi = atoi(input.c_str()); + } else { + std::cin >> input; + binc = atoi(input.c_str()); + std::cin >> input; //input == "winc" となるはず + std::cin >> input; + winc = atoi(input.c_str()); + } + //ここまで持ち時間の設定 + //ここから思考部分 + if (usi_option.fixed_depth > 0) { + //thread_manager_.startThink(position_, usi_option.fixed_depth); + return; + } + if (byoyomi != 0) { + shared_data.limit_msec = byoyomi; + } else { + shared_data.limit_msec = binc; + } + threads[0]->startSearch(); + return; + } else if (input == "infinite") { + //stop来るまで思考し続ける + //思考時間をほぼ無限に + shared_data.limit_msec = LLONG_MAX; + + //randomturnをなくす + usi_option.random_turn = 0; + + threads[0]->startSearch(); + } else if (input == "mate") { + //詰み探索 + std::cin >> input; + if (input == "infinite") { + //stop来るまで + } else { + //思考時間が指定された場合 + //どう実装すればいいんだろう + } + } +} + +void USI::stop() { + shared_data.stop_signal = true; + for (auto& t : threads) { + t->waitForFinishSearch(); + } +} + +void USI::ponderhit() { + //まだ未実装 +} + +void USI::quit() { + stop(); + exit(0); +} + +void USI::gameover() { + std::string input; + std::cin >> input; + if (input == "win") { + //勝ち + } else if (input == "lose") { + //負け + } else if (input == "draw") { + //引き分け + return; + } +} \ No newline at end of file diff --git a/kaitei_WCSC28/usi.hpp b/kaitei_WCSC28/usi.hpp new file mode 100644 index 0000000..3357f6d --- /dev/null +++ b/kaitei_WCSC28/usi.hpp @@ -0,0 +1,26 @@ +#ifndef USI_HPP +#define USI_HPP + +#include"common.hpp" +#include"position.hpp" +#include"searcher.hpp" +#include"move.hpp" +#include"thread.hpp" +#include + +class USI { +public: + void loop(); + void usi(); + void isready(); + void setoption(); + void usinewgame(); + void position(); + void go(); + void stop(); + void ponderhit(); + void quit(); + void gameover(); +}; + +#endif diff --git a/kaitei_WCSC28/usi_options.hpp b/kaitei_WCSC28/usi_options.hpp new file mode 100644 index 0000000..e577c9e --- /dev/null +++ b/kaitei_WCSC28/usi_options.hpp @@ -0,0 +1,22 @@ +#pragma once +#ifndef USI_OPTIONS_HPP +#define USI_OPTIONS_HPP + +#include"types.hpp" +#include +#include +#include + +class USIOption{ +public: + long long byoyomi_margin; + unsigned int random_turn; + Depth fixed_depth; + long long USI_Hash; + unsigned int thread_num; + bool do_use_mate_search; +}; + +extern USIOption usi_option; + +#endif