/// <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="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> public static Move CreateDrop(BWType turn, Square dst, PieceType dropPieceType) { if (turn == BWType.None) { throw new ArgumentException("turn"); } if (dst == null || !dst.Validate()) { throw new ArgumentException("dst"); } if (dropPieceType == PieceType.None) { throw new ArgumentException("dropPieceType"); } return(new Move { BWType = turn, DstSquare = dst, DropPieceType = dropPieceType, }); }
/// <summary> /// ハッシュコードを取得します。 /// </summary> public override int GetHashCode() { // ValueType.GetHashCodeは遅いらしい。。。 if (IsSpecialMove) { return(SpecialMoveType.GetHashCode()); } else { var baseHashCode = BWType.GetHashCode() ^ SameAsOld.GetHashCode() ^ RelFileType.GetHashCode() ^ RankMoveType.GetHashCode() ^ ActionType.GetHashCode() ^ (Piece != null ? Piece.GetHashCode() : 0) ^ (SrcSquare != null ? SrcSquare.GetHashCode() : 0); if (SameAsOld) { return(baseHashCode); } else { return( baseHashCode ^ File.GetHashCode() ^ Rank.GetHashCode()); } } }
/// <summary> /// ハッシュ値を返します。 /// </summary> public override int GetHashCode() { return( BWType.GetHashCode() ^ PieceType.GetHashCode() ^ IsPromoted.GetHashCode()); }
/// <summary> /// 成るか不成りかダイアログによる選択を行います。 /// </summary> private bool CheckToPromote(PieceType pieceType, BWType bwType) { var dialog = new PromoteDialog(); var screenPos = Cursor.Position; dialog.StartPosition = FormStartPosition.Manual; dialog.Left = screenPos.X - (dialog.Width / 2); dialog.Top = screenPos.Y - dialog.Height - (int)SquareSize.Height / 2; dialog.AdjustInDisplay(); try { ClosePromoteDialog(); // 成り・不成り選択中に外から局面が設定されることがあります。 // その場合に備えてダイアログ自体を持っておきます。 this.promoteDialog = dialog; var result = dialog.ShowDialog(); ClosePromoteDialog(); return(result == DialogResult.OK); } finally { ClosePromoteDialog(); } }
private Board MakeBoard1(BWType turn) { /* 後手の持駒:飛 桂 香 歩三 9 8 7 6 5 4 3 2 1 +---------------------------+ |v香 ・ 龍 ・ ・ ・ ・ ・ ・|一 | ・ ・ ・ ・v玉 ・ ・ ・ ・|二 | ・ 香v歩 ・v金 ・v桂 ・ ・|三 | ・ 歩v銀v銀v金 ・ ・ 銀 ・|四 | 歩 ・ ・ ・ ・ 歩 歩 ・ ・|五 | 香 ・ 歩 ・ 馬 ・ ・ ・v歩|六 |v歩 ・ 桂 金 ・ ・ ・ ・ ・|七 | ・ 玉 銀 金 ・ ・ 馬 ・ ・|八 | ・ ・ ・ ・ ・ ・ ・ ・ ・|九 +---------------------------+ 先手の持駒:桂 歩七 */ var turnCh = (turn == BWType.Black ? 'b' : 'w'); var sfen = "l1+R6/4k4/1Lp1g1n2/1Pssg2S1/P4PP2/L1P1+B3p/p1NG5/1KSG2+B2/9" + " " + turnCh + " N7Prnl3p 1"; var board = SfenBoard.Parse(sfen); Assert.NotNull(board); Assert.True(board.Validate()); return board; }
/// <summary> /// 駒を強制的に成る必要があるか調べます。 /// </summary> public static bool IsPromoteForce(Piece piece, BWType bwType, Square dstSquare) { if (dstSquare == null || !dstSquare.Validate()) { return(false); } // 駒の移動元に自分の駒がなければダメ if (piece == null || piece.IsPromoted) { return(false); } var normalizedRank = ( bwType == BWType.White ? (BoardSize + 1) - dstSquare.Rank : dstSquare.Rank); if (piece.PieceType == PieceType.Kei) { return(normalizedRank <= 2); } else if (piece.PieceType == PieceType.Kyo || piece.PieceType == PieceType.Hu) { return(normalizedRank == 1); } return(false); }
/// <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 = new BoardMove() { DstSquare = dstSquare, SrcSquare = srcSquare, MovePiece = piece.Piece, BWType = bwType, }; // 成り駒でなければ、成る可能性があります。 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> /// 詰みになったときの手数をパースします。 /// </summary> /// <example> /// + /// -10 /// +5↑ /// </example> public static Score ParseMate(string text, BWType turn) { if (string.IsNullOrEmpty(text)) { throw new ArgumentNullException("text"); } var trimmedText = text.Trim(); var value = StringToInt(trimmedText); if (value == 0) { if (trimmedText[0] == '+') { return new Score(turn, trimmedText, 0, true); } else if (trimmedText[0] == '-') { return new Score(turn, trimmedText, 0, false); } else { throw new ShogiException( trimmedText + ": 詰み手数が正しくありません。"); } } return new Score(turn, trimmedText, Math.Abs(value), (value > 0)); }
/// <summary> /// 詰みになったときの手数をパースします。 /// </summary> /// <example> /// + /// -10 /// +5↑ /// </example> public static Score ParseMate(string text, BWType turn) { if (string.IsNullOrEmpty(text)) { throw new ArgumentNullException("text"); } var trimmedText = text.Trim(); var value = StringToInt(trimmedText); if (value == 0) { if (trimmedText[0] == '+') { return(new Score(turn, 0, true)); } else if (trimmedText[0] == '-') { return(new Score(turn, 0, false)); } else { //throw new ShogiException( // trimmedText + ": メイト手数が正しくありません。"); // 本来は先頭に+/-が必要ですが、そうなっていないソフトも多いので // ここでは現状に合わせてエラーにはしないことにします。 return(new Score(turn, 0, true)); } } return(new Score(turn, Math.Abs(value), (value > 0))); }
/// <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 = new BoardMove() { DstSquare = dstSquare, DropPieceType = pieceType, BWType = bwType, }; // 駒打ちが可能なら、それも該当手となります。 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; } } }
private Board MakeBoard1(BWType turn) { /* * 後手の持駒:飛 桂 香 歩三 * 9 8 7 6 5 4 3 2 1 +---------------------------+ |v香 ・ 龍 ・ ・ ・ ・ ・ ・|一 | ・ ・ ・ ・v玉 ・ ・ ・ ・|二 | ・ 香v歩 ・v金 ・v桂 ・ ・|三 | ・ 歩v銀v銀v金 ・ ・ 銀 ・|四 | 歩 ・ ・ ・ ・ 歩 歩 ・ ・|五 | 香 ・ 歩 ・ 馬 ・ ・ ・v歩|六 |v歩 ・ 桂 金 ・ ・ ・ ・ ・|七 | ・ 玉 銀 金 ・ ・ 馬 ・ ・|八 | ・ ・ ・ ・ ・ ・ ・ ・ ・|九 +---------------------------+ | 先手の持駒:桂 歩七 */ var turnCh = (turn == BWType.Black ? 'b' : 'w'); var sfen = "l1+R6/4k4/1Lp1g1n2/1Pssg2S1/P4PP2/L1P1+B3p/p1NG5/1KSG2+B2/9" + " " + turnCh + " N7Prnl3p 1"; var board = SfenBoard.Parse(sfen); Assert.NotNull(board); Assert.True(board.Validate()); return(board); }
/// <summary> /// コンストラクタ /// </summary> public Score(BWType turn, string text = "", int value = 0) { ScoreType = ScoreType.Value; Turn = turn; Text = text; Value = value; }
/// <summary> /// 詰みになったときの手数をパースします。 /// </summary> /// <example> /// + /// -10 /// +5↑ /// </example> public static Score ParseMate(string text, BWType turn) { if (string.IsNullOrEmpty(text)) { throw new ArgumentNullException("text"); } var trimmedText = text.Trim(); var value = StringToInt(trimmedText); if (value == 0) { if (trimmedText[0] == '+') { return(new Score(turn, trimmedText, 0, true)); } else if (trimmedText[0] == '-') { return(new Score(turn, trimmedText, 0, false)); } else { throw new ShogiException( trimmedText + ": 詰み手数が正しくありません。"); } } return(new Score(turn, trimmedText, Math.Abs(value), (value > 0))); }
/// <summary> /// 駒の種類と新しい位置から、可能な指し手をすべて検索します。 /// </summary> public IEnumerable <Move> ListupMoves(Piece piece, BWType bwType, Square dstSquare) { // 打てる駒をすべて列挙します。 if (!piece.IsPromoted && GetHand(piece.PieceType, bwType) > 0) { var move = Move.CreateDrop(bwType, dstSquare, piece.PieceType); // 駒打ちが可能なら、それも該当手となります。 if (CanMove(move, MoveFlags.CheckOnly)) { yield return(move); } } // 移動による指し手をすべて列挙します。 var srcRange = GetCanMoveRange(piece, dstSquare, bwType.Flip()); foreach (var srcSquare in srcRange) { var moves = GetAvailableMove(piece, bwType, srcSquare, dstSquare); foreach (var move in moves) { 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 = Move.CreateDrop(bwType, square, pieceType); if (DoMove(move, MoveFlags.DoMoveDefault & ~MoveFlags.CheckPawnDropCheckMate)) { if (!IsChecked(bwType)) { Undo(); return(false); } Undo(); return(true); } return(true); }
/// <summary> /// <paramref name="srcSquare"/>の駒を<paramref name="dstSquare"/> /// に動かすことが可能な指し手をすべて列挙します。 /// </summary> private IEnumerable <Move> GetAvailableMove(Piece target, BWType bwType, Square srcSquare, Square dstSquare) { var piece = this[srcSquare]; if (piece == null || piece.Piece != target || piece.BWType != bwType) { yield break; } var move = Move.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> /// 駒の種類と新しい位置から、可能な指し手をすべて検索します。 /// </summary> public IEnumerable <BoardMove> ListupMoves(Piece piece, BWType bwType, Square dstSquare) { return(ListupMoves(bwType, dstSquare) .Where(_ => (_.MovePiece == piece) || (_.DropPieceType == piece.PieceType && !piece.IsPromoted))); }
/// <summary> /// 駒の種類と新しい位置から、可能な指し手をすべて検索します。 /// </summary> public IEnumerable<BoardMove> ListupMoves(Piece piece, BWType bwType, Square dstSquare) { return ListupMoves(bwType, dstSquare) .Where(_ => (_.MovePiece == piece) || (_.DropPieceType == piece.PieceType && !piece.IsPromoted)); }
/// <summary> /// コンストラクタ /// </summary> public Score(BWType turn, int value = 0, ScoreBound bound = ScoreBound.Exact) { ScoreType = ScoreType.Value; ScoreBound = bound; Turn = turn; Value = value; }
/// <summary> /// テーブルを使用し、指定の相対位置に動けるか調べます。(先手専用) /// </summary> private bool CanMoveWithTable(BWType bwType, int relFile, int relRank, int[][] table) { // 後手側なら上下反転します。 var sign = (bwType == BWType.White ? -1 : +1); relRank *= sign; return table.Any(_ => relFile == _[0] && relRank == _[1]); }
/// <summary> /// 持ち駒の数を減らします。 /// </summary> public void DecCapturedPieceCount(PieceType pieceType, BWType bwType) { using (LazyLock()) { var capturedPiece = GetCapturedPieceBox(bwType); capturedPiece.Decrement(pieceType); } }
/// <summary> /// 指定の筋にある歩の数を確認します。 /// </summary> public int GetPawnCount(BWType bwType, int file) { return(Enumerable.Range(1, BoardSize) .Select(_ => this[file, _]) .Where(_ => _ != null) .Where(_ => _.Piece == Piece.Hu) .Where(_ => _.BWType == bwType) .Count()); }
/// <summary> /// 局面と表示で駒台の駒の数を合わせます。 /// </summary> private void SyncCapturedPieceCount(PieceType pieceType, BWType bwType) { var piece = GetCapturedPieceObject(pieceType, bwType); if (piece != null) { piece.Count = GetCapturedPieceCount(pieceType, bwType); } }
/// <summary> /// コンストラクタ /// </summary> public Score(BWType turn, int mate, bool isMateWin) { ScoreType = ScoreType.Mate; ScoreBound = ScoreBound.Exact; Turn = turn; Mate = mate; IsMateWin = isMateWin; Value = (isMateWin ? MateScore : -MateScore); }
/// <summary> /// 持ち駒の数を取得します。 /// </summary> public int GetCapturedPieceCount(PieceType pieceType, BWType bwType) { using (LazyLock()) { var capturedPiece = GetCapturedPieceBox(bwType); return(capturedPiece.GetCount(pieceType)); } }
/// <summary> /// テーブルを使用し、指定の相対位置に動けるか調べます。(先手専用) /// </summary> private bool CanMoveWithTable(BWType bwType, int relFile, int relRank, int[][] table) { // 後手側なら上下反転します。 var sign = (bwType == BWType.White ? -1 : +1); relRank *= sign; return(table.Any(_ => relFile == _[0] && relRank == _[1])); }
/// <summary> /// 指定の筋にある歩の数を取得します。 /// </summary> private int GetHuCountOfFile(int file, BWType bwType) { return(Enumerable.Range(1, BoardSize) .Select(_ => this[file, _]) .Where(_ => _ != null) .Where(_ => _.Piece == Piece.Hu) .Where(_ => _.BWType == bwType) .Count()); }
/// <summary> /// コンストラクタ /// </summary> public Score(BWType turn, string text, int mate, bool isMateWin) { ScoreType = ScoreType.Mate; Turn = turn; Text = text; Mate = mate; IsMateWin = isMateWin; Value = (isMateWin ? MateScore : -MateScore); }
/// <summary> /// 手番をSFEN形式に変換します。 /// </summary> private static string TurnToSfen(BWType turn) { if (turn == BWType.None) { throw new SfenException( "局面の手番が正しくありません。"); } return(turn == BWType.Black ? "b" : "w"); }
/// <summary> /// 駒台上の表示用の駒を取得します。 /// </summary> private PieceObject GetCapturedPieceObject(PieceType pieceType, BWType bwType) { var index = GetCapturedPieceIndex(bwType, BWType.Black); var capturedPieceList = this.capturedPieceObjectList[index]; return( (int)pieceType < capturedPieceList.Count ? capturedPieceList[(int)pieceType] : null); }
/// <summary> /// 手番をCSA形式に変換します。 /// </summary> private static string TurnToCsa(BWType turn) { if (turn == BWType.None) { throw new CsaException( "局面の手番が正しくありません。"); } return (turn == BWType.Black ? "+" : "-"); }
/// <summary> /// 持ち駒の数を設定します。 /// </summary> public void SetCapturedPieceCount(PieceType pieceType, BWType bwType, int count) { using (LazyLock()) { var capturedPiece = GetCapturedPieceBox(bwType); capturedPiece.SetCount(pieceType, count); } }
/// <summary> /// 駒の種類にかかわりなく、指定の位置に着手可能な指し手をすべて列挙します。 /// </summary> public IEnumerable <Move> ListupMoves(BWType bwType, Square dstSquare) { return (from type in EnumEx.GetValues <PieceType>() where type != PieceType.None from promoted in new bool[] { true, false } let piece = new Piece(type, promoted) from move in ListupMoves(piece, bwType, dstSquare) select move); }
/// <summary> /// 手番をCSA形式に変換します。 /// </summary> private static string TurnToCsa(BWType turn) { if (turn == BWType.None) { throw new CsaException( "局面の手番が正しくありません。"); } return(turn == BWType.Black ? "+" : "-"); }
/// <summary> /// 持ち駒の数を取得します。 /// </summary> public Hand GetHand(BWType bwType) { if (bwType == BWType.Black) { return(this.blackHandBox); } else { return(this.whiteHandBox); } }
/// <summary> /// 通常の評価値をパースします。 /// </summary> /// <example> /// 0 /// -98 /// +456↑ /// </example> public static Score ParseValue(string text, BWType turn) { if (string.IsNullOrEmpty(text)) { throw new ArgumentNullException("text"); } var trimmedText = text.Trim(); var value = StringToInt(trimmedText); return new Score(turn, trimmedText, value); }
/// <summary> /// テーブルを使用し、指定の相対位置に動けるか調べます。(先手専用) /// </summary> private IEnumerable<Square> GetMoveRangeWithTable(BWType bwType, Square square, int[][] table) { // 後手側なら上下反転します。 var sign = (bwType == BWType.White ? -1 : +1); foreach (var elem in table) { var sq = new Square( square.File + elem[0], square.Rank + elem[1] * sign); if (sq.Validate()) { yield return sq; } } }
/// <summary> /// CSA形式の局面を1行ごと読み込んでみます。 /// </summary> public bool TryParse(string line) { if (string.IsNullOrEmpty(line)) { throw new ArgumentNullException("line"); } var trimmedLine = line.TrimEnd('\r', '\n'); // 手番読み取り var ch0 = trimmedLine[0]; if (trimmedLine.Length == 1 && (ch0 == '+' || ch0 == '-')) { this.turn = (ch0 == '+' ? BWType.Black : BWType.White); return true; } // 各形式の局面読み取り if (ch0 == 'P' && trimmedLine.Length >= 2) { var ch1 = trimmedLine[1]; if (ch1 == 'I') { ParseBoardPI(trimmedLine); return true; } if (ch1 == '+' || ch1 == '-') { ParseBoardP(trimmedLine); return true; } if ('1' <= ch1 && ch1 <= '9') { ParseBoardPn(trimmedLine); return true; } } return false; }
/// <summary> /// 持ち駒の各駒をパースします。 /// </summary> /// <remarks> /// 各駒文字を最初の漢字で表し、後に続く漢数字でその数を示します。 /// </remarks> private Tuple<PieceType, int> ParseHandPiece(BWType bwType, string handPieceText) { // 駒の種類を取得します。 var piece = KifUtil.CharToPiece(handPieceText[0]); if (piece == null) { throw new ShogiException( string.Format( "{0}手の持ち駒'{1}'が正しくありません。", bwType == BWType.Black ? "先" : "後", handPieceText)); } if (piece.IsPromoted) { throw new ShogiException( string.Format( "{0}手の持ち駒に成り駒があります。", bwType == BWType.Black ? "先" : "後")); } // 駒の数指定があれば、それを解析します。 int count = 1; if (handPieceText.Length > 1) { var numText = handPieceText.Substring(1); var normalized = StringNormalizer.NormalizeNumber(numText); count = int.Parse(normalized); } return Tuple.Create(piece.PieceType, count); }
/// <summary> /// <paramref name="square"/>にある駒が移動できる /// 可能性のある位置をすべて列挙します。 /// </summary> private IEnumerable<Square> GetCanMoveRange(Square square, BWType bwType) { if (square == null || !square.Validate()) { yield break; } var piece = this[square]; if (piece == null || piece.BWType != bwType) { yield break; } IEnumerable<Square> list; var file = square.File; var rank = square.Rank; using (LazyLock()) { if (piece.IsPromoted) { // 成り駒の場合 switch (piece.PieceType) { case PieceType.Gyoku: case PieceType.Hisya: case PieceType.Kaku: list = GetMoveRangeWithTable(bwType, square, MoveTableGyoku); foreach (var p in list) { yield return p; } break; case PieceType.Kin: case PieceType.Gin: case PieceType.Kei: case PieceType.Kyo: case PieceType.Hu: list = GetMoveRangeWithTable(bwType, square, MoveTableKin); foreach (var p in list) { yield return p; } break; } } else { // 成り駒で無い場合 switch (piece.PieceType) { case PieceType.Gyoku: list = GetMoveRangeWithTable(bwType, square, MoveTableGyoku); foreach (var p in list) { yield return p; } break; case PieceType.Kin: list = GetMoveRangeWithTable(bwType, square, MoveTableKin); foreach (var p in list) { yield return p; } break; case PieceType.Gin: list = GetMoveRangeWithTable(bwType, square, MoveTableGin); foreach (var p in list) { yield return p; } break; case PieceType.Kei: list = GetMoveRangeWithTable(bwType, square, MoveTableKei); foreach (var p in list) { yield return p; } break; case PieceType.Hu: list = GetMoveRangeWithTable(bwType, square, MoveTableHu); foreach (var p in list) { yield return p; } break; case PieceType.Kyo: for (var r = 1; r <= BoardSize; ++r) { yield return new Square(file, r); } break; } } // 飛車角は成り/不成りに関わらず調べる箇所があります。 switch (piece.PieceType) { case PieceType.Hisya: for (var f = 1; f <= BoardSize; ++f) { if (piece.IsPromoted && Math.Abs(file - f) <= 1) { continue; } var sq = new Square(f, rank); if (sq.Validate()) { yield return sq; } } for (var r = 1; r <= BoardSize; ++r) { if (piece.IsPromoted && Math.Abs(rank - r) <= 1) { continue; } var sq = new Square(file, r); if (sq.Validate()) { yield return sq; } } break; case PieceType.Kaku: for (var index = -BoardSize; index <= BoardSize; ++index) { if (piece.IsPromoted && Math.Abs(index) <= 1) { continue; } var sq = new Square(file + index, rank + index); if (sq.Validate()) { yield return sq; } } for (var index = -BoardSize; index <= BoardSize; ++index) { if (piece.IsPromoted && Math.Abs(index) <= 1) { continue; } var sq = new Square(file + index, rank - index); if (sq.Validate()) { yield return sq; } } break; } } }
/// <summary> /// 持ち駒や駒箱の駒の数を増やします。 /// </summary> private void IncHandCount(PieceType pieceType, BWType bwType) { SetHandCount( pieceType, bwType, GetHandCount(pieceType, bwType) + 1); }
/// <summary> /// 成るか不成りかダイアログによる選択を行います。 /// </summary> private bool CheckToPromote(PieceType pieceType, BWType bwType) { var dialog = new PromoteDialog(); var screenPos = Cursor.Position; dialog.StartPosition = FormStartPosition.Manual; dialog.Left = screenPos.X - (dialog.Width / 2); dialog.Top = screenPos.Y - dialog.Height - (int)SquareSize.Height / 2; dialog.AdjustInDisplay(); try { ClosePromoteDialog(); // 成り・不成り選択中に外から局面が設定されることがあります。 // その場合に備えてダイアログ自体を持っておきます。 this.promoteDialog = dialog; var result = dialog.ShowDialog(); ClosePromoteDialog(); return (result == DialogResult.OK); } finally { ClosePromoteDialog(); } }
/// <summary> /// 手番を内部インデックスに変換します。(局面の反転を考慮します) /// </summary> private static int GetPieceBoxIndex(BWType bwType, BWType viewSide) { if (bwType == BWType.None) { return 0; } else if (bwType == viewSide) { return 1; } else { return 2; } }
/// <summary> /// 駒台上の駒のデフォルト中心位置を取得します。 /// </summary> public PointF HandPieceToPoint(PieceType pieceType, BWType bwType) { var index = GetPieceBoxIndex(bwType, ViewSide); var bounds = this.pieceBoxBounds[index]; // ○ 駒位置の計算方法 // 駒台には駒を横に2つ並べます。また、両端と駒と駒の間には // 駒の幅/2をスペースとして挿入します。 // このため、駒の幅/2 を基本区間とし、 // 2(両端) + 1(駒間) + 4(駒数*2) = 7 // を区間数として、駒の位置を計算します。 // // また、縦の計算では先手・後手などの文字列を表示するため、 // 手前側は上部、向かい側は下部に余計な空間を作ります。 // 4(上下端+α) + 3(駒間) + 8(駒数*4) = 15 var hw = bounds.Width / 7; var hh = bounds.Height / 15; var x = ((int)pieceType - 2) % 2; var y = ((int)pieceType - 2) / 2; var offsetY = 0.0f; // 駒箱の玉も表示します。 if (pieceType == PieceType.Gyoku) { x = 1; y = 3; } if (bwType == BWType.None || bwType == ViewSide) { offsetY = 1.9f; } else { x = 1 - x; y = 3 - y; offsetY = 0.3f; } // 駒の中心位置 // 駒の数を右肩に表示するため、少し左にずらしています。 // また、対局者名などを表示するため上下にずらしています。 return new PointF( bounds.Left + hw * (x * 3 + 2 - 0.2f), bounds.Top + hh * (y * 3 + 2 + offsetY)); }
/// <summary> /// 駒の移動などを開始できるかどうか調べます。 /// </summary> private bool CanBeginMove(BWType pieceSide) { if (this.movingPiece != null) { return false; } if (this.autoPlay != null) { return false; } var turn = (Board != null ? Board.Turn : BWType.None); if ((EditMode == EditMode.NoEdit) || (EditMode == EditMode.Normal && turn != pieceSide)) { return false; } return true; }
/// <summary> /// 編集モードでの駒の移動を行います。 /// </summary> /// <remarks> /// 駒の移動元・移動先は /// ・盤面上のマス /// ・駒台 or 駒箱 /// の2種類があります。 /// </remarks> private void DoMoveEditing(Square dstSquare, BWType? boxColor) { var piece = this.movingPiece.BoardPiece; var srcSquare = this.movingPiece.Square; if (dstSquare == null && boxColor == null) { throw new ArgumentException("DoMoveEditing"); } // 駒箱から持ってきた場合は先手番の駒として置きます。 var bwType = ( piece.BWType == BWType.None ? ViewSide : piece.BWType); // 先に盤面の状態を元に戻しておきます。 EndMove(); // 盤上に駒を動かす場合は、2歩を禁止する必要があります。 if (dstSquare != null && piece.Piece == Piece.Hu) { // 同じ筋に動かす場合は2歩の判定を行いません。 if ((srcSquare == null || dstSquare.File != srcSquare.File) && (Board.IsDoublePawn(bwType, dstSquare))) { return; } } if (dstSquare != null && Board.IsPromoteForce(piece.Piece, piece.BWType, dstSquare)) { piece.IsPromoted = true; } // 移動元の駒の消去 if (srcSquare != null) { // 盤上の移動前にあった駒を削除します。 Board[srcSquare] = null; } else { // 駒箱などの駒の数を減らします。 DecHandCount(piece.PieceType, piece.BWType); } if (boxColor != null) { // 駒箱へ移動する場合 IncHandCount(piece.PieceType, boxColor.Value); } else if (dstSquare != null) { // 移動先が盤上の場合 var target = Board[dstSquare]; if (target != null) { IncHandCount(target.PieceType, piece.BWType); Board[dstSquare] = null; } // 移動先の駒を増やします。 Board[dstSquare] = new BoardPiece(piece.PieceType, piece.IsPromoted, bwType); } }
/// <summary> /// 香車が指定の場所に動けるか判断します。 /// </summary> private bool CanMoveKyo(BWType bwType, Square basePos, int relFile, int relRank) { // 香車は横には動けません。 if (relFile != 0) { return false; } // 反対方向には動けません。 if ((bwType == BWType.Black && relRank >= 0) || (bwType == BWType.White && relRank <= 0)) { return false; } var destRank = basePos.Rank + relRank; var addRank = (relRank >= 0 ? +1 : -1); // 基準点には自分がいるので、とりあえず一度は // 駒の位置をズラしておきます。 var baseFile = basePos.File; var baseRank = basePos.Rank + addRank; // 駒を動かしながら、目的地まで動かします。 // 動かす途中に何か駒があれば、目的地へは動けません。 while (baseRank != destRank) { // 自分の駒があっても相手の駒があってもダメです。 if (this[baseFile, baseRank] != null) { return false; } baseRank += addRank; } return true; }
/// <summary> /// 持ち駒をCSA形式に変換します。 /// </summary> /// <example> /// P+00KIOOFU /// </example> private static string HandToCsa(Board board, BWType turn) { var handList = from pieceType in EnumEx.GetValues<PieceType>() let count = board.GetCapturedPieceCount(pieceType, turn) let str = string.Format("00{0}", CsaUtil.PieceToStr(new Piece(pieceType))) let list = Enumerable.Range(1, count).Select(_ => str) select string.Join("", list.ToArray()); // ToArray()しないと、MONOでstring.Joinのコンパイルに失敗します。 var array = handList.ToArray(); if (!array.Any()) { return string.Empty; } else { return string.Format("P{0}{1}", turn == BWType.Black ? "+" : "-", string.Join("", array)); } }
/// <summary> /// 持ち駒を読み込みます。 /// </summary> /// <example> /// 後手の持駒:飛 角 金 銀 桂 香 歩四 /// </example> private void ParseHand(BWType bwType, string handText) { if (handText == "なし") { return; } // 持ち駒をまとめて設定します。 handText .Split(new char[] { ' ', ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries) .Select(_ => ParseHandPiece(bwType, _)) .ForEach(_ => this.board.SetCapturedPieceCount(_.Item1, bwType, _.Item2)); }
/// <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 = new BoardMove() { DstSquare = square, DropPieceType = pieceType, BWType = bwType, }; if (DoMove(move)) { if (!IsChecked(bwType)) { Undo(); return false; } Undo(); return true; } return true; }
/// <summary> /// <paramref name="bwType"/>側の玉が王手されているか調べます。 /// </summary> public bool IsChecked(BWType bwType) { var gyoku = GetGyoku(bwType); if (gyoku == null || !gyoku.Validate()) { return false; } // 玉の位置に移動できる駒があれば王手されています。 return ListupMoves(bwType.Flip(), gyoku).Any(); }
/// <summary> /// 玉のいる位置を取得します。 /// </summary> public Square GetGyoku(BWType bwType) { var list = from sq in Board.AllSquares() let piece = this[sq] where piece != null where piece.PieceType == PieceType.Gyoku where piece.BWType == bwType select sq; return list.FirstOrDefault(); }
/// <summary> /// 強制的に手番を設定します。 /// </summary> private void SetTurn(BWType bwType) { this.board.Turn = bwType; this.turnHandled = true; }
/// <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 = new BoardMove() { DstSquare = dstSquare, SrcSquare = srcSquare, MovePiece = piece.Piece, BWType = bwType, }; // 成り駒でなければ、成る可能性があります。 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> public static string ToString(BWType bwType) { return BWTypeTable[bwType]; }
/// <summary> /// 持ち駒や駒箱の駒の数を減らします。 /// </summary> private void DecHandCount(PieceType pieceType, BWType bwType) { SetHandCount( pieceType, bwType, GetHandCount(pieceType, bwType) - 1); }