本課簡介
本課為五子棋系列最後一課這次的目標就是要寫一個簡單的勝利判斷
來看看怎麼寫吧!
教學影片
注意:影片有高畫質 720P 的選項,可以看得更清楚喔!重點提示
製作重點
- 檢查勝利的方式 - 只要檢查最後下的那顆棋子的八個方向是否有五顆棋子連成一直線
- 在測試程式時,若發生錯誤,可以把滑鼠移到變數上。此時就會顯示該變數的值為多少。
- 注意在 if 內有多個條件時,若程式發現只要檢查前面的條件就足夠,後面的條件就不會被執行。
製作步驟
- 在 Board 內新增一個 private Point 變數,lastPlacedNote,一開始設為 NO_MATCH_NODE
- 新增一個 public get 存取器 LastPlacedNote,用來取得 lastPlacedNote
- 在 Board 內的 PlaceAPiece 把最後下子的位置存在 lastPlacedNote
- 在 Game 內新增一個 private PieceType 變數 winner,一開始設為 PieceType.NONE
- 新增一個 public get 存取器 Winner,用來取得 winner
- 在 Game.PlaceAPiece 內的交換選手前,加入 CheckWinner。代表在交換前會先檢查勝利者。
- 實作 Game 的 CheckWinner() (先檢查一個方向)
- 先用兩個變數 centerX 跟 centerY 儲存 board.LastPlacedNote 的 X 跟 Y 座標
- 加入一個 while 迴圈檢查五顆座標連續的棋子是否顏色相同。檢查棋盤上某個位置的顏色可以用 board.GetPieceType(x, y)。
- 記得在呼叫 board.GetPieceType(x, y) 前,先檢查座標是否有超出棋盤邊界
- 若發現有一顆顏色不同就提早跳出 (break)
- 檢查是否看到五顆棋子,若沒看到代表沒有勝利者
- 在 Form1_MouseDown 內檢查 game.Winner。
- 若為 PieceType.BLACK,就用 MessageBox 印出「黑色獲勝」。白色也用類似方式處理。
- 在 Game.CheckWinner 內增加兩個 for 迴圈檢查八個方向
練習
- 嘗試解決最後棋子下在五顆連線的中間,而不是邊邊時無法判斷勝利的 bug
- 嘗試實作重新啟動遊戲的功能
- 在有人快勝利時提出警告訊息
- 想出一個方法讓白色自動下子 (例如:最簡單的作法,隨便找個讓白色隨機亂下)
作者已經移除這則留言。
回覆刪除不好意思,我所想的方法还是有bug。所以我删除了。
回覆刪除您好,您的练习题第一个,我想出了一种方法:检查一个方向的棋子,同时检查相对方向的棋子。我把您的check Winner的方法中增加了一些代码:在两个for循环中增加了一个:int count2 = 1;
回覆刪除while (count2 < 5)
{
int targetX = centerX - count2 * xDir;
int targetY = centerY - count2 * yDir;
if (targetX < 0 || targetX >= Board.NODE_COUNT ||
targetY < 0 || targetY >= Board.NODE_COUNT ||
board.GetPieceType(targetX, targetY) != currentPlayer)
{
break;
}
count2++;
}
同时在最后增加了一个判断:
//检查是否看到5颗棋子
if (count + count2 > 5)
winner = currentPlayer;
不知道我测试的一下,没有问题。如果有更好的方法或者我这个方法有问题,请多多指教,谢谢您。
竟然有人想法跟我一樣 來留言下
刪除不過count2=0才對 你這樣會多算一次就會變4子棋了
你也是个大佬
刪除上面這個方法會讓程式有多餘程式,基本上只要迴圈有四次並且使用以上的方法,
回覆刪除就等於找完八個地方了,應該是有更好解。
作者已經移除這則留言。
刪除谢谢指点,但是我还是不太明白,能给出稍微详细的解释吗?
刪除作者已經移除這則留言。
刪除我的想法是找完後存成二維array
刪除然後在一相對應位置相加看有沒有 4 個
public int[,] countPieceRecord = new int[3,3]; // 紀錄八個方向相同顏色棋子個數
// 判斷有無連線
private void CheckWinner()
{
int centerX = board.LastPlaceNode.X;
int centerY = board.LastPlaceNode.Y;
// 檢查八個方向
for( int xDir = -1; xDir <=1; xDir ++)
{
for( int yDir = -1; yDir <=1; yDir ++)
{
// 排除檢查自己(中心)
if( xDir ==0 && yDir ==0)
continue;
// 紀錄相同顏色棋子個數
int count = 1;
while(count < 5)
{
int targetX = centerX + count * xDir;
int targetY = centerY + count * yDir;
//Console.WriteLine("targetX:" + targetX);
// Console.WriteLine("targetY:" + targetY);
// 檢查顏色是否相同
if (targetX < 0 || targetX >= Board.NODE_COUNT ||
targetY < 0 || targetY >= Board.NODE_COUNT ||
board.GetPieceType(targetX, targetY) != currentPlayer)
break;
count++;
}
countPieceRecord[xDir + 1, yDir + 1] = count - 1;
if (isWinnerExist(countPieceRecord))
winner = currentPlayer;
}
}
}
//check winner exist or not
private bool isWinnerExist(int[,] record)
{
// int recordCenter_X = 1;
// int recordCenter_Y = 1;
int result1 = record[0,1] + record[2,1]; // 橫
int result2 = record[1,0] + record[1,2]; // 直
int result3 = record[0,2] + record[2,0]; // 斜
int result4 = record[0,0] + record[2,2]; // 反斜
if (result1 == 4 ||
result2 == 4 ||
result3 == 4 ||
result4 == 4 )
{
// winner exist
return true;
}
return false;
}
看完你寫五子棋程式的過程,收穫真是非常的多.
回覆刪除以前在書上看的語法,有讀沒有懂,經過模仿你寫程式的過程,
對於物件導向.重構.物件導向的設計原則.設計模式的理解和如何運用,
突然變得好清楚.真是非常感謝你!非常感謝你!
希望以後你有空,能夠再出一些這種從無到有的專案類型的教學影片.真是非常感謝你!
謝謝你的回饋~
刪除作者已經移除這則留言。
回覆刪除嘗試實作重新啟動遊戲的功能
回覆刪除private void removeAll()
{
int nodeCount = Board.NODE_COUNT;
for(int i=0; i<nodeCount ; i++)
{
for(int j=0; j<=nodeCount ; j++)
{
Piece p = game.GetPiece(i, j);
this.Controls.Remove(p);
// this.Controls.remove(p);
}
}
}
請問你的這段程式碼是放在哪一個位置?
刪除我是直接放Controls.Clear(); 再把其他值改成原本的樣子
刪除請問要改那些值?
刪除重作的話 我直接clear()掉棋子
回覆刪除並且把pieces = new pieces[9,9];
安安 小山,不知道小山方不方便開一門針對msdn lib 的教學影片
回覆刪除舉個例子
比方說public IButtonControl AcceptButton { get; set; } 這個
當看了這些敘述之後下一步該怎麼使用...等等之類的
有些在msdn 下方有例子,有些則沒有,可否請小山老師可以幫忙教如何看msdn 與用
謝謝
大神 能繼續新增嗎
回覆刪除我是剛學C#的新手 真的很需要您啊 拜託拜託><
我也試著重做了
回覆刪除但我發現直接使用Clear()方法好像會佔用內存,所以找了個應該算是蠻好的方法吧,
這裡會用到一個新的 集合 叫消息隊列
我在Game類裡添加一個新的變數:
public Queue QControls = new Queue();//消息隊列
還有一個新的方法:
public void ClearGame(Control item)
{
for (int i = 0; i < item.Controls.Count; i++)
{
//這裡又調用了自己 我也不太清楚,估計是為了防止出現bug
if (item.Controls[i].HasChildren)
{
ClearGame(item.Controls[i]);
}
else
{
//把找到的控件加入到消息隊列
QControls.Enqueue(item.Controls[i]);
}
}
//清理完後好像原來有棋子的地方就下不了棋了 所以重設了值
currentPlayer = PieceType.BLACK;
winner = PieceType.NONE;
Board.pieces = new Piece[Board.NODE_COUNT, Board.NODE_COUNT];
}
form1:
public void RemoveControls()
{
game.ClearGame(this);// this是指把form這個窗體傳進去
while (game.QControls.Count != 0)
{
game.QControls.Dequeue().Dispose();//釋放消息隊列
}
}
//如果還有不對的地方希望大佬指點
練習1,解決了,詳:
回覆刪除https://github.com/oscarsun72/c-sharp-course-sample-code/tree/master/class-40-50/Class46Ex_1
感恩感恩 讚歎讚歎 南無阿彌陀佛
練習2,解決了,詳:
刪除https://github.com/oscarsun72/c-sharp-course-sample-code/tree/master/class-40-50/Class46Ex_2
感恩感恩 讚歎讚歎 南無阿彌陀佛
練習3,OK了,詳:
回覆刪除https://github.com/oscarsun72/c-sharp-course-sample-code/tree/master/class-40-50/Class46Ex_3
但怕有bug,請再多試、留意。感恩感恩 南無阿彌陀佛
【在有人快勝利時提出警告訊息】
小弟分享我的方法和大家一起學習成長:
回覆刪除1.在Board中建立一個method找出對應當下PlaceAPiece的對應棋盤最: 左上角座標、右上角座標、上方座標、左方座標
2.在Game中建立一個掃描盤的method分別對上述四個點位掃描:左上到右下掃描、右上到左下掃描、上方至下掃描、左方至右掃描
掃描出看有沒有連續五個棋子等於CurrentPlayer,如果有winnerPlayer=CurrentPlayer
簡單說就是對當下放下去的那顆棋子進行米自行掃描,所以跳四變Win也能掃出來
3.在Board中增加Reset的method把pieces=new Piece[9,9]
4.在Game也增加一個Reset method調用Board的Reset,並把winner=PieceType.NONE;
5.在form code的buttom down下判斷game.winner!=PieceType.NONE就執行game.Reset();
3.~5.是指遊戲結束重來,5.要在加一行removeall的指令來清除棋子
刪除1)在for迴圈前增加一個int count_x的計數器
回覆刪除2)在第二個for迴圈count++,下面增加一行count_x++
3)在第一個for迴圈的底部增加判斷 if(count_x >= 5)則勝利,因為連線時有可能超過5棵棋子!