1) 把程序编译通过, 跑起来。

    读懂程序,在你觉得比较难懂的地方加上一些注释,这样大家就能比较容易地了解这些程序在干什么。

     把正确的 playPrev(GoMove) 的方法给实现了。

注释见Github上的代码,正确的实现我写在了下面:

 1 public void playPrev(GoMove gm)
 2         {
 3             Point P=gm.Point;
 4             
 5             Grid[P.X, P.Y].removeStone();
 6             m_gmLastMove = gameTree.peekPrev();
 7             if (m_gmLastMove != null)
 8             {
 9                 Grid[m_gmLastMove.Point.X, m_gmLastMove.Point.Y].setUpdated();
10             }
11             if (gm.DeadGroup != null)
12             {
13                 System.Collections.IEnumerator myEnumerator =gm.DeadGroup.GetEnumerator();
14                 while (myEnumerator.MoveNext())
15                 {
16                     Point p;
17                     p = (Point)myEnumerator.Current;
18                     Grid[p.X, p.Y].setStone(m_colorToPlay);
19                 }
20             }
21             m_colorToPlay = nextTurn(m_colorToPlay);
22             optRepaint();
23             textBox1.Clear();
24             textBox1.AppendText(gm.Comment);
25             return;
26         }

2)根据你选择的教材 (三本之一或更多),点评一下这个程序设计方面的不足,例如:

     编码风格,

     程序架构,有哪些不符合良好的设计,这个程序的设计模式 (MVC等) 是高端大气国际化的么? 等等。 

编码风格方面:这个程序把所有的方法都写在了GoBoard类里面,没有写成几个高内聚性的类,因此读起来比较吃力。程序中也没有把控制器和视图分开,所有的控制器都写在了GoBoard这个Winform里面,不是标准的MVC设计模式。

3)程序的注释

注释详见Github,注释时主要有两方面不明白,因此注释就没有写。

1)label和mark分别是什么意思,这一点没有在程序中看明白。

2)从文件中读取棋谱后,一些文件处理操作的函数没有看明白。

4)功能上小问题的改进:

我改的地方是对于向后回退,即悔棋时,如果只在playprev中操作,而不更改其他地方的话,会发现对于棋谱中,这种已经既定后续操作的情况下可以适用,但是如果是用户自己去下棋的情况时,就会发现回退不能得到意想的效果。主要原因出在这里:

1 public void addAMove(GoMove gm) 
2         {
3             gm.Seq = m_total ++;
4             m_moves.Insert(m_seq,gm);
5             m_seq++;
6             //m_moves.Add(gm);
7             
8         }

被注释掉的是原来的代码。原来的代码的弊端在于它对于用户回退掉的棋子并没有在m_moves这个动态数组中delete掉,导致用户先回退,在添加,再回退,这种场景时就会出现选择棋子上的错误。因此我采用的解决方案是将m_moves.Add(gm);改为m_moves.Insert(m_seq,gm);,这样就可以覆盖掉先前的棋局中的棋子,而达到理想的回退效果。

5)我还想做一个功能上的改进,但是由于时间上的原因,就没有具体实现,下面把我的想法大致说明一下:

save功能的具体实现:

save功能我的设想就是把用户手动下的棋局保存为系统要求的sgf文件。

实现的方法是遍历gametree,因为gametree里面存储了所有的下棋步骤。再利用foreach从第一个遍历到最后一个,然后获取它的颜色和横纵坐标存储为类似B[A,B]的形式。可以先保存到txt文件,最后再将后缀名改为.sgf即可。这样就可以得到一个棋谱了。

 6)其他改进及附加题

其他改进的话就是算气的那一部分可以改成压栈是非递归。

附加题2:改成联网的对战游戏。

这一问比较容易实现。

那么每次传递的就是对方的棋子位置和颜色即可。就是将每次换颜色的这部分改掉,改成接受对方传过来的参数,在棋盘上绘制棋子即可。

附加题1:AI接收的是当前棋盘的格局,然后返回的是下一个要下的棋子的点。

那么程序的架构就可以改为每当用户点击完成后,mouseuphandler方法执行完该用户下的棋子的绘制操作后,就将棋局的格局和要下的颜色传给AI,然后AI返回一个下棋的点的位置,在调用playnext函数将AI返回的棋子绘制在棋局上即可。伪代码如下所示:增加的语句为:

GoMove gmNextMove = AI(gameTree,m_colorToPlay);
playNext(gmNextMove);
gameTree.addMove(gmThisMove);                                                             

 1 private void MouseUpHandler(Object sender,MouseEventArgs e)
 2         {
 3             Point p;
 4             GoMove    gmThisMove;
 5 
 6             p = PointToGrid(e.X,e.Y);
 7             if (!onBoard(p.X, p.Y) || !closeEnough(p,e.X, e.Y)|| Grid[p.X,p.Y].hasStone())
 8                 return; //如果鼠标的位置不在面板上或不在格点上或该点已经有了一颗棋子,那么就返回不绘制点
 9 
10             //ZZZZZ ZZZZ ZZ Z ZZZZZZ ZZZZ, ZZ ZZZZ ZZ ZZZ ZZZ ZZZZ ZZ ZZZ ZZZZ ZZZZ
11             //将这个点的信息添加到gameTree中
12             gmThisMove = new GoMove(p.X, p.Y, m_colorToPlay, 0);
13             playNext(ref gmThisMove);
14             gameTree.addMove(gmThisMove);
15             GoMove gmNextMove = AI(gameTree,m_colorToPlay);
16             playNext(gmNextMove);
17             gameTree.addMove(gmThisMove);
18      }