/// <summary> /// [UI Thread] : 読み筋などのメッセージを受け取り、 /// UsiEngineReportMessageTypeの内容に応じた処理に振り分ける。 /// </summary> /// <param name="message"></param> public void DispatchThinkReportMessage(UsiThinkReportMessage message) { switch (message.type) { case UsiEngineReportMessageType.NumberOfInstance: // 非表示なので検討ウィンドウが表示されているなら消しておく。 Visible = message.number != 0; SetEngineInstanceNumber(message.number); break; case UsiEngineReportMessageType.SetGameMode: var gameMode = (GameModeEnum)message.data; var b = (gameMode == GameModeEnum.ConsiderationWithEngine); // MultiPV用の表示に ConsiderationInstance(0).Notify.EnableMultiPVComboBox = b; ConsiderationInstance(0).SortRanking = b; break; case UsiEngineReportMessageType.SetEngineName: var engine_name = message.data as string; ConsiderationInstance(message.number).EngineName = engine_name; break; case UsiEngineReportMessageType.SetRootSfen: var sfen = message.data as string; ConsiderationInstance(message.number).RootSfen = sfen; break; case UsiEngineReportMessageType.UsiThinkReport: var thinkReport = message.data as UsiThinkReport; ConsiderationInstance(message.number).AddThinkReport(thinkReport); break; } }
/// <summary> /// [UI thread] : 読み筋などのメッセージを受け取り、ququeに積む。 /// UsiEngineReportMessageTypeの内容に応じた処理に振り分ける。 /// </summary> /// <param name="message"></param> private void DispatchThinkReportMessage(UsiThinkReportMessage message) { switch (message.type) { case UsiEngineReportMessageType.NumberOfInstance: if (!message.skipDisplay) { // 非表示なので検討ウィンドウが表示されているなら消しておく。 Visible = message.number != 0; SetEngineInstanceNumber(message.number); #if false // このメッセージに対して、継ぎ盤の局面を初期化したほうが良いのでは…。 miniShogiBoard1.BoardData = new MiniShogiBoardData() { rootSfen = BoardType.NoHandicap.ToSfen() }; // → このメッセージの直後にrootSfenが来るはずだから、それに応じて、その局面で初期化するようにする #endif must_init_miniboard = true; } break; case UsiEngineReportMessageType.SetGameMode: if (!message.skipDisplay) { var gameMode = (GameModeEnum)message.data; var b = (gameMode == GameModeEnum.ConsiderationWithEngine); // MultiPV用の表示に ConsiderationInstance(0).ViewModel.EnableMultiPVComboBox = b; ConsiderationInstance(0).SortRanking = b; } break; case UsiEngineReportMessageType.SetEngineName: if (!message.skipDisplay) { var engine_name = message.data as string; ConsiderationInstance(message.number).EngineName = engine_name; } break; case UsiEngineReportMessageType.SetRootSfen: var sfen = message.data as string; if (!message.skipDisplay) { ConsiderationInstance(message.number).RootSfen = sfen; } // まだミニ盤面の初期化がなされていないならば。 if (must_init_miniboard) { miniShogiBoard1.BoardData = new MiniShogiBoardData() { rootSfen = sfen }; must_init_miniboard = false; } break; case UsiEngineReportMessageType.UsiThinkEnd: if (!message.skipDisplay) { ConsiderationInstance(message.number).DisplayThinkEnd(); } break; case UsiEngineReportMessageType.UsiThinkReport: if (!message.skipDisplay) { var thinkReport = message.data as UsiThinkReport; ConsiderationInstance(message.number).AddThinkReport(thinkReport); } // TODO : ここで評価値グラフのためのデータ構造体の更新を行うべき。 break; } }
/// <summary> /// 読み筋などのメッセージを受け取り、ququeに積む。 /// /// OnIdle()が呼び出されたときにdispatchする。 /// </summary> public void EnqueueThinkReportMessage(UsiThinkReportMessage message) { thinkQuque.Add(message); }
/// <summary> /// 手番側のプレイヤーに自分の手番であることを通知するためにThink()を呼び出す。 /// また、手番側のCanMove = trueにする。非手番側のプレイヤーに対してCanMove = falseにする。 /// </summary> private void NotifyTurnChanged() { var stm = Position.sideToMove; // 検討モードでは、先手側のプレイヤーがエンジンに紐づけられている。 if (GameMode.IsConsiderationWithEngine()) { stm = Color.BLACK; } var stmPlayer = Player(stm); var isHuman = stmPlayer.PlayerType == PlayerTypeEnum.Human; // 手番が変わった時に特殊な局面に至っていないかのチェック if (GameMode == GameModeEnum.InTheGame) { var misc = TheApp.app.Config.GameSetting.MiscSettings; Move m = kifuManager.Tree.IsNextNodeSpecialNode(isHuman, misc); // 上で判定された特殊な指し手であるか? if (m != Move.NONE) { // この特殊な状況を棋譜に書き出して終了。 kifuManager.Tree.AddNode(m, KifuMoveTimes.Zero); // speical moveでもDoMoveできることは保証されている。 kifuManager.Tree.DoMove(m); GameEnd(m); return; } } // USIエンジンのときだけ、"position"コマンドに渡す形で局面図が必要であるから、 // 生成して、それをPlayer.Think()の引数として渡してやる。 var isUsiEngine = stmPlayer.PlayerType == PlayerTypeEnum.UsiEngine; string usiPosition = isUsiEngine ? kifuManager.UsiPositionString : null; stmPlayer.CanMove = true; stmPlayer.SpecialMove = Move.NONE; LastCheckedByoyomiReadOut = 0; // BestMove,PonderMoveは、Think()以降、正常に更新されることは、Playerクラス側で保証されているので、 // ここではそれらの初期化は行わない。 // -- MultiPVの設定 if (GameMode == GameModeEnum.ConsiderationWithEngine) { // MultiPVは、GlobalConfigの設定を引き継ぐ (stmPlayer as UsiEnginePlayer).Engine.MultiPV = TheApp.app.Config.ConsiderationMultiPV; } // それ以外のGameModeなら、USIのoption設定を引き継ぐので変更しない。 // -- Think() // 通常対局モードのはずなので現在の持ち時間設定を渡してやる。 // エンジン検討モードなら検討エンジン設定に従う UsiThinkLimit limit = UsiThinkLimit.TimeLimitLess; switch (GameMode) { case GameModeEnum.InTheGame: limit = UsiThinkLimit.FromTimeSetting(PlayTimers, stm); break; case GameModeEnum.ConsiderationWithEngine: { var setting = TheApp.app.Config.ConsiderationEngineSetting; if (setting.Limitless) { limit = UsiThinkLimit.TimeLimitLess; } else // if (setting.TimeLimit) { limit = UsiThinkLimit.FromSecond(setting.Second); } } break; case GameModeEnum.ConsiderationWithMateEngine: { var setting = TheApp.app.Config.MateEngineSetting; if (setting.Limitless) { limit = UsiThinkLimit.TimeLimitLess; } else // if (setting.TimeLimit) { limit = UsiThinkLimit.FromSecond(setting.Second); } } break; } stmPlayer.Think(usiPosition, limit, stm); // -- 検討ウィンドウに対して、ここをrootSfenとして設定 if (ThinkReportEnable && isUsiEngine) { ThinkReport = new UsiThinkReportMessage() { type = UsiEngineReportMessageType.SetRootSfen, number = NumberOfEngine == 1 ? 0 : (int)stm, // CPU1つなら1番目の窓、CPU2つならColorに相当する窓に data = Position.ToSfen(), // ここ、せめて前の局面からのsfenを渡さないと、 // PVの1手目に同金みたいな表現が出来なくなってしまう。 }; } // 手番側のプレイヤーの時間消費を開始 if (GameMode == GameModeEnum.InTheGame) { // InTheGame == trueならば、PlayerTimeSettingは適切に設定されているはず。 // (対局開始時に初期化するので) PlayTimer(stm).ChangeToOurTurn(); } // 非手番側のCanMoveをfalseに var nextPlayer = Player(stm.Not()); nextPlayer.CanMove = false; // -- 手番が変わった時の各種propertyの更新 EngineTurn = stmPlayer.PlayerType == PlayerTypeEnum.UsiEngine; // 対局中でなければ自由に動かせる。対局中は人間のプレイヤーでなければ駒を動かせない。 CanUserMove = stmPlayer.PlayerType == PlayerTypeEnum.Human || GameMode.CanUserMove(); // 値が変わっていなくとも変更通知を送りたいので自力でハンドラを呼び出す。 RaisePropertyChanged("TurnChanged", CanUserMove); // 仮想プロパティ"TurnChanged" }
/// <summary> /// プレイヤー情報を検討ダイアログにリダイレクトする設定をする。 /// </summary> private void InitEngineConsiderationInfo(GameModeEnum nextGameMode) { // CPUの数をNumberOfEngineに反映。 int num = 0; if (nextGameMode.IsConsiderationWithEngine()) { num = 1; // エンジンによる検討モードなら出力は一つ。 } else { foreach (var c in All.Colors()) { if (GameSetting.PlayerSetting(c).IsCpu) { ++num; } } } NumberOfEngine = num; // エンジン数が確定したので、検討ウィンドウにNumberOfInstanceメッセージを送信してやる。 ThinkReport = new UsiThinkReportMessage() { type = UsiEngineReportMessageType.NumberOfInstance, number = NumberOfEngine, }; ThinkReport = new UsiThinkReportMessage() { type = UsiEngineReportMessageType.SetGameMode, data = nextGameMode }; // 各エンジンの情報を検討ウィンドウにリダイレクトするようにハンドラを設定 num = 0; foreach (var c in All.Colors()) { if ((nextGameMode == GameModeEnum.InTheGame && GameSetting.PlayerSetting(c).IsCpu) || (nextGameMode.IsConsiderationWithEngine() && c == Color.BLACK) // // 検討用エンジンがぶら下がっていると考えられる。 ) { var num_ = num; // copy for capturing var engineName = GetEngineDefine(c).EngineDefine.DisplayName; var engineName2 = continuousGame.PresetName(c) == null ? engineName : $"{engineName} { continuousGame.PresetName(c)}"; var playerName = (nextGameMode.IsConsiderationWithEngine() || DisplayName(c) == engineName) ? // 検討時には、エンジンの名前をそのまま表示。 engineName2 : // 通常対局モードなら対局者名に括弧でエンジン名を表記。 $"{DisplayName(c)}({engineName2})"; ThinkReport = new UsiThinkReportMessage() { type = UsiEngineReportMessageType.SetEngineName, number = num_, // is captured data = playerName, }; // UsiEngineのThinkReportプロパティを捕捉して、それを転送してやるためのハンドラをセットしておく。 var engine_player = Player(c) as UsiEnginePlayer; engine_player.Engine.AddPropertyChangedHandler("ThinkReport", (args) => { //// 1) 読み筋の抑制条件その1 //// 人間対CPUで、メニューの「ウィンドウ」のところで表示するになっていない場合。 //var surpress1 = NumberOfEngine == 1 && !TheApp.app.config.EngineConsiderationWindowEnableWhenVsHuman; if (ThinkReportEnable /* && !(surpress1) */) { var report = args.value as UsiThinkReport; // このクラスのpropertyのsetterを呼び出してメッセージを移譲してやる。 ThinkReport = new UsiThinkReportMessage() { type = UsiEngineReportMessageType.UsiThinkReport, number = num_, // is captrued data = report, }; } }); num++; } } }
/// <summary> /// プレイヤー情報を検討ダイアログに反映させる。 /// </summary> private void InitEngineConsiderationInfo(GameModeEnum nextGameMode) { // CPUの数をNumberOfEngineに反映。 int num = 0; foreach (var c in All.Colors()) { if (GameSetting.Player(c).IsCpu) { ++num; } } NumberOfEngine = num; // エンジン数が確定したので、検討ウィンドウにNumberOfInstanceメッセージを送信してやる。 ThinkReport = new UsiThinkReportMessage() { type = UsiEngineReportMessageType.NumberOfInstance, number = NumberOfEngine, }; ThinkReport = new UsiThinkReportMessage() { type = UsiEngineReportMessageType.SetGameMode, data = nextGameMode }; // 各エンジンの情報を検討ウィンドウにリダイレクトするようにハンドラを設定 num = 0; foreach (var c in All.Colors()) { if (GameSetting.Player(c).IsCpu) { var num_ = num; // copy for capturing // 検討モードなら、名前は.. var name = (nextGameMode == GameModeEnum.ConsiderationWithEngine) ? "検討用エンジン" : (nextGameMode == GameModeEnum.ConsiderationWithMateEngine) ? "詰将棋エンジン" : DisplayName(c); ThinkReport = new UsiThinkReportMessage() { type = UsiEngineReportMessageType.SetEngineName, number = num_, // is captured data = name, }; // UsiEngineのThinkReportプロパティを捕捉して、それを転送してやるためのハンドラをセットしておく。 var engine_player = Player(c) as UsiEnginePlayer; engine_player.engine.AddPropertyChangedHandler("ThinkReport", (args) => { //// 1) 読み筋の抑制条件その1 //// 人間対CPUで、メニューの「ウィンドウ」のところで表示するになっていない場合。 //var surpress1 = NumberOfEngine == 1 && !TheApp.app.config.EngineConsiderationWindowEnableWhenVsHuman; if (ThinkReportEnable /* && !(surpress1) */) { var report = args.value as UsiThinkReport; // このクラスのpropertyのsetterを呼び出してメッセージを移譲してやる。 ThinkReport = new UsiThinkReportMessage() { type = UsiEngineReportMessageType.UsiThinkReport, number = num_, // is captrued data = report, }; } }); num++; } } }