Top >コンソール・アプリケーション集

ヘキサポーン Hexapawn C#

using System;
using System.Collections.Generic;
using System.Text;

/// <summary>
/// 盤のマス等、白、黒、その他を表す
/// </summary>
enum HexPawnDefs : byte
{
    None,       // 盤外、未定を表す
    Black,      // 黒を表す
    White,      // 白を表す
    Empty       // 空き
}

/// <summary>
/// 盤のマスを示すクラス
/// </summary>
class HpPoint
{
    public int file { getset; }   // 左を0とした横方向
    public int rank { getset; }   // 下を0とした縦方向

    /// <summary>
    /// コンストラクタ
    /// </summary>
    public HpPoint()
    {
        file = -1;
        rank = -1;
    }

    /// <summary>
    /// コンストラクタ
    /// </summary>
    /// <param name="f">ファイル(横)</param>
    /// <param name="r">ランク(縦)</param>
    public HpPoint(int f, int r)
    {
        file = f;
        rank = r;
    }

    /// <summary>
    /// 左右反対のHpPointを取得する
    /// </summary>
    /// <returns></returns>
    public HpPoint Mirror()
    {
        HpPoint mirror = new HpPoint(this.file, this.rank);

        if (mirror.file == 0)
            mirror.file = 2;
        else if (mirror.file == 2)
            mirror.file = 0;

        return mirror;
    }

    /// <summary>
    /// HpPointを文字列にする
    /// </summary>
    /// <returns>"a1","b3"のような文字列</returns>
    public override string ToString()
    {
        string s = "";

        s += (char)('a' + this.file);   // 左から a,b,c
        s += (char)('1' + this.rank);   // 下から 1,2,3

        return s;
    }
}

/// <summary>
/// 盤の状態を表すクラス
/// </summary>
class HpBoard
{
    protected static int BOARD_SIZE = 3;    // 盤の大きさ

    protected HexPawnDefs[,] Squares = new HexPawnDefs[BOARD_SIZE, BOARD_SIZE]; // 盤のマス

    /// <summary>
    /// コンストラクタ
    /// </summary>
    public HpBoard()
    {
        // 盤の初期状態を作成する
        for (int file = 0; file < BOARD_SIZE; file++)
        {
            SetSquare(new HpPoint(file, 2), HexPawnDefs.Black); // 黒のポーン
            SetSquare(new HpPoint(file, 1), HexPawnDefs.Empty); // 空き
            SetSquare(new HpPoint(file, 0), HexPawnDefs.White); // 白のポーン
        }
    }

    /// <summary>
    /// すべてのマスの位置を取得する
    /// </summary>
    /// <returns>HpPointの配列</returns>
    public static HpPoint[] GetAllPoints()
    {
        List<HpPoint> list = new List<HpPoint>();

        for (int rank = 0; rank < BOARD_SIZE; rank++)
        {
            for (int file = 0; file < BOARD_SIZE; file++)
                list.Add(new HpPoint(file, rank));
        }

        return list.ToArray();
    }

    /// <summary>
    /// 指定されたマスの状態を取得する
    /// </summary>
    /// <param name="pt">マスの位置</param>
    /// <returns>マスの状態</returns>
    public HexPawnDefs GetSquare(HpPoint pt)
    {
        return GetSquare(pt.file, pt.rank);
    }

    /// <summary>
    /// 指定されたマスの状態を取得する
    /// </summary>
    /// <param name="file">横方向の位置</param>
    /// <param name="rank">縦方向の位置</param>
    /// <returns>マスの状態</returns>
    public HexPawnDefs GetSquare(int file, int rank)
    {
        if (0 <= file && file < BOARD_SIZE
            && 0 <= rank && rank < BOARD_SIZE)
        {
            return Squares[file, rank];
        }
        else
        {
            return HexPawnDefs.None;
        }
    }

    /// <summary>
    /// 指定されたマスの状態を設定する
    /// </summary>
    /// <param name="pt">マスの位置</param>
    /// <param name="pawn">マスの状態</param>
    public void SetSquare(HpPoint pt, HexPawnDefs pawn)
    {
        SetSquare(pt.file, pt.rank, pawn);
    }

    /// <summary>
    /// 指定されたマスの状態を設定する
    /// </summary>
    /// <param name="file">横方向の位置</param>
    /// <param name="rank">縦方向の位置</param>
    /// <param name="pawn">マスの状態</param>
    public void SetSquare(int file, int rank, HexPawnDefs pawn)
    {
        if (0 <= file && file < BOARD_SIZE
            && 0 <= rank && rank < BOARD_SIZE)
        {
            Squares[file, rank] = pawn;
        }
    }

    /// <summary>
    /// 盤の状態を表示する
    /// </summary>
    public void Show()
    {
        Console.WriteLine("");

        for (int rank = BOARD_SIZE - 1; rank >= 0; rank--)
        {
            Console.Write("\n   +---+---+---+");
            Console.Write("\n " + (rank + 1) + " |");

            for (int file = 0; file < BOARD_SIZE; file++)
            {
                if (GetSquare(new HpPoint(file, rank)) == HexPawnDefs.Black)
                    Console.Write(" ○|");
                else if (GetSquare(new HpPoint(file, rank)) == HexPawnDefs.White)
                    Console.Write(" ●|");
                else if ((rank % 2) == (file % 2))
                    Console.Write(":::|");
                else
                    Console.Write("   |");
            }

            if (rank == 2)
                Console.Write("      7  8  9");
            else if (rank == 1)
                Console.Write("      4  5  6");
            else if (rank == 0)
                Console.Write("      1  2  3");
        }

        Console.Write("\n   +---+---+---+");
        Console.Write("\n     a   b   c\n");
    }

    /// <summary>
    /// 指定された位置のポーンが動けるかどうか
    /// </summary>
    /// <param name="pt">調べるマス</param>
    /// <returns>true:動ける、false:動けない</returns>
    public bool IsMoveablePawn(HpPoint pt)
    {
        int dir;            // 進む方向
        HexPawnDefs opp;    // 対戦相手の駒

        if (GetSquare(pt) == HexPawnDefs.White)
        {
            dir = 1;
            opp = HexPawnDefs.Black;
        }
        else if (GetSquare(pt) == HexPawnDefs.Black)
        {
            dir = -1;
            opp = HexPawnDefs.White;
        }
        else
        {
            return false;
        }

        if (GetSquare(pt.file, pt.rank + dir) == HexPawnDefs.Empty)
            return true;    // 1マス前が空
        else if (GetSquare(pt.file - 1, pt.rank + dir) == opp)
            return true;    // 斜め前に相手の駒がある
        else if (GetSquare(pt.file + 1, pt.rank + dir) == opp)
            return true;    // 反対の斜め前に相手の駒がある
        else
            return false;
    }

    /// <summary>
    /// 勝敗を確認する
    /// </summary>
    /// <param name="next">次が白番か、黒番か</param>
    /// <returns>勝った方の色、または、未定</returns>
    public HexPawnDefs CheckFinish(HexPawnDefs next)
    {
        // 3列目に駒があるか /////////////////////////
        for (int file = 0; file < BOARD_SIZE; file++)
        {
            if (GetSquare(file, BOARD_SIZE - 1) == HexPawnDefs.White)
                return HexPawnDefs.White;
            else if (GetSquare(file, 0) == HexPawnDefs.Black)
                return HexPawnDefs.Black;
        }

        // 動ける駒の数 ///////////////////////////////
        HpPoint[] all = HpBoard.GetAllPoints();
        int cntWhite = 0;
        int cntBlack = 0;

        foreach( HpPoint pt in all )
        {
            if (IsMoveablePawn(pt))
            {
                if (HexPawnDefs.White == GetSquare(pt))
                    cntWhite++;
                else if (HexPawnDefs.Black == GetSquare(pt))
                    cntBlack++;
            }
        }

        if (next == HexPawnDefs.White && cntWhite == 0)
            return HexPawnDefs.Black;   // 次が白番で動かせる駒が無い
        else if (next == HexPawnDefs.Black && cntBlack == 0)
            return HexPawnDefs.White;   // 次が黒番で動かせる駒が無い

        return HexPawnDefs.Empty;   // 勝敗未定
    }
};

/// <summary>
/// 盤の状態と駒の移動を表すクラス
/// </summary>
class HpBoardMove : HpBoard
{
    protected HpPoint From = null;  // 駒の移動元
    protected HpPoint To = null;    // 駒の移動先

    /// <summary>
    /// コンストラクタ
    /// </summary>
    public HpBoardMove()
    {
    }

    /// <summary>
    /// コンストラクタ(コピー)
    /// </summary>
    /// <param name="bdmove">コピー元</param>
    public HpBoardMove(HpBoardMove bdmove)
    {
        HpPoint[] all = HpBoard.GetAllPoints();

        foreach (HpPoint pt in all)
            SetSquare(pt, bdmove.GetSquare(pt));

        From = bdmove.From;
        To = bdmove.To;
    }

    /// <summary>
    /// 移動を設定する
    /// </summary>
    /// <param name="from">移動元</param>
    /// <param name="to">移動先</param>
    public void SetMove(HpPoint from, HpPoint to)
    {
        From = from;
        To = to;
    }

    /// <summary>
    ///  駒の移動が有効であるかどうかを調べる
    /// </summary>
    /// <returns>true:有効、false:無効</returns>
    public bool IsValidMove()
    {
        int df = To.file - From.file;   // 横方向移動量
        int dr = To.rank - From.rank;   // 縦方向移動量

        if (GetSquare(From) == HexPawnDefs.White)
        {
            // 動かす駒が白

            if (GetSquare(To) == HexPawnDefs.Empty && df == 0 && dr == 1)
                return true;    // 1マス前に動ける
            else if (GetSquare(To) == HexPawnDefs.Black && (df == 1 || df == -1) && dr == 1)
                return true;    // 斜め前に動ける
        }
        else if (GetSquare(From) == HexPawnDefs.Black)
        {
            // 動かす駒が黒

            if (GetSquare(To) == HexPawnDefs.Empty && df == 0 && dr == -1)
                return true;    // 1マス前に動ける
            else if (GetSquare(To) == HexPawnDefs.White && (df == 1 || df == -1) && dr == -1)
                return true;    // 斜め前に動ける
        }

        return false;
    }

    /// <summary>
    /// 同じかどうか比較する
    /// </summary>
    /// <param name="bdmove">比較先</param>
    /// <returns>true:同じ</returns>
    public bool TestEqual(HpBoardMove bdmove)
    {
        HpPoint[] all = HpBoard.GetAllPoints();

        foreach (HpPoint pt in all)
        {
            if (this.GetSquare(pt) != bdmove.GetSquare(pt))
                return false;
        }

        if (this.From.file != bdmove.From.file || this.From.rank != bdmove.From.rank
            || this.To.file != bdmove.To.file || this.To.rank != bdmove.To.rank)
            return false;

        return true;
    }

    /// <summary>
    /// 駒を移動する
    /// </summary>
    public void MovePawn()
    {
        SetSquare(To, GetSquare(From));
        SetSquare(From, HexPawnDefs.Empty);
    }

    /// <summary>
    /// 左右反対の状態を作成する
    /// </summary>
    /// <returns>左右反対のHpBoardMove</returns>
    public HpBoardMove MakeMirror()
    {
        HpBoardMove mirror = new HpBoardMove();

        mirror.SetMove(From.Mirror(), To.Mirror());

        HpPoint[] all = HpBoard.GetAllPoints();

        foreach (HpPoint pt in all)
            mirror.SetSquare(pt, GetSquare(pt.Mirror()));

        return mirror;
    }
};

/// <summary>
/// ゲームを実現するクラス
/// </summary>
class Game
{
    private List<HpBoardMove> DefeatList = new List<HpBoardMove>(); // 負けの状態リスト
    private Random  RandObj = new Random();     // 乱数オブジェクト

    private HexPawnDefs PlayerColor = HexPawnDefs.White;    // プレイヤーの色
    private HexPawnDefs MatchBoxColor = HexPawnDefs.Black;  // マッチ箱の色

    /// <summary>
    /// 負けの状態リストに存在するかどうか
    /// </summary>
    /// <param name="bdmove">盤の状態と駒の移動</param>
    /// <returns>true:存在する</returns>
    public bool ExistDefeatList(HpBoardMove bdmove)
    {
        HpBoardMove mirror = bdmove.MakeMirror();

        foreach (HpBoardMove defeat in DefeatList)
        {
            if (defeat.TestEqual(bdmove))
                return true;

            if (defeat.TestEqual(mirror))
                return true;
        }

        return false;
    }

    /// <summary>
    /// マッチ箱の番
    /// </summary>
    /// <param name="bdmove">現在の盤の状態</param>
    /// <returns>true 打った false リザイン</returns>
    public bool MatchBoxTurn(HpBoardMove bdmove)
    {
        // すべてのマスの位置を取得する
        HpPoint[] allsq = HpBoard.GetAllPoints();

        // ランダムに打つよう、配列の順番を変更する
        for (int i = 0; i < 20; i++)
        {
            // 任意の2つのマスの配列内の順番を入れ替える
            int p = RandObj.Next(allsq.Length);
            int q = RandObj.Next(allsq.Length);

            if (p != q)
            {
                HpPoint t = allsq[p];
                allsq[p] = allsq[q];
                allsq[q] = t;
            }
        }

        // 有効な手を探索する
        foreach (HpPoint from in allsq)
        {
            if (bdmove.GetSquare(from) != MatchBoxColor)
                continue;   // 手番の駒が無ければ次へ

            foreach (HpPoint to in allsq)
            {
                bdmove.SetMove(from, to);

                // 有効な手であり、負けの手に含まれていなければOK!
                if (bdmove.IsValidMove() && !ExistDefeatList(bdmove))
                {
                    Console.Write("\nマッチ箱: " + from.ToString() + " を " + to.ToString() + " へ");
                    return true;
                }
            }
        }

        Console.Write("\nリザインします。");

        return false;   // リザイン
    }

    /// <summary>
    /// キーボードから入力し、駒の位置に変換する
    /// </summary>
    /// <param name="strGuide">入力ガイド文字列</param>
    /// <returns>駒の位置</returns>
    HpPoint InputSquarePoint(string strGuide)
    {
        for (; ; )
        {
            Console.Write(strGuide);    // 入力ガイドの表示

            ConsoleKeyInfo kinfo = Console.ReadKey();   // 一文字入力

            switch (kinfo.KeyChar)
            {
                case '7':
                    return new HpPoint(0, 2);
                case '8':
                    return new HpPoint(1, 2);
                case '9':
                    return new HpPoint(2, 2);
                case '4':
                    return new HpPoint(0, 1);
                case '5':
                    return new HpPoint(1, 1);
                case '6':
                    return new HpPoint(2, 1);
                case '1':
                    return new HpPoint(0, 0);
                case '2':
                    return new HpPoint(1, 0);
                case '3':
                    return new HpPoint(2, 0);
            }
        }
    }

    /// <summary>
    /// プレイヤーの番
    /// </summary>
    /// <param name="bdmove">現在の盤の状態</param>
    public void PlayerTurn(HpBoardMove bdmove)
    {
        HpPoint from, to;

        for (; ; )
        {
            from = InputSquarePoint("\n駒を選んでください(1~9):");

            if (PlayerColor == bdmove.GetSquare(from) && bdmove.IsMoveablePawn(from))
                break;
            else
                Console.Write("\n動かせる駒がありません");
        }

        for (; ; )
        {
            to = InputSquarePoint("\nどこに動かしますか?(1~9):");

            bdmove.SetMove(from, to);

            if (bdmove.IsValidMove())
                break;
            else
                Console.Write("\nそこには動かせません");
        }
    }

    /// <summary>
    /// 1ゲーム、プレイする
    /// </summary>
    public void Play()
    {
        HpBoardMove board = new HpBoardMove();  // 現在の盤の状態
        HpBoardMove lastMatchBoxMove = null;    // マッチ箱の最後の手
        HexPawnDefs winner = HexPawnDefs.None;  // 勝者
        HexPawnDefs turn = HexPawnDefs.White;   // 現在の番(白番から始める)

        // 盤面を表示
        board.Show();

        while (winner != HexPawnDefs.White && winner != HexPawnDefs.Black)
        {
            if (turn == PlayerColor)
            {
                // プレイヤーのターン

                PlayerTurn(board);
            }
            else
            {
                // マッチ箱のターン

                if (MatchBoxTurn(board))
                {
                    lastMatchBoxMove = new HpBoardMove(board);
                }
                else
                {
                    // リザインした場合
                    winner = PlayerColor;
                    break;
                }
            }

            // 駒を移動
            board.MovePawn();

            // 盤面を表示
            board.Show();

            // 次のターンへ
            if (turn == HexPawnDefs.Black)
                turn = HexPawnDefs.White;
            else
                turn = HexPawnDefs.Black;

            // 勝敗の確認
            winner = board.CheckFinish(turn);
        }

        if (winner == HexPawnDefs.White)
            Console.Write("\n白の勝ちです。\n");
        else if (winner == HexPawnDefs.Black)
            Console.Write("\n黒の勝ちです。\n");

        if (winner != MatchBoxColor && lastMatchBoxMove != null)
            DefeatList.Add(lastMatchBoxMove);
    }
};

/// <summary>
/// HexPawn
/// </summary>
class HexPawn
{
    /// <summary>
    /// Main
    /// </summary>
    /// <param name="args"></param>
    static void Main(string[] args)
    {
        ConsoleKeyInfo reply;   // ユーザー入力を受け取る変数

        Game game = new Game();

        do
        {
            game.Play();    // 1ゲーム、プレイする

            Console.Write("終了する場合は'Q'、続ける場合は他のキーを押してください");
            reply = Console.ReadKey();
        } while (reply.Key != ConsoleKey.Q);
    }
}
PAPER BOWL
NEZEN