基于对象的棋类程序结构设计

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

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 

 三、总结

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

原文地址:https://www.cnblogs.com/daxia319/p/1759454.html