zoukankan      html  css  js  c++  java
  • 基于对象的棋类程序结构设计

    标题不大好起,姑且叫这个名字。

    2010年5月,做了一个下棋的Demo,目前有五子棋和黑白棋两种。可支持两人对弈,也可人机对弈。留下了计算机走棋的接口。 

    本人是07级软件工程本科生,写过不少程序。但过去写的程序代码质量不高,存在大量复制粘贴。

    本人经过学习,现在能做一部分的优化。 

    就本程序而言,仍然是比较紧的耦合。在此将拙作展示,望同侪指正。 

     开发语言为C# 3.5。WinForm结构。

    先看看运行结果: (界面比较简陋,见笑了)

      

     下面作者将从多方面阐述本程序。

    ====================================================================================

    一、设计。

    类图如下: 

     

    1. 左边这棵树是基于对象的设计,封装了棋类的通用操作,并且作者希望通过继承达到多态性的效果(这一点会在后面提到)。其中ChessManager为抽象类,由其派生出OthelloManager,FiveChessManager(分别为黑白棋和五子棋),然后OthelloManager派生出AIOthelloManager(支持人机对战)。
    2. Common类保存全局变量,如棋子的颜色。
    3. ChessType为枚举类型,表示一个棋子的状态。包括四状态:NONE, FIRST, SECOND, BOTH。分别表示【无】【先手方(黑棋)】【后手方(白棋)】【双方(这个状态是用来表示平局的)】。有两个地方要用,一是用来表示棋盘上每个点的有无棋子,是黑是白;二是表示获胜的玩家,如无人胜,黑胜,白胜,双方胜(可理解为平局)。
    4. IJudgeable接口。用来判断哪一方获胜。由ChessManager来实现,事后发现此接口没有什么用(囧)。
    5. IAIChess接口。声明了与AI有关的方法。

    二、实现  

    从简入手。

    1.Common类,代码如下:

     1 using System;
     2 using System.Collections.Generic;
     3 using System.Linq;
     4 using System.Text;
     5 using System.Drawing;
     6 
     7 namespace MyChess
     8 {
     9     public enum ChessType
    10     {
    11         NONE,
    12         FIRST,
    13         SECOND,
    14         BOTH        // 平局,五子棋不出现,黑白棋可能出现
    15     }
    16 
    17     public class Common
    18     {
    19         /// <summary>
    20         /// 随机数生成器
    21         /// </summary>
    22         public static Random random = new Random();
    23 
    24         #region 窗口信息
    25         public static int leftBound = 100;
    26         public static int topBound = 50;
    27         #endregion
    28 
    29         #region 棋盘、棋子信息
    30         /// <summary>
    31         /// 棋盘边长
    32         /// </summary>
    33         public static int defaultBoardSize = 5;
    34 
    35         /// <summary>
    36         /// 棋子半径
    37         /// </summary>
    38         public static float diameter = 30;
    39 
    40 
    41         /// <summary>
    42         /// 网格线颜色
    43         /// </summary>
    44         public static Color boardColor = Color.Black;
    45 
    46         /// <summary>
    47         /// 玩家1棋子颜色
    48         /// </summary>
    49         public static Color oneColor = Color.Black;
    50 
    51         /// <summary>
    52         /// 玩家2棋子颜色
    53         /// </summary>
    54         public static Color twoColor = Color.White;
    55         #endregion
    56 
    57     }
    58 }
    59 

    2. 接口定义 

    1 public interface IJudgeable
    2 {
    3     ChessType JudgeWinner();
    4 }
    5 
    1 public interface IAIChess
    2 {
    3     /// <summary>
    4     /// 得到AI的落子坐标
    5      /// </summary>
    6     /// <returns>AI计算得到的落子坐标</returns>
    7     Point getAIposition();
    8 }

    3. ChessManager抽象类。为核心类,封装了Form与其的关系,以及棋类的自治功能。 

    public 方法:

    AddChess方法:在指定位置落子,首先判断是否成功落子(位置原来没有棋子),然后判定胜负(每落一子,就会作一次判定)。

    SwitchPlayer方法:切换双方。

    Init方法:初始化 

    Clear方法:清空棋盘。

    Pass方法: 弃权。

     JudgeWinner方法:判定胜负,包括黑胜,白胜,未下完,平局。

    private方法: 

    SetChess方法 :在指定位置落子。

    等等

    以上方法多数可以覆盖,供不同种类的棋配置。 

      1 using System;
      2 using System.Collections.Generic;
      3 using System.Linq;
      4 using System.Text;
      5 using System.Drawing;
      6 
      7 namespace MyChess
      8 {
      9     public delegate void WinningHandler(ChessType winner);
     10 
     11     /// <summary>
     12     /// 两人对战棋类游戏的基类
     13     /// 
     14     /// IJugeable包含了JudgeWinner方法,用以判断某方获胜,请在继承时覆盖。
     15     /// winning事件用以处理一方获胜。
     16     /// 
     17     /// SetChess方法指定落子归则,默认为落子后不对其他棋子产生影响。
     18     /// 对于五子棋,不需覆盖。对于黑白棋、围棋,请覆盖该方法。
     19     /// </summary>
     20     public abstract class ChessManager : IJudgeable
     21     {
     22         #region 变量
     23         protected ChessType[,] chessBoard;
     24         protected ChessType currentType = ChessType.NONE;
     25 
     26         protected int boardSize = 5;
     27 
     28         /// <summary>
     29         /// 当前落子的位置 X表示行,Y表示列
     30         /// </summary>
     31         protected Point currentChess = new Point(-1-1);
     32         private bool isAlive = true;
     33         #endregion
     34 
     35         #region 属性
     36         /// <summary>
     37         /// 当前棋子状态
     38         /// </summary>
     39         public ChessType CurrentType
     40         {
     41             get { return currentType; }
     42         }
     43 
     44         /// <summary>
     45         /// 棋盘大小
     46         /// </summary>
     47         public int BoardSize
     48         {
     49             get { return boardSize; }
     50         }
     51 
     52         /// <summary>
     53         /// 棋局正在进行:true;已经结束:false
     54         /// </summary>
     55         public bool IsAlive
     56         {
     57             get { return isAlive; }
     58         }
     59         #endregion
     60 
     61         #region 事件
     62         public event WinningHandler winning;
     63         private void OnWin(ChessType player)
     64         {
     65             if (winning != null)
     66             {
     67                 winning(player);
     68             }
     69         }
     70         #endregion
     71 
     72         #region 构造器
     73         public ChessManager()
     74             : this(5)
     75         {
     76         }
     77 
     78         public ChessManager(int pBoardSize)
     79         {
     80             this.boardSize = pBoardSize;
     81             this.chessBoard = new ChessType[this.boardSize, this.boardSize];
     82             this.currentType = ChessType.FIRST;
     83         }
     84         #endregion
     85 
     86         #region 公共方法
     87         /// <summary>
     88         /// 渲染
     89         /// </summary>
     90         /// <param name="graphics"></param>
     91         public abstract void Render(Graphics graphics);
     92 
     93         /// <summary>
     94         /// 在指定位置落子
     95         /// 切换选手
     96         /// 判定胜负
     97         /// </summary>
     98         /// <param name="px"></param>
     99         /// <param name="py"></param>
    100         /// <returns>true if success</returns>
    101         public bool AddChess(int px, int py)
    102         {
    103             bool ret = false;
    104             if (chessBoard[px, py] == ChessType.NONE)
    105             {
    106                 currentChess = new Point(px, py);
    107                 SetChess(px, py, this.currentType);
    108                 if (chessBoard[px, py] != ChessType.NONE)   // 下子成功则切换选手
    109                 {
    110                     ret = true;
    111                     SwitchPlayer();
    112                 }
    113                 ChessType winner = JudgeWinner();
    114                 if (winner != ChessType.NONE)
    115                 {
    116                     this.isAlive = false;
    117                     OnWin(winner);
    118                 }
    119             }
    120             return ret;
    121         }
    122 
    123         /// <summary>
    124         /// 切换选手
    125         /// </summary>
    126         protected void SwitchPlayer()
    127         {
    128             if (currentType == ChessType.FIRST)
    129                 currentType = ChessType.SECOND;
    130             else if (currentType == ChessType.SECOND)
    131                 currentType = ChessType.FIRST;
    132         }
    133 
    134         /// <summary>
    135         /// 随机分布
    136         /// 测试用
    137         /// </summary>
    138         public void Shuffle()
    139         {
    140             for (int i = 0; i < this.boardSize; i++)
    141             {
    142                 for (int j = 0; j < this.boardSize; j++)
    143                 {
    144                     int tmp = Common.random.Next(3);
    145                     switch (tmp)
    146                     {
    147                         case 0:
    148                             chessBoard[i, j] = ChessType.NONE;
    149                             break;
    150                         case 1:
    151                             chessBoard[i, j] = ChessType.FIRST;
    152                             break;
    153                         case 2:
    154                             chessBoard[i, j] = ChessType.SECOND;
    155                             break;
    156                         default:
    157                             throw new Exception("Error");
    158                             break;
    159                     }
    160                 }
    161             }
    162         }
    163 
    164         /// <summary>
    165         /// 初始化
    166         /// </summary>
    167         public virtual void Init()
    168         {
    169             Clear();
    170             this.currentType = ChessType.FIRST;
    171             this.isAlive = true;
    172         }
    173 
    174         /// <summary>
    175         /// 清空棋盘
    176         /// </summary>
    177         public void Clear()
    178         {
    179             for (int i = 0; i < this.boardSize; i++)
    180             {
    181                 for (int j = 0; j < this.boardSize; j++)
    182                 {
    183                     chessBoard[i, j] = ChessType.NONE;
    184                 }
    185             }
    186         }
    187 
    188 
    189         #endregion
    190 
    191         #region 判定胜负
    192         public abstract ChessType JudgeWinner();
    193         #endregion
    194 
    195         #region 工具方法
    196         /// <summary>
    197         /// 设计指定位置棋子
    198         /// 并处理该棋子影响到的其他棋子
    199         /// </summary>
    200         /// <param name="px">横坐标</param>
    201         /// <param name="py">纵坐标</param>
    202         /// <param name="type">棋子类别:无、玩家1、玩家2</param>
    203         protected virtual void SetChess(int px, int py, ChessType type)
    204         {
    205             if (px < 0 || px >= this.boardSize || py < 0 || py > this.boardSize)
    206             {
    207                 throw new Exception("Index out of bound");
    208             }
    209             chessBoard[px, py] = type;
    210         }
    211         #endregion
    212 
    213         /// <summary>
    214         /// 当前一步放弃下子
    215         /// 黑白棋中不允许随便切换
    216         /// </summary>
    217         /// <returns></returns>
    218         public virtual bool Pass()
    219         {
    220             this.SwitchPlayer();
    221             return true;
    222         }
    223     }
    224 }
    225 

     4. OthelloManager继承自ChessManager,复用棋类通用方法。同时在region 辅助 中有其特有的方法。 

      1 using System;
      2 using System.Collections.Generic;
      3 using System.Linq;
      4 using System.Text;
      5 using System.Drawing;
      6 
      7 namespace MyChess
      8 {
      9     /// <summary>
     10     /// 黑白棋
     11     /// </summary>
     12     public class OthelloManager : ChessManager
     13     {
     14         public OthelloManager()
     15             : base(8)
     16         {
     17             // nop
     18         }
     19 
     20         public override void Init()
     21         {
     22             base.Init();
     23             int half = this.boardSize / 2;
     24             chessBoard[half - 1, half - 1= chessBoard[half, half] = ChessType.FIRST;
     25             chessBoard[half - 1, half] = chessBoard[half, half - 1= ChessType.SECOND;
     26         }
     27 
     28         protected override void SetChess(int px, int py, ChessType type)
     29         {
     30             #region 搜索
     31             // 向八个方向搜索
     32             ChessType curType = type;
     33             // 八个方向的搜索终点的长度
     34             // 记录该方向数几个棋子,将其反色
     35             int upleft = 0, up = 0, upright = 0, left = 0, right = 0, downleft = 0, down = 0, downright = 0;
     36             #region 左上
     37             {
     38                 ChessType limitType = ChessType.NONE;  // 该方向上的最靠边的棋子
     39                 for (int i = currentChess.X - 1, j = currentChess.Y - 1; i >= 0 && j >= 0; i--, j--)
     40                 {
     41                     if (chessBoard[i, j] == ChessType.NONE)
     42                     {
     43                         upleft = 0;
     44                         break;
     45                     }
     46                     else if (chessBoard[i, j] == curType)
     47                     {
     48                         limitType = currentType;
     49                         break;
     50                     }
     51                     else
     52                     {
     53                         upleft++;
     54                     }
     55                     limitType = chessBoard[i, j];
     56                 }
     57                 if (limitType != curType)       // 搜索至边界无同色棋子,不反色
     58                 {
     59                     upleft = 0;
     60                 }
     61             }
     62             {
     63                 int tmpCount = upleft;
     64                 for (int i = currentChess.X - 1, j = currentChess.Y - 1; tmpCount > 0 && i >= 0 && j >= 0; i--, j--, tmpCount--)
     65                 {
     66                     chessBoard[i, j] = currentType;
     67                 }
     68             }
     69             #endregion
     70             #region 上
     71             {
     72                 ChessType limitType = ChessType.NONE;  // 该方向上的最靠边的棋子
     73                 int j = currentChess.Y;
     74                 for (int i = currentChess.X - 1; i >= 0; i--)
     75                 {
     76                     if (chessBoard[i, j] == ChessType.NONE)
     77                     {
     78                         up = 0;
     79                         break;
     80                     }
     81                     else if (chessBoard[i, j] == curType)
     82                     {
     83                         limitType = currentType;
     84                         break;
     85                     }
     86                     else
     87                     {
     88                         up++;
     89                     }
     90                     limitType = chessBoard[i, j];
     91                 }
     92                 if (limitType != curType)       // 搜索至边界无同色棋子,不反色
     93                 {
     94                     up = 0;
     95                 }
     96             }
     97             {
     98                 int tmpCount = up;
     99                 int j = currentChess.Y;
    100                 for (int i = currentChess.X - 1; tmpCount > 0 && i >= 0; i--, tmpCount--)
    101                 {
    102                     chessBoard[i, j] = currentType;
    103                 }
    104             }
    105             #endregion
    106             #region 右上
    107             {
    108                 ChessType limitType = ChessType.NONE;  // 该方向上的最靠边的棋子
    109                 for (int i = currentChess.X - 1, j = currentChess.Y + 1; i >= 0 && j < this.boardSize; i--, j++)
    110                 {
    111                     if (chessBoard[i, j] == ChessType.NONE)
    112                     {
    113                         upright = 0;
    114                         break;
    115                     }
    116                     else if (chessBoard[i, j] == curType)
    117                     {
    118                         limitType = currentType;
    119                         break;
    120                     }
    121                     else
    122                     {
    123                         upright++;
    124                     }
    125                     limitType = chessBoard[i, j];
    126                 }
    127                 if (limitType != curType)       // 搜索至边界无同色棋子,不反色
    128                 {
    129                     upright = 0;
    130                 }
    131             }
    132             {
    133                 int tmpCount = upright;
    134                 for (int i = currentChess.X - 1, j = currentChess.Y + 1; tmpCount > 0 && i >= 0 && j < this.boardSize; i--, j++, tmpCount--)
    135                 {
    136                     chessBoard[i, j] = currentType;
    137                 }
    138             }
    139             #endregion
    140             #region 左
    141             {
    142                 ChessType limitType = ChessType.NONE;  // 该方向上的最靠边的棋子
    143                 int i = currentChess.X;
    144                 for (int j = currentChess.Y - 1; j >= 0; j--)
    145                 {
    146                     if (chessBoard[i, j] == ChessType.NONE)
    147                     {
    148                         left = 0;
    149                         break;
    150                     }
    151                     else if (chessBoard[i, j] == curType)
    152                     {
    153                         limitType = currentType;
    154                         break;
    155                     }
    156                     else
    157                     {
    158                         left++;
    159                     }
    160                     limitType = chessBoard[i, j];
    161                 }
    162                 if (limitType != curType)       // 搜索至边界无同色棋子,不反色
    163                 {
    164                     left = 0;
    165                 }
    166             }
    167             {
    168                 int tmpCount = left;
    169                 int i = currentChess.X;
    170                 for (int j = currentChess.Y - 1; tmpCount > 0 && j >= 0; j--, tmpCount--)
    171                 {
    172                     chessBoard[i, j] = currentType;
    173                 }
    174             }
    175             #endregion
    176             #region 右
    177             {
    178                 ChessType limitType = ChessType.NONE;  // 该方向上的最靠边的棋子
    179                 int i = currentChess.X;
    180                 for (int j = currentChess.Y + 1; j < this.boardSize; j++)
    181                 {
    182                     if (chessBoard[i, j] == ChessType.NONE)
    183                     {
    184                         right = 0;
    185                         break;
    186                     }
    187                     else if (chessBoard[i, j] == curType)
    188                     {
    189                         limitType = currentType;
    190                         break;
    191                     }
    192                     else
    193                     {
    194                         right++;
    195                     }
    196                     limitType = chessBoard[i, j];
    197                 }
    198                 if (limitType != curType)       // 搜索至边界无同色棋子,不反色
    199                 {
    200                     right = 0;
    201                 }
    202             }
    203             {
    204                 int tmpCount = right;
    205                 int i = currentChess.X;
    206                 for (int j = currentChess.Y + 1; tmpCount > 0 && j < this.boardSize; j++, tmpCount--)
    207                 {
    208                     chessBoard[i, j] = currentType;
    209                 }
    210             }
    211             #endregion
    212             #region 左下
    213             {
    214                 ChessType limitType = ChessType.NONE;  // 该方向上的最靠边的棋子
    215                 for (int i = currentChess.X + 1, j = currentChess.Y - 1; i < this.boardSize && j >= 0; i++, j--)
    216                 {
    217                     if (chessBoard[i, j] == ChessType.NONE)
    218                     {
    219                         downleft = 0;
    220                         break;
    221                     }
    222                     else if (chessBoard[i, j] == curType)
    223                     {
    224                         limitType = currentType;
    225                         break;
    226                     }
    227                     else
    228                     {
    229                         downleft++;
    230                     }
    231                     limitType = chessBoard[i, j];
    232                 }
    233                 if (limitType != curType)       // 搜索至边界无同色棋子,不反色
    234                 {
    235                     downleft = 0;
    236                 }
    237             }
    238             {
    239                 int tmpCount = downleft;
    240                 for (int i = currentChess.X + 1, j = currentChess.Y - 1; tmpCount > 0 && i < this.boardSize && j >= 0; i++, j--, tmpCount--)
    241                 {
    242                     chessBoard[i, j] = currentType;
    243                 }
    244             }
    245             #endregion
    246             #region 下
    247             {
    248                 ChessType limitType = ChessType.NONE;  // 该方向上的最靠边的棋子
    249                 int j = currentChess.Y;
    250                 for (int i = currentChess.X + 1; i < this.boardSize; i++)
    251                 {
    252                     if (chessBoard[i, j] == ChessType.NONE)
    253                     {
    254                         down = 0;
    255                         break;
    256                     }
    257                     else if (chessBoard[i, j] == curType)
    258                     {
    259                         limitType = currentType;
    260                         break;
    261                     }
    262                     else
    263                     {
    264                         down++;
    265                     }
    266                     limitType = chessBoard[i, j];
    267                 }
    268                 if (limitType != curType)       // 搜索至边界无同色棋子,不反色
    269                 {
    270                     down = 0;
    271                 }
    272             }
    273             {
    274                 int tmpCount = down;
    275                 int j = currentChess.Y;
    276                 for (int i = currentChess.X + 1; tmpCount > 0 && i < this.boardSize; i++, tmpCount--)
    277                 {
    278                     chessBoard[i, j] = currentType;
    279                 }
    280             }
    281             #endregion
    282             #region 右下
    283             {
    284                 ChessType limitType = ChessType.NONE;  // 该方向上的最靠边的棋子
    285                 for (int i = currentChess.X + 1, j = currentChess.Y + 1; i < this.boardSize && j < this.boardSize; i++, j++)
    286                 {
    287                     if (chessBoard[i, j] == ChessType.NONE)
    288                     {
    289                         downright = 0;
    290                         break;
    291                     }
    292                     else if (chessBoard[i, j] == curType)
    293                     {
    294                         limitType = currentType;
    295                         break;
    296                     }
    297                     else
    298                     {
    299                         downright++;
    300                     }
    301                     limitType = chessBoard[i, j];
    302                 }
    303                 if (limitType != curType)       // 搜索至边界无同色棋子,不反色
    304                 {
    305                     downright = 0;
    306                 }
    307             }
    308             {
    309                 int tmpCount = downright;
    310                 for (int i = currentChess.X + 1, j = currentChess.Y + 1; tmpCount > 0 && i < this.boardSize && j < this.boardSize; i++, j++, tmpCount--)
    311                 {
    312                     chessBoard[i, j] = currentType;
    313                 }
    314             }
    315             #endregion
    316             #endregion
    317 
    318             if (upleft + up + upright + left + right + downleft + down + downright <= 0)
    319             {
    320                 // 不能在此下子
    321                 chessBoard[currentChess.X, currentChess.Y] = ChessType.NONE;
    322             }
    323             else
    324             {
    325                 base.SetChess(px, py, type);
    326             }
    327         }
    328 
    329         public override bool Pass()
    330         {
    331             int firstCount;
    332             int secondCount;
    333             int firstAvailablePosLeft;   // 玩家一 剩余可以下子的位置
    334             int secondAvailablePosLeft;   // 玩家二 剩余可以下子的位置
    335             getCurrentInfo(out firstCount, out secondCount, out firstAvailablePosLeft, out secondAvailablePosLeft);
    336 
    337             bool canPass = true;            // 是否允许弃权     当前棋局还有可以下的位置,禁止弃权
    338             if (currentType == ChessType.FIRST)
    339             {
    340                 if (firstAvailablePosLeft > 0)
    341                     canPass = false;
    342             }
    343             else if (currentType == ChessType.SECOND)
    344             {
    345                 if (secondAvailablePosLeft > 0)
    346                     canPass = false;
    347             }
    348             else
    349             {
    350                 throw new Exception("当前状态不是合法状态(FIRST SECOND)");
    351             }
    352             if (canPass)
    353             {
    354                 SwitchPlayer();
    355             }
    356             return canPass;
    357         }
    358 
    359         public override void Render(System.Drawing.Graphics graphics)
    360         {
    361             Pen pen = new Pen(Common.boardColor);
    362             Brush oneBrush = new SolidBrush(Common.oneColor);
    363             Brush twoBrush = new SolidBrush(Common.twoColor);
    364 
    365             #region 画棋盘
    366             for (int i = 0; i < this.boardSize + 1; i++)
    367             {
    368                 float t1 = i * Common.diameter;
    369                 float s1 = 0;
    370                 float s2 = s1 + this.boardSize * Common.diameter;
    371 
    372                 graphics.DrawLine(pen, (Common.leftBound + t1), (Common.topBound + s1),
    373                      (Common.leftBound + t1), (Common.topBound + s2));
    374                 graphics.DrawLine(pen, (Common.leftBound + s1), (Common.topBound + t1),
    375                      (Common.leftBound + s2), (Common.topBound + t1));
    376             }
    377             #endregion
    378 
    379             #region 画棋子
    380             for (int i = 0; i < this.boardSize; i++)
    381             {
    382                 for (int j = 0; j < this.boardSize; j++)
    383                 {
    384                     float x = Common.leftBound + j * Common.diameter;
    385                     float y = Common.topBound + i * Common.diameter;
    386                     switch (chessBoard[i, j])
    387                     {
    388                         case ChessType.FIRST:
    389                             graphics.FillEllipse(oneBrush, x, y, Common.diameter, Common.diameter);
    390                             break;
    391                         case ChessType.SECOND:
    392                             graphics.FillEllipse(twoBrush, x, y, Common.diameter, Common.diameter);
    393                             break;
    394                         default:
    395                             break;
    396                     }
    397                 }
    398             }
    399             #endregion
    400         }
    401 
    402         public override ChessType JudgeWinner()
    403         {
    404             int firstCount;
    405             int secondCount;
    406             int firstAvailablePosLeft;      // 玩家一 剩余可以下子的位置
    407             int secondAvailablePosLeft;     // 玩家二 剩余可以下子的位置
    408 
    409             ChessType winner = ChessType.NONE;
    410 
    411             getCurrentInfo(out firstCount, out secondCount, out firstAvailablePosLeft, out secondAvailablePosLeft);
    412 
    413             if (firstAvailablePosLeft + secondAvailablePosLeft <= 0)
    414             {
    415                 if (firstCount > secondCount)
    416                     winner = ChessType.FIRST;
    417                 else if (firstCount < secondCount)
    418                     winner = ChessType.SECOND;
    419                 else        //平局
    420                     winner = ChessType.BOTH;
    421             }
    422             return winner;
    423         }
    424 
    425         #region 辅助
    426         /// <summary>
    427         /// 计算八个方向上影响到的对方棋子数
    428         /// </summary>
    429         /// <param name="px"></param>
    430         /// <param name="py"></param>
    431         /// <param name="cType">以cType的观点</param>
    432         /// <returns></returns>
    433         protected int CountAffectedChess(int px, int py, ChessType cType)
    434         {
    435             if (!(cType == ChessType.FIRST || cType == ChessType.SECOND))
    436                 throw new Exception("cType must be FIRST or SECOND");
    437             #region 搜索
    438             // 向八个方向搜索
    439             ChessType tmpType = cType;
    440             Point chess = new Point(px, py);
    441             // 八个方向的搜索终点的长度
    442             // 记录该方向数几个棋子
    443             int upleft = 0, up = 0, upright = 0, left = 0, right = 0, downleft = 0, down = 0, downright = 0;
    444             #region 左上
    445             {
    446                 ChessType limitType = ChessType.NONE;  // 该方向上的最靠边的棋子
    447                 for (int i = chess.X - 1, j = chess.Y - 1; i >= 0 && j >= 0; i--, j--)
    448                 {
    449                     if (chessBoard[i, j] == ChessType.NONE)
    450                     {
    451                         upleft = 0;
    452                         break;
    453                     }
    454                     else if (chessBoard[i, j] == tmpType)
    455                     {
    456                         limitType = tmpType;
    457                         break;
    458                     }
    459                     else
    460                     {
    461                         upleft++;
    462                     }
    463                     limitType = chessBoard[i, j];
    464                 }
    465                 if (limitType != tmpType)       // 搜索至边界无同色棋子,不反色
    466                 {
    467                     upleft = 0;
    468                 }
    469             }
    470             #endregion
    471             #region 上
    472             {
    473                 ChessType limitType = ChessType.NONE;  // 该方向上的最靠边的棋子
    474                 int j = chess.Y;
    475                 for (int i = chess.X - 1; i >= 0; i--)
    476                 {
    477                     if (chessBoard[i, j] == ChessType.NONE)
    478                     {
    479                         up = 0;
    480                         break;
    481                     }
    482                     else if (chessBoard[i, j] == tmpType)
    483                     {
    484                         limitType = tmpType;
    485                         break;
    486                     }
    487                     else
    488                     {
    489                         up++;
    490                     }
    491                     limitType = chessBoard[i, j];
    492                 }
    493                 if (limitType != tmpType)       // 搜索至边界无同色棋子,不反色
    494                 {
    495                     up = 0;
    496                 }
    497             }
    498             #endregion
    499             #region 右上
    500             {
    501                 ChessType limitType = ChessType.NONE;  // 该方向上的最靠边的棋子
    502                 for (int i = chess.X - 1, j = chess.Y + 1; i >= 0 && j < this.boardSize; i--, j++)
    503                 {
    504                     if (chessBoard[i, j] == ChessType.NONE)
    505                     {
    506                         upright = 0;
    507                         break;
    508                     }
    509                     else if (chessBoard[i, j] == tmpType)
    510                     {
    511                         limitType = tmpType;
    512                         break;
    513                     }
    514                     else
    515                     {
    516                         upright++;
    517                     }
    518                     limitType = chessBoard[i, j];
    519                 }
    520                 if (limitType != tmpType)       // 搜索至边界无同色棋子,不反色
    521                 {
    522                     upright = 0;
    523                 }
    524             }
    525             #endregion
    526             #region 左
    527             {
    528                 ChessType limitType = ChessType.NONE;  // 该方向上的最靠边的棋子
    529                 int i = chess.X;
    530                 for (int j = chess.Y - 1; j >= 0; j--)
    531                 {
    532                     if (chessBoard[i, j] == ChessType.NONE)
    533                     {
    534                         left = 0;
    535                         break;
    536                     }
    537                     else if (chessBoard[i, j] == tmpType)
    538                     {
    539                         limitType = tmpType;
    540                         break;
    541                     }
    542                     else
    543                     {
    544                         left++;
    545                     }
    546                     limitType = chessBoard[i, j];
    547                 }
    548                 if (limitType != tmpType)       // 搜索至边界无同色棋子,不反色
    549                 {
    550                     left = 0;
    551                 }
    552             }
    553             #endregion
    554             #region 右
    555             {
    556                 ChessType limitType = ChessType.NONE;  // 该方向上的最靠边的棋子
    557                 int i = chess.X;
    558                 for (int j = chess.Y + 1; j < this.boardSize; j++)
    559                 {
    560                     if (chessBoard[i, j] == ChessType.NONE)
    561                     {
    562                         right = 0;
    563                         break;
    564                     }
    565                     else if (chessBoard[i, j] == tmpType)
    566                     {
    567                         limitType = tmpType;
    568                         break;
    569                     }
    570                     else
    571                     {
    572                         right++;
    573                     }
    574                     limitType = chessBoard[i, j];
    575                 }
    576                 if (limitType != tmpType)       // 搜索至边界无同色棋子,不反色
    577                 {
    578                     right = 0;
    579                 }
    580             }
    581             #endregion
    582             #region 左下
    583             {
    584                 ChessType limitType = ChessType.NONE;  // 该方向上的最靠边的棋子
    585                 for (int i = chess.X + 1, j = chess.Y - 1; i < this.boardSize && j >= 0; i++, j--)
    586                 {
    587                     if (chessBoard[i, j] == ChessType.NONE)
    588                     {
    589                         downleft = 0;
    590                         break;
    591                     }
    592                     else if (chessBoard[i, j] == tmpType)
    593                     {
    594                         limitType = tmpType;
    595                         break;
    596                     }
    597                     else
    598                     {
    599                         downleft++;
    600                     }
    601                     limitType = chessBoard[i, j];
    602                 }
    603                 if (limitType != tmpType)       // 搜索至边界无同色棋子,不反色
    604                 {
    605                     downleft = 0;
    606                 }
    607             }
    608             #endregion
    609             #region 下
    610             {
    611                 ChessType limitType = ChessType.NONE;  // 该方向上的最靠边的棋子
    612                 int j = chess.Y;
    613                 for (int i = chess.X + 1; i < this.boardSize; i++)
    614                 {
    615                     if (chessBoard[i, j] == ChessType.NONE)
    616                     {
    617                         down = 0;
    618                         break;
    619                     }
    620                     else if (chessBoard[i, j] == tmpType)
    621                     {
    622                         limitType = tmpType;
    623                         break;
    624                     }
    625                     else
    626                     {
    627                         down++;
    628                     }
    629                     limitType = chessBoard[i, j];
    630                 }
    631                 if (limitType != tmpType)       // 搜索至边界无同色棋子,不反色
    632                 {
    633                     down = 0;
    634                 }
    635             }
    636             #endregion
    637             #region 右下
    638             {
    639                 ChessType limitType = ChessType.NONE;  // 该方向上的最靠边的棋子
    640                 for (int i = chess.X + 1, j = chess.Y + 1; i < this.boardSize && j < this.boardSize; i++, j++)
    641                 {
    642                     if (chessBoard[i, j] == ChessType.NONE)
    643                     {
    644                         downright = 0;
    645                         break;
    646                     }
    647                     else if (chessBoard[i, j] == tmpType)
    648                     {
    649                         limitType = tmpType;
    650                         break;
    651                     }
    652                     else
    653                     {
    654                         downright++;
    655                     }
    656                     limitType = chessBoard[i, j];
    657                 }
    658                 if (limitType != tmpType)       // 搜索至边界无同色棋子,不反色
    659                 {
    660                     downright = 0;
    661                 }
    662             }
    663             #endregion
    664             #endregion
    665             int totalCount = upleft + up + upright + left + right + downleft + down + downright;
    666             return totalCount;
    667         }
    668 
    669         /// <summary>
    670         /// 得到当前棋局的信息
    671         /// </summary>
    672         /// <param name="firstCount">玩家一的棋子数</param>
    673         /// <param name="secondCount">玩家二的棋子数</param>
    674         /// <param name="firstAvailablePosLeft">玩家一能下的位置的个数</param>
    675         /// <param name="secondAvailablePosLeft">玩家二能下的位置的个数</param>
    676         private void getCurrentInfo(out int firstCount, out int secondCount, out int firstAvailablePosLeft, out int secondAvailablePosLeft)
    677         {
    678             firstCount = 0;
    679             secondCount = 0;
    680             firstAvailablePosLeft = 0;
    681             secondAvailablePosLeft = 0;
    682             for (int i = 0; i < this.boardSize; i++)
    683             {
    684                 for (int j = 0; j < this.boardSize; j++)
    685                 {
    686                     if (chessBoard[i, j] == ChessType.FIRST)
    687                     {
    688                         firstCount++;
    689                     }
    690                     else if (chessBoard[i, j] == ChessType.SECOND)
    691                     {
    692                         secondCount++;
    693                     }
    694                     else if (chessBoard[i, j] == ChessType.NONE)        // 判断当前位置双方可否下子
    695                     {
    696                         int firstAffected = CountAffectedChess(i, j, ChessType.FIRST);
    697                         int secondAffected = CountAffectedChess(i, j, ChessType.SECOND);
    698                         if (firstAffected > 0)
    699                         {
    700                             ++firstAvailablePosLeft;
    701                         }
    702                         if (secondAffected > 0)
    703                         {
    704                             ++secondAvailablePosLeft;
    705                         }
    706                     }
    707                 }
    708             }
    709         }
    710         #endregion
    711     }
    712 }
    713 

     5. AIOthelloManager继承自OthelloManager,复用了所有黑白棋的规则。同时实现IAIChess接口,加入了AI功能。

      1 using System;

     2 using System.Collections.Generic;
     3 using System.Linq;
     4 using System.Text;
     5 using System.Drawing;
     6 
     7 namespace MyChess
     8 {
     9     public class AIOthelloManager : OthelloManager, IAIChess
    10     {
    11         #region IAIChess Members
    12 
    13         /// <summary>
    14         /// 贪心法  结合抢边角算法
    15         /// 不是最优算法
    16         /// </summary>
    17         /// <returns></returns>
    18         public Point getAIposition()
    19         {
    20             Point ret = new Point(-1-1);
    21 
    22             // TODO 改为胜者树更好
    23             float maxAffected = 0;
    24             for (int i = 0; i < this.boardSize; i++)
    25             {
    26                 for (int j = 0; j < this.boardSize; j++)
    27                 {
    28                     if (chessBoard[i, j] != ChessType.NONE)         // 无法落子
    29                         continue;
    30                     float curAffected = CountAffectedChess(i, j, this.currentType);
    31                     if (curAffected <= 0)
    32                         continue;
    33 
    34                     #region 计算加权
    35                     float factor = 1.0f;
    36                     if (i == 0 || i == this.boardSize - 1)        // 2个边
    37                     {
    38                         if (j == i)                                 // 4个角
    39                             factor = 40.0f;
    40                         else if (j == 1 || j == this.boardSize - 2)
    41                             factor = 0.01f;
    42                         else
    43                             factor = 20.0f;                            // 2个边(非角)
    44                     }
    45                     else if (j == 0 || j == this.boardSize - 1)   // 另外2个边(非角)
    46                     {
    47                         factor = 20.0f;
    48                     }
    49                     else if ((i == j || i + j == this.boardSize - 1&& (i == 2 || i == this.boardSize - 2))    // 1~8*1~8 (2, 2)(7, 7)(2, 7)(7, 2)点位置不佳
    50                     {
    51                         factor = 0.01f;
    52                     }
    53 
    54                     #endregion
    55                     curAffected = curAffected * factor;
    56 
    57                     if (curAffected > maxAffected)
    58                     {
    59                         ret.X = i; ret.Y = j;
    60                         maxAffected = curAffected;
    61                     }
    62                 }
    63             }
    64             return ret;
    65             // throw new NotImplementedException();
    66         }
    67 
    68         #endregion
    69     }
    70 }
    71 

     6. 客户端调用方式:理论上ChessManager可以被任意种类客户端调用,如Web或WPF。作者选用Form作为客户端,Form类中包含一个ChessManager。

     如下代码即体现了解耦:

    1 if (chessManager != null && chessManager.IsAlive)                      

    2{
    3     if (chessManager is IAIChess)
    4     {
    5          processAI();
    6          this.Invalidate();
    7     }
    8}

    通过判断chessManager is IAIChess,将AI类与非AI类区分开来,至于其内部处理流程均在各自类里面处理,如此程序的可扩展性就体现出来了。

    完整代码如下:

      1 using System;
      2 using System.Collections.Generic;
      3 using System.ComponentModel;
      4 using System.Data;
      5 using System.Drawing;
      6 using System.Linq;
      7 using System.Text;
      8 using System.Windows.Forms;
      9 
     10 namespace MyChess
     11 {
     12     public partial class Form1 : Form
     13     {
     14         private Timer aiTimer = new Timer();
     15 
     16         public Form1()
     17         {
     18             InitializeComponent();
     19 
     20             aiTimer.Interval = 500;
     21             aiTimer.Tick += new EventHandler(aiTimer_Tick);
     22 
     23             //// 五子棋
     24             //chessManager = new FiveChessManager();
     25             //// 黑白棋
     26             ////chessManager = new OthelloManager();
     27             //chessManager.winning += new WinningHandler(chessManager_winning);
     28             //chessManager.Init();
     29         }
     30 
     31         void aiTimer_Tick(object sender, EventArgs e)
     32         {
     33             #region AI
     34             if (chessManager != null && chessManager.IsAlive)
     35             {
     36                 if (chessManager is IAIChess)
     37                 {
     38                     processAI();
     39                     this.Invalidate();
     40                 }
     41             }
     42             #endregion
     43             aiTimer.Stop();
     44         }
     45 
     46         void chessManager_winning(ChessType winner)
     47         {
     48             // 刷新以显示最后一个棋子
     49             this.Invalidate();
     50 
     51             string tipMsg = string.Empty;
     52             if (winner == ChessType.NONE)
     53                 return;
     54             else if (winner == ChessType.FIRST)
     55                 tipMsg = "First win!";
     56             else if (winner == ChessType.SECOND)
     57                 tipMsg = "Second win!";
     58             else if (winner == ChessType.BOTH)
     59                 tipMsg = "Draw game!";
     60             MessageBox.Show(tipMsg, "Tip");
     61         }
     62 
     63         ChessManager chessManager;
     64 
     65         private void Form1_Paint(object sender, PaintEventArgs e)
     66         {
     67             if (chessManager == null)
     68                 return;
     69             chessManager.Render(e.Graphics);
     70         }
     71 
     72         private void Form1_MouseClick(object sender, MouseEventArgs e)
     73         {
     74             Point tmpLocation = e.Location;
     75             if (aiTimer.Enabled)    // AI正进下棋
     76                 return;
     77             if (chessManager == null || !chessManager.IsAlive)
     78                 return;
     79             #region 坐标映射
     80             {
     81                 if (tmpLocation.X < Common.leftBound
     82                     || tmpLocation.X > Common.leftBound + Common.diameter * (chessManager.BoardSize + 0.5)
     83                     || tmpLocation.Y < Common.topBound
     84                     || tmpLocation.Y > Common.topBound + Common.diameter * (chessManager.BoardSize + 0.5)
     85                     )
     86                 {
     87                     return;
     88                 }
     89 
     90                 int tx, ty;
     91                 tx = (int)Math.Round((tmpLocation.Y - Common.topBound) / Common.diameter - 0.5);
     92                 ty = (int)Math.Round((tmpLocation.X - Common.leftBound) / Common.diameter - 0.5);
     93 
     94                 bool isSuccess; // 下子是否成功
     95                 isSuccess = chessManager.AddChess(tx, ty);
     96 
     97                 #region AI
     98                 if (isSuccess && chessManager is IAIChess)
     99                 {
    100                     aiTimer.Start();
    101                 }
    102                 #endregion
    103             }
    104             #endregion
    105 
    106             this.Invalidate();
    107         }
    108 
    109         private void btnShuffle_Click(object sender, EventArgs e)
    110         {
    111             chessManager.Shuffle();
    112             this.Invalidate();
    113         }
    114 
    115         private void btnClear_Click(object sender, EventArgs e)
    116         {
    117             if (chessManager == null)
    118                 return;
    119             chessManager.Init();
    120             this.Invalidate();
    121         }
    122 
    123         private void btnPass_Click(object sender, EventArgs e)
    124         {
    125             if (chessManager == null || !chessManager.IsAlive)
    126                 return;
    127             if (!chessManager.Pass())
    128             {
    129                 MessageBox.Show("当前形势下不能弃权");
    130                 return;
    131             }
    132             if (chessManager is IAIChess)
    133                 aiTimer.Start();
    134         }
    135 
    136         /// <summary>
    137         /// 处理AI
    138         /// </summary>
    139         private void processAI()
    140         {
    141             Point aiChoice = ((IAIChess)chessManager).getAIposition();
    142             if (aiChoice.X < 0 || aiChoice.Y < 0)     // Computer Pass
    143             {
    144                 bool canPass = chessManager.Pass();
    145                 if (canPass)
    146                 {
    147                     aiTimer.Stop();
    148                     MessageBox.Show("AI pass");
    149                 }
    150                 else
    151                     throw new Exception("AI算法失效");
    152             }
    153             else
    154             {
    155                 chessManager.AddChess(aiChoice.X, aiChoice.Y);
    156                 aiTimer.Stop();
    157             }
    158         }
    159 
    160         private void btnFive_Click(object sender, EventArgs e)
    161         {
    162             this.BackColor = Color.Orange;
    163             // 五子棋
    164             chessManager = new FiveChessManager();
    165             chessManager.winning += new WinningHandler(chessManager_winning);
    166             chessManager.Init();
    167             this.Invalidate();
    168         }
    169 
    170         private void btnOthello_Click(object sender, EventArgs e)
    171         {
    172             this.BackColor = Color.Green;
    173 
    174             // 黑白棋
    175             chessManager = new OthelloManager();
    176             chessManager.winning += new WinningHandler(chessManager_winning);
    177             chessManager.Init();
    178             this.Invalidate();
    179         }
    180 
    181         private void btnFiveAI_Click(object sender, EventArgs e)
    182         {
    183             this.BackColor = Color.Orange;
    184 
    185             chessManager = new AIFiveChessManager();
    186             chessManager.winning += new WinningHandler(chessManager_winning);
    187             chessManager.Init();
    188             this.Invalidate();
    189         }
    190 
    191         private void btnOthelloAI_Click(object sender, EventArgs e)
    192         {
    193             this.BackColor = Color.Green;
    194 
    195             chessManager = new AIOthelloManager();
    196             chessManager.winning += new WinningHandler(chessManager_winning);
    197             chessManager.Init();
    198             this.Invalidate();
    199         }
    200     }
    201 }
    202 

     三、总结

    作者对本程序的思想都在类图中。遵循了具体依赖于抽象、高层依赖于抽象的原则。因作者水平有限,设计、编程存在诸多不足之处,望网友多多拍砖。

  • 相关阅读:
    oracle笔记
    随笔
    EclipsePDT PHP的开发环境配置
    winXP的系统如何避免他人在不需要密码的情况进入安全模式
    WINDOWS图片和传真查看器找不到
    Oracle常用语句大全
    jdbc连接oracle
    jdk与jre的区别
    mysql手工注入.md
    sql手工注入.md
  • 原文地址:https://www.cnblogs.com/daxia319/p/1759454.html
Copyright © 2011-2022 走看看