/// <summary> /// 上、引、寄るの判定をします。 /// </summary> private static bool CheckRankMoveType(BoardMove bm, Move referenceMove) { if (bm.ActionType == ActionType.Drop) { return(false); } var fileMove = bm.DstSquare.File - bm.SrcSquare.File; var rankMove = bm.DstSquare.Rank - bm.SrcSquare.Rank; switch (referenceMove.RankMoveType) { case RankMoveType.Back: return(bm.BWType == BWType.Black ? (rankMove > 0) : (rankMove < 0)); case RankMoveType.Up: return(bm.BWType == BWType.Black ? (rankMove < 0) : (rankMove > 0)); case RankMoveType.Sideways: return(fileMove != 0 && rankMove == 0); } return(false); }
/// <summary> /// 1手指したときに呼ばれます。 /// </summary> private void MoveDone(BoardMove move) { NotifyBoardChanging(move, false); // 特殊な指し手の場合は手番などの入れ替えを行いません。 // (投了後の局面から駒を移動するため) if (!move.IsSpecialMove) { Turn = Turn.Flip(); PrevMovedSquare = move.DstSquare; } this.moveList.Add(move); // リドゥスタックの更新を行います。 var redoMove = this.redoList.LastOrDefault(); if (redoMove != null && redoMove.Equals(move)) { // 指し手がリドゥと同じなら手を一つ削るだけです。 this.redoList.RemoveAt(this.redoList.Count - 1); } else { this.redoList.Clear(); } // 局面の変化を通知します。 NotifyBoardChanged(move, false); }
/// <summary> /// バイト列から指し手リストを取得します。 /// </summary> private List <BoardMove> DeserializeMoveList(byte[] moveListBytes) { // 指し手が無い場合、配列はnullになります。 if (moveListBytes == null) { return(new List <BoardMove>()); } var result = new List <BoardMove>(moveListBytes.Count() / 4); for (var i = 0; i < moveListBytes.Count(); i += 4) { var bits = ( (moveListBytes[i + 0] << 0) | (moveListBytes[i + 1] << 8) | (moveListBytes[i + 2] << 16) | (moveListBytes[i + 3] << 24)); var boardMove = new BoardMove(); boardMove.Deserialize((uint)bits); result.Add(boardMove); } return(result); }
/// <summary> /// 打つ、成る、成らず、などを判定します。 /// </summary> /// <remarks> /// 駒が指定の場所に移動できない場合は自動的に打つが選ばれます。 /// (盤上に飛車がないのに、"32飛車"のときなど) /// </remarks> private static bool CheckActionType(BoardMove bm, Move referenceMove, bool canMove) { if (referenceMove.ActionType == ActionType.None) { // 指し手一覧の中に移動できる駒があれば、 // 「打」と指定しなければ打つと判定されません。 if (canMove) { // 指定無しと成らずでおkとします。 return( bm.ActionType == ActionType.None || bm.ActionType == ActionType.Unpromote); } else { return( bm.ActionType == ActionType.None || bm.ActionType == ActionType.Unpromote || bm.ActionType == ActionType.Drop); } } else if (referenceMove.ActionType == ActionType.Unpromote) { // 「不成」の場合は無と成らずでおkとします。 return( bm.ActionType == ActionType.None || bm.ActionType == ActionType.Unpromote); } return(bm.ActionType == referenceMove.ActionType); }
/// <summary> /// 駒の種類にかかわりなく、指定の位置に着手可能な指し手をすべて列挙します。 /// </summary> public IEnumerable <BoardMove> ListupMoves(BWType bwType, Square dstSquare) { // 打てる駒をすべて列挙します。 foreach (var pieceType in EnumEx.GetValues <PieceType>()) { if (GetCapturedPieceCount(pieceType, bwType) <= 0) { continue; } var move = BoardMove.CreateDrop(bwType, dstSquare, pieceType); // 駒打ちが可能なら、それも該当手となります。 if (CanMove(move, MoveFlags.CheckOnly)) { yield return(move); } } // 移動による指し手をすべて列挙します。 foreach (var srcSquare in Board.AllSquares()) { var moves = GetAvailableMove(bwType, srcSquare, dstSquare); foreach (var move in moves) { yield return(move); } } }
/// <summary> /// <paramref name="srcSquare"/>の駒を<paramref name="dstSquare"/> /// に動かすことが可能な指し手をすべて列挙します。 /// </summary> private IEnumerable <BoardMove> GetAvailableMove(BWType bwType, Square srcSquare, Square dstSquare) { var piece = this[srcSquare]; if (piece == null || piece.BWType != bwType) { yield break; } var move = BoardMove.CreateMove( bwType, srcSquare, dstSquare, piece.Piece, false); // 成り駒でなければ、成る可能性があります。 if (!piece.IsPromoted) { move.IsPromote = true; if (CanMove(move, MoveFlags.CheckOnly)) { // yield returnなのでCloneしないとまずい。 yield return(move.Clone()); } } move.IsPromote = false; if (CanMove(move, MoveFlags.CheckOnly)) { yield return(move); } }
/// <summary> /// <paramref name="square"/>に<paramref name="pieceType"/>を打ち、 /// なお王手されているか確認します。 /// </summary> private bool IsDropAndChecked(BWType bwType, PieceType pieceType, Square square) { var piece = this[square]; if (piece != null) { return(true); } var move = BoardMove.CreateDrop(bwType, square, pieceType); if (DoMove(move)) { if (!IsChecked(bwType)) { Undo(); return(false); } Undo(); return(true); } return(true); }
/// <summary> /// 複数ある指し手の中から、適切なひとつの指し手を選択します。 /// </summary> /// <remarks> /// <paramref name="referenceMove"/>にはXからYに移動したという情報 /// しかないため、これを52金右などの指し手に変換します。 /// </remarks> private static Move FilterMove(this Board board, List <BoardMove> boardMoveList, BoardMove referenceMove, Piece fromPiece, bool useSrcSquare) { if (!boardMoveList.Any()) { return(null); } var nextPos = referenceMove.DstSquare; var move = new Move { BWType = referenceMove.BWType, Piece = fromPiece, File = nextPos.File, Rank = nextPos.Rank, ActionType = ( // '打', '不成'は消える可能性があります。 referenceMove.ActionType == ActionType.Promote ? referenceMove.ActionType : ActionType.None), SameAsOld = (board.PrevMovedSquare == nextPos), }; // 移動元情報を使う場合は、これで終わりです。 // (.kifファイルからの読み込み時は、駒の移動の場合は移動前情報がつき、 // 駒打ちの場合は"打"が必ずつきます) if (useSrcSquare) { move.SrcSquare = referenceMove.SrcSquare; move.ActionType = referenceMove.ActionType; return(move); } // 駒打ち、成り、不成りなどでフィルターします。 var tmpMoveList = FilterAction(move, referenceMove, boardMoveList); if (!tmpMoveList.Any()) { return(null); } // 段の位置でフィルターします。 tmpMoveList = FilterRank(move, referenceMove, tmpMoveList); if (!tmpMoveList.Any()) { return(null); } // 列の位置でフィルターします。 tmpMoveList = FilterFile(move, referenceMove, tmpMoveList); if (!tmpMoveList.Any()) { return(null); } return(move); }
/// <summary> /// 特殊な指し手の着手が行えるか調べ、必要なら実行します。 /// </summary> private bool CheckAndMakeSpecialMove(BoardMove move, MoveFlags flags) { if (!EnumEx.HasFlag(flags, MoveFlags.CheckOnly)) { MoveDone(move); } return(true); }
/// <summary> /// 駒を強制的に成る必要があるか調べます。 /// </summary> public static bool IsPromoteForce(BoardMove move) { if (move == null) { throw new ArgumentNullException("move"); } return(IsPromoteForce(move.MovePiece, move.BWType, move.DstSquare)); }
/// <summary> /// 段で指し手をフィルターし、Moveに適切なRankMoveTypeを設定します。 /// </summary> private static List <BoardMove> FilterRank(Move move, BoardMove referenceMove, List <BoardMove> boardMoveList) { // 駒の移動前情報が必要です。 var nextPos = referenceMove.DstSquare; var prevPos = referenceMove.SrcSquare; if (prevPos == null) { // 何もフィルターしません。 return(boardMoveList); } // 黒から見ると正の場合は引く or 左へ移動(右)で // 負の場合は上がる or 右へ移動(左)です。 var relRank = nextPos.Rank - prevPos.Rank; // 段の位置でフィルターします。 var tmpMoveList = boardMoveList.Where(mv => { var rel = nextPos.Rank - mv.SrcSquare.Rank; if (relRank < 0) { return(rel < 0); } else if (relRank > 0) { return(rel > 0); } else { return(rel == 0); } }).ToList(); // 段情報でフィルターされた場合は、段の移動情報を付加します。 if (tmpMoveList.Count() != boardMoveList.Count()) { var relRank2 = relRank * referenceMove.BWType.Sign(); if (relRank2 < 0) { move.RankMoveType = RankMoveType.Up; } else if (relRank2 > 0) { move.RankMoveType = RankMoveType.Back; } else { move.RankMoveType = RankMoveType.Sideways; } } return(tmpMoveList); }
/// <summary> /// 局面の変更直前の通知を出します。 /// </summary> private void NotifyBoardChanging(BoardMove move, bool isUndo) { var handler = BoardChanging; if (handler != null) { Util.CallEvent( () => handler(this, new BoardChangedEventArgs(move, isUndo))); } }
/// <summary> /// undo操作を実行します。 /// </summary> private void DoUndo(BoardMove move) { if (move.IsSpecialMove) { // 何もしません。 } else if (move.ActionType == ActionType.Drop) { // 駒打ちの場合は、その駒を駒台に戻します。 this[move.DstSquare] = null; IncCapturedPieceCount(move.DropPieceType, move.BWType); } else { var movedPiece = this[move.DstSquare]; // 駒を成った場合はそれを元に戻します。 if (move.ActionType == ActionType.Promote) { movedPiece.IsPromoted = false; } // 駒を取った場合は、その駒を元に戻します。 if (move.TookPiece != null) { this[move.DstSquare] = new BoardPiece( move.TookPiece.Clone(), move.BWType.Flip()); // 駒を取ったはずなので、その分を駒台から減らします。 DecCapturedPieceCount( move.TookPiece.PieceType, move.BWType); } else { this[move.DstSquare] = null; } this[move.SrcSquare] = movedPiece; } if (!move.IsSpecialMove) { Turn = Turn.Flip(); PrevMovedSquare = ( this.moveList.Any() ? this.moveList.Last().DstSquare : null); } }
/// <summary> /// 駒を動かすか、または駒が動かせるか調べます。 /// </summary> private bool CheckAndMakeMove(BoardMove move, MoveFlags flags) { if (move == null || !move.Validate()) { throw new ArgumentNullException("move"); } // 投了などの特殊な指し手がある場合はゲームが既に終了しているので // 指し手を進めることはできません。 if (HasSpecialMove) { if (flags.HasFlag(MoveFlags.CheckOnly)) { // CheckOnlyの場合は、ここで失敗させません。 } else if (flags.HasFlag(MoveFlags.AutoRemoveSpecialMove)) { // 特殊な指し手を取り除きます。 RemoveSpecialMove(); } else { return(false); } } using (LazyLock()) { // 手番があわなければ失敗とします。 if (EnumEx.HasFlag(flags, MoveFlags.CheckTurn)) { if (this.turn == BWType.None || this.turn != move.BWType) { return(false); } } if (move.IsSpecialMove) { return(CheckAndMakeSpecialMove(move, flags)); } else if (move.ActionType == ActionType.Drop) { return(CheckAndMakeDrop(move, flags)); } else { return(CheckAndMakeMoveOnly(move, flags)); } } }
/// <summary> /// 文字列から得られた指し手から、移動前の情報も含むような /// 指し手情報を取得します。 /// </summary> public static BoardMove ConvertMove(this Board board, Move move, BWType bwType, bool multipleIsNull = false) { if (board == null) { throw new ArgumentNullException("board"); } if (move == null) { return(null); } if (move.SameAsOld && board.PrevMovedSquare == null) { return(null); } if (move.IsSpecialMove) { return(BoardMove.CreateSpecialMove( bwType, move.SpecialMoveType)); } // 移動後の位置を取得します。 // 同○○なら前回の位置を使います。 var dstSquare = move.DstSquare; if (move.SameAsOld) { move = move.Clone(); move.DstSquare = board.PrevMovedSquare; dstSquare = board.PrevMovedSquare; } var boardMoveList = board.ListupMoves( move.Piece, bwType, dstSquare) .ToList(); // 複数の指し手の中から適切な一つを選びます。 var boardMove = FilterBoardMove(boardMoveList, move, multipleIsNull); if (boardMove == null) { return(null); } return(boardMove); }
/// <summary> /// 局面の変更を通知します。 /// </summary> private void NotifyBoardChanged(BoardMove move, bool isUndo) { var handler = BoardChanged; if (handler != null) { Util.CallEvent( () => handler(this, new BoardChangedEventArgs(move, isUndo))); } this.RaisePropertyChanged("MoveCount"); this.RaisePropertyChanged("CanUndo"); this.RaisePropertyChanged("CanRedo"); }
/// <summary> /// 指し手の種類で手をフィルターし、Moveに適切なRankMoveTypeを設定します。 /// </summary> private static List <BoardMove> FilterAction(Move move, BoardMove referenceMove, List <BoardMove> boardMoveList) { var tmpMoveList = boardMoveList.Where( mv => mv.ActionType == referenceMove.ActionType) .ToList(); if (tmpMoveList.Count() != boardMoveList.Count()) { move.ActionType = referenceMove.ActionType; } return(tmpMoveList); }
/// <summary> /// <paramref name="srcSquare"/>の駒を<paramref name="dstSquare"/> /// に動かすことが可能な指し手を指してみて、なお王手されているか確認します。 /// </summary> private bool IsMoveAndChecked(BWType bwType, Square srcSquare, Square dstSquare) { var piece = this[srcSquare]; if (piece == null || piece.BWType != bwType) { return(true); } var move = BoardMove.CreateMove( bwType, srcSquare, dstSquare, piece.Piece, false); // 成り駒でなければ、成る可能性があります。 if (!piece.IsPromoted) { move.IsPromote = true; if (DoMove(move)) { if (!IsChecked(bwType)) { Undo(); return(false); } Undo(); return(true); } } move.IsPromote = false; if (DoMove(move)) { if (!IsChecked(bwType)) { Undo(); return(false); } Undo(); return(true); } return(true); }
/// <summary> /// 駒打ちの動作が行えるか調べ、必要なら実行します。 /// </summary> private bool CheckAndMakeDrop(BoardMove move, MoveFlags flags) { if (GetCapturedPieceCount(move.DropPieceType, move.BWType) <= 0) { return(false); } // 駒を打つ場所に駒があれば、当然失敗です。 var piece = this[move.DstSquare]; if (piece != null) { return(false); } // 駒が置けるか確かめます。 if (!CanDrop(move.BWType, move.DstSquare, move.DropPieceType)) { return(false); } // 打ち歩詰のチェックを行います。 if (move.DropPieceType == PieceType.Hu && IsDropPawnMate(move.BWType, move.DstSquare)) { return(false); } if (!EnumEx.HasFlag(flags, MoveFlags.CheckOnly)) { // 駒を盤面に置き、持ち駒から駒を減らします。 this[move.DstSquare] = new BoardPiece( move.DropPieceType, false, move.BWType); DecCapturedPieceCount(move.DropPieceType, move.BWType); MoveDone(move); } return(true); }
/// <summary> /// 指し手をXX->YYの形式から、ZZ銀上などの形に変換します。 /// </summary> /// <remarks> /// <paramref name="useSrcSquare"/>を真にすると、差し手の後に /// 古い位置の情報が付加されるようになります。(例: 32金(22)) /// </remarks> public static Move ConvertMove(this Board board, BoardMove move, bool useSrcSquare) { if (board == null) { throw new ArgumentNullException("board"); } if (move == null || !move.Validate()) { throw new ArgumentNullException("move"); } if (move.IsSpecialMove) { return(new Move { BWType = move.BWType, SpecialMoveType = move.SpecialMoveType, }); } var fromPiece = ( move.ActionType == ActionType.Drop ? new Piece(move.DropPieceType) : move.MovePiece); if (fromPiece == null) { return(null); } // 駒の種類と最終位置から、あり得る指し手をすべて検索します。 var boardMoveList = board.ListupMoves( fromPiece, move.BWType, move.DstSquare) .ToList(); return(FilterMove( board, boardMoveList, move, fromPiece, useSrcSquare)); }
/// <summary> /// 駒の移動のみの動作を調べるか実際にそれを行います。 /// </summary> private bool CheckAndMakeMoveOnly(BoardMove move, MoveFlags flags) { // 駒の移動元に自分の駒がなければダメ var srcPiece = this[move.SrcSquare]; if (srcPiece == null || srcPiece.BWType != move.BWType) { return(false); } // 駒の移動先に自分の駒があったらダメ var dstPiece = this[move.DstSquare]; if (dstPiece != null && dstPiece.BWType == move.BWType) { return(false); } // これはエラーだけど。。。w if (srcPiece.Piece != move.MovePiece) { return(false); } // 各駒が動ける位置に移動するかどうか確認します。 if (!CanMovePiece(move)) { return(false); } if (move.ActionType == ActionType.Promote) { // 成れない場合は帰ります。 if (!CanPromote(move)) { return(false); } } else { // 成らないといけない場合は帰ります。 if (IsPromoteForce(move)) { return(false); } } if (!EnumEx.HasFlag(flags, MoveFlags.CheckOnly)) { var pieceType = srcPiece.PieceType; // 移動先に駒があれば、それを自分のものにします。 if (dstPiece != null) { IncCapturedPieceCount(dstPiece.PieceType, move.BWType); // 取った駒を記憶しておきます。 move.TookPiece = dstPiece.Piece; } // 移動後の駒の成り/不成りを決定します。 var promoted = ( srcPiece.IsPromoted || move.ActionType == ActionType.Promote); this[move.DstSquare] = new BoardPiece( pieceType, promoted, move.BWType); // 移動前の位置からは駒をなくします。 this[move.SrcSquare] = null; // 前回の指し手と位置が同じか調べます。 move.HasSameSquareAsPrev = (move.DstSquare == PrevMovedSquare); MoveDone(move); } return(true); }
/// <summary> /// 左、右、直の判定をします。 /// </summary> private static bool CheckRelPosType(BoardMove bm, Move referenceMove, List <BoardMove> boardMoveList) { if (bm.ActionType == ActionType.Drop) { return(false); } if (bm.MovePiece == Piece.Ryu || bm.MovePiece == Piece.Uma) { // 竜、馬の場合、「直」は使わずに「右左」のみを使用します。 if (boardMoveList.Count() == 1) { return(referenceMove.RelFileType == RelFileType.None); } else { // 駒は二つしかないはずなので、相方に比べて自分が // 左にあれば「左」、右にあれば「右」となっているか // 判定します。 var other = ( ReferenceEquals(bm, boardMoveList[0]) ? boardMoveList[1] : boardMoveList[0]); var fileDif = bm.SrcSquare.File - other.SrcSquare.File; switch (referenceMove.RelFileType) { case RelFileType.Left: return(bm.BWType == BWType.Black ? (fileDif > 0) : (fileDif < 0)); case RelFileType.Right: return(bm.BWType == BWType.Black ? (fileDif < 0) : (fileDif > 0)); case RelFileType.None: return(fileDif == 0); } } } else { var fileMove = bm.DstSquare.File - bm.SrcSquare.File; var rankMove = bm.DstSquare.Rank - bm.SrcSquare.Rank; switch (referenceMove.RelFileType) { case RelFileType.Left: return(bm.BWType == BWType.Black ? (fileMove < 0) : (fileMove > 0)); case RelFileType.Right: return(bm.BWType == BWType.Black ? (fileMove > 0) : (fileMove < 0)); case RelFileType.Straight: return((bm.BWType == BWType.Black ? (rankMove < 0) : (rankMove > 0)) && (fileMove == 0)); } } return(false); }
/// <summary> /// その指し手を実際に実行します。 /// </summary> public bool DoMove(BoardMove move, MoveFlags flags = MoveFlags.DoMoveDefault) { flags &= ~MoveFlags.CheckOnly; return(CheckAndMakeMove(move, flags)); }
/// <summary> /// 列で指し手をフィルターし、Moveに適切なRelPosTypeを設定します。 /// </summary> private static List <BoardMove> FilterFile(Move move, BoardMove referenceMove, List <BoardMove> boardMoveList) { // 駒の移動前情報が必要です。 var nextPos = referenceMove.DstSquare; var prevPos = referenceMove.SrcSquare; if (prevPos == null) { // 何もフィルターしません。 return(boardMoveList); } if ((move.Piece == Piece.Ryu || move.Piece == Piece.Uma) && boardMoveList.Count() == 2) { // 馬と竜の場合は'直'ではなく、右と左しか使いません。 var other = ( referenceMove == boardMoveList[0] ? boardMoveList[1] : boardMoveList[0]); // 動かす前の駒が相方に比べて右にあるか左にあるか調べます。 // 先手の場合は筋が小さい方が右です。 var relFile = prevPos.File - other.SrcSquare.File; relFile *= (referenceMove.BWType == BWType.White ? -1 : +1); if (relFile == 0) { return(boardMoveList); } else if (relFile < 0) { move.RelFileType = RelFileType.Right; } else { move.RelFileType = RelFileType.Left; } return(new List <BoardMove> { referenceMove }); } else { // 黒から見ると正の場合は引く or 左へ移動(右)で // 負の場合は上がる or 右へ移動(左)です。 var relFile = nextPos.File - prevPos.File; // 列の位置でフィルターします。 var tmpMoveList = boardMoveList.Where(mv => { var rel = nextPos.File - mv.SrcSquare.File; return( relFile < 0 ? (rel < 0) : relFile > 0 ? (rel > 0) : (rel == 0)); }).ToList(); // 列情報でフィルターされた場合は、列の移動情報を付加します。 if (tmpMoveList.Count() != boardMoveList.Count()) { var relFile2 = relFile * referenceMove.BWType.Sign(); if (relFile2 < 0) { move.RelFileType = RelFileType.Left; } else if (relFile2 > 0) { move.RelFileType = RelFileType.Right; } else { // 直の場合は、左右の動きはありません。 move.RankMoveType = RankMoveType.None; move.RelFileType = RelFileType.Straight; } } return(tmpMoveList); } }
/// <summary> /// コンストラクタ /// </summary> public BoardChangedEventArgs(BoardMove move, bool isUndo) { Move = move; IsUndo = isUndo; }
/// <summary> /// 実際に駒が動けるか確認します。 /// </summary> private bool CanMovePiece(BoardMove move) { var relFile = move.DstSquare.File - move.SrcSquare.File; var relRank = move.DstSquare.Rank - move.SrcSquare.Rank; var piece = move.MovePiece; if (piece.IsPromoted) { // 成り駒が指定の場所に動けるか調べます。 switch (piece.PieceType) { case PieceType.Gyoku: return(CanMoveWithTable(move.BWType, relFile, relRank, MoveTableGyoku)); case PieceType.Hisya: if (CanMoveWithTable(move.BWType, relFile, relRank, MoveTableGyoku)) { return(true); } return(CanMoveHisya(move.SrcSquare, relFile, relRank)); case PieceType.Kaku: if (CanMoveWithTable(move.BWType, relFile, relRank, MoveTableGyoku)) { return(true); } return(CanMoveKaku(move.SrcSquare, relFile, relRank)); case PieceType.Kin: case PieceType.Gin: case PieceType.Kei: case PieceType.Kyo: case PieceType.Hu: return(CanMoveWithTable(move.BWType, relFile, relRank, MoveTableKin)); } } else { // 成り駒以外の駒が指定の場所に動けるか調べます。 switch (piece.PieceType) { case PieceType.Gyoku: return(CanMoveWithTable(move.BWType, relFile, relRank, MoveTableGyoku)); case PieceType.Hisya: return(CanMoveHisya(move.SrcSquare, relFile, relRank)); case PieceType.Kaku: return(CanMoveKaku(move.SrcSquare, relFile, relRank)); case PieceType.Kin: return(CanMoveWithTable(move.BWType, relFile, relRank, MoveTableKin)); case PieceType.Gin: return(CanMoveWithTable(move.BWType, relFile, relRank, MoveTableGin)); case PieceType.Kei: return(CanMoveWithTable(move.BWType, relFile, relRank, MoveTableKei)); case PieceType.Kyo: return(CanMoveKyo(move.BWType, move.SrcSquare, relFile, relRank)); case PieceType.Hu: return(CanMoveWithTable(move.BWType, relFile, relRank, MoveTableHu)); } } return(false); }