/// <summary> /// UI側から、worker threadで実行して欲しいコマンドを渡す。 /// View-ViewModelアーキテクチャにおいてViewからViewModelにcommandを渡す感じ。 /// ここで渡されたコマンドは、CheckUICommand()で吸い出されて実行される。 /// </summary> /// <param name="command"></param> private void AddCommand(UICommand command) { lock (UICommandLock) { UICommands.Add(command); } }
/// <summary> /// UI側から、worker threadで実行して欲しいコマンドを渡す。 /// View-ViewModelアーキテクチャにおいてViewからViewModelにcommandを渡す感じ。 /// ここで渡されたコマンドは、CheckUICommand()で吸い出されて実行される。 /// </summary> /// <param name="command"></param> private void AddCommand(UICommand command) { // workerを作っていないなら、自分のスレッドで実行すれば良い。 if (NoThread) { command(); return; } lock (UICommandLock) { UICommands.Add(command); } }
/// <summary> /// UI側からの中断要求。 /// </summary> public void GameInterruptCommand() { lock (UICommandLock) { UICommands.Add( () => { // コンピューター同士の対局中であっても人間判断で中断できなければならないので常に受理する。 var stm = kifuManager.Position.sideToMove; var stmPlayer = Player(stm); // 中断の指し手 stmPlayer.BestMove = Move.INTERRUPT; }); } }
// 仮想プロパティ // 残り消費時間が変更になった時に呼び出される。 //public bool RestTimeChanged { get; set; } #endregion #region UI側からのコマンド /* * UI側からのコマンドは、 delegateで渡され、対局監視スレッド側で実行される。 * delegateのなかのkifuManager.PositionやkifuManager.KifuListは、無名関数の束縛の性質から、 * 現在のものであって過去のPositionやKifuListのへ参照ではない。 * * また、処理するのは対局監視スレッドであるから(対局監視スレッドはシングルスレッドでかつ、対局監視スレッド側でしか * Position.DoMove()は行わないので)、これが処理されるタイミングでは、kifuManager.Positionは最新のPositionであり、 * これを調べているときに他のスレッドが勝手にPosition.DoMove()を行ったり、他のコマンドを受け付けたり、持ち時間切れに * なったりすることはない。 */ /// <summary> /// 対局スタート /// </summary> public void GameStartCommand(GameSetting gameSetting) { lock (UICommandLock) { UICommands.Add( () => { // いったんリセット GameEnd(); GameStart(gameSetting); // エンジンの初期化が終わったタイミングで自動的にNotifyTurnChanged()が呼び出される。 } ); } }
/// <summary> /// エンジンに対して、いますぐに指させる。 /// 受理されるかどうかは別。 /// </summary> public void MoveNowCommand() { lock (UICommandLock) { UICommands.Add( () => { var stm = kifuManager.Position.sideToMove; var stmPlayer = Player(stm); // エンジン以外であれば受理しない。 if (stmPlayer.PlayerType == PlayerTypeEnum.UsiEngine) { var enginePlayer = stmPlayer as UsiEnginePlayer; enginePlayer.MoveNow(); } }); } }
/// <summary> /// ユーザーから指し手が指されたときにUI側から呼び出す。 /// /// ユーザーがマウス操作によってmの指し手を入力した。 /// ユーザーはこれを合法手だと思っているが、これが受理されるかどうかは別の話。 /// (時間切れなどがあるので) /// </summary> /// <param name="m"></param> public void DoMoveCommand(Move m) { lock (UICommandLock) { UICommands.Add( () => { var stm = kifuManager.Position.sideToMove; var stmPlayer = Player(stm); // Human以外であれば受理しない。 if (stmPlayer.PlayerType == PlayerTypeEnum.Human) { // これを積んでおけばworker_threadのほうでいずれ処理される。(かも) // 仮に、すでに次の局面になっていたとしても、次にこのユーザーの手番になったときに // BestMove = Move.NONEとされるのでその時に破棄される。 stmPlayer.BestMove = m; } }); } }
/// <summary> /// ユーザーによる対局中の2手戻し /// 受理できるかどうかは別 /// </summary> public void UndoCommand() { lock (UICommandLock) { UICommands.Add( () => { var stm = kifuManager.Position.sideToMove; var stmPlayer = Player(stm); // 人間の手番でなければ受理しない if (stmPlayer.PlayerType == PlayerTypeEnum.Human) { // 棋譜を消すUndo() kifuManager.UndoMoveInTheGame(); kifuManager.UndoMoveInTheGame(); // これにより、2手目の局面などであれば1手しかundoできずに手番が変わりうるので手番の更新を通知。 NotifyTurnChanged(); } }); } }