GOBang对战
一、學(xué)習(xí)目標(biāo)
參考可以通過(guò)因特網(wǎng)對(duì)弈的“吃棋子”游戲程序,按以下要求進(jìn)行改編,要求如下:
1)將游戲改為雙方對(duì)弈,而不是系統(tǒng)自動(dòng)下棋;
2)修改游戲規(guī)則,如五子棋的游戲規(guī)則;
3)同步改異步;
?
二、設(shè)計(jì)思路
1)放置棋子。先不考慮輪流下子的情況和輸贏的問(wèn)題,重新把服務(wù)器端和客戶端的發(fā)送信息和處理收到的信息處理一次。
客戶端:修改玩家對(duì)鼠標(biāo)點(diǎn)擊的事件處理,設(shè)置顏色color變量屬性表示玩家下的棋子的顏色,和side變量相同,下的棋子的顏色會(huì)相同,在下棋的地方會(huì)出現(xiàn)玩家相應(yīng)顏色的棋子。
private int color; //edit this.color = side;
/// <summary>在pictureBox1中按下鼠標(biāo)觸發(fā)的事件</summary>
??????? private void pictureBox1_MouseDown(object sender, MouseEventArgs e)
??????? {
??????????? int x = e.X / 20;
??????????? int y = e.Y / 20;
??????????? if (!(x < 1 || x > 15 || y < 1 || y > 15))
??????????? {
??????????????? if (grid[x - 1, y - 1] == DotColor.None)//edit
??????????????? {
??????????????????? //int color = (int)grid[x - 1, y - 1];
??????????????????? //發(fā)送格式:setDot,桌號(hào),座位號(hào),行,列,顏色
?????????????????? ?service.SendToServer(string.Format(
?????????????????????? "SetDot,{0},{1},{2},{3},{4}", tableIndex, side, x - 1, y - 1, this.color));//edit
??????????????? }
??????????? }
??????? }
服務(wù)器端,去掉課本例子中根據(jù)事件間隔隨機(jī)交替的發(fā)送兩種顏色的棋子那部分功能。每當(dāng)一方玩家下棋后,給同桌的玩家兩人發(fā)送新的棋子的位置。
public int turn;?????????????????? //輪流玩,0為黑,1為白
/// <summary>發(fā)送產(chǎn)生的棋子信息</summary>
??????? /// <param name="i">指定棋盤(pán)的第幾行</param>
??????? /// <param name="j">指定棋盤(pán)的第幾列</param>
??????? /// <param name="dotColor">棋子顏色</param>
??????? public void SetDot(int i, int j, int dotColor)//edit private to public
??????? {
??????????? if (dotColor == turn)//edit
??????????? {
??????????????? //向兩個(gè)用戶發(fā)送產(chǎn)生的棋子信息,并判斷是否有相鄰棋子
??????????????? //發(fā)送格式:SetDot,行,列,顏色
??????????????? grid[i, j] = dotColor;
??????????????? service.SendToBoth(this, string.Format("SetDot,{0},{1},{2}", i, j, dotColor));
??????????????? if (win(dotColor))//edit
??????????????? {
??????????????????? ShowWin(dotColor);
??????????????? }
??????????????? turn = (turn + 1)%2;
??????????? }
??????? }
?
2)解決輸贏判斷的問(wèn)題。五子棋游戲的規(guī)則為是先有相同顏色的五子相連的那方的玩家贏,棋子可以按照橫豎斜三個(gè)方向,但是考慮邊界的情況。
修改游戲勝利的條件。每當(dāng)一個(gè)玩家下子完畢后就判斷。GameTable類中更改勝利條件
//是否勝利。
??????? private bool win(int dotColor)//edit
??????? {
??????????? int num = 15;
??????????? int checkPoint = 0;
??????????? int i = 0;
??????????? int j = 0;
??????????? //橫著檢查。
??????????? for (int x = 0; x < num * num; x++)
??????????? {
??????????????? int consecutive = 0;
??????????????? checkPoint = x;
??????????????? for (int y = 0; y < 5; y++)
??????????????? {
??????????????????? i = (checkPoint + y) % num;
??????????????????? j = (int)checkPoint / num;
??????????????????? if (checkPoint > (num * num - 1) || i > num - 1)
??????????????????????? break;
??????????????????? if (grid[i, j] == dotColor)
??????????????????? {
??????????????????????? consecutive++;
??????????????????? }
??????????????????? //checkPoint++;
??????????????? }
??????????????? if (consecutive == 5)
??????????????????? return true;
??????????? }
??????????? //豎著檢查
??????????? for (int x = 0; x < num * num; x++)
??????????? {
??????????????? int consecutive = 0;
??????????????? checkPoint = x;
??????????????? for (int y = 0; y < 5; y++)
??????????????? {
??????????????????? i = (checkPoint + y) % num;
??????? ????????????j = (int)checkPoint / num;
??????????????????? if (checkPoint > (num * num - 1) || i > num - 1)
??????????????????????? break;
??????????????????? if (grid[j, i] == dotColor)
??????????????????? {
??????????????????????? consecutive++;
??????? ????????????}
??????????????????? //checkPoint++;
??????????????? }
??????????????? if (consecutive == 5)
??????????????????? return true;
??????????? }
??????????? //正斜
??????????? for (int x = 0; x < num * num; x++)
??????????? {
??????????????? int consecutive = 0;
??????????????? checkPoint = x;
??????????????? for (int y = 0; y < 5; y++)
??????????????? {
??????????????????? i = checkPoint % num + y;
??????????? ????????j = ((int)checkPoint / num) + y;
??????????????????? if (i > num - 1 || j > num - 1)
??????????????????????? break;
??????????????????? if (grid[i, j] == dotColor)
??????????????????? {
??????????????????????? consecutive++;
??????????????????? }
??????????????? }
??????????????? if (consecutive == 5)
??????????????????? return true;
??????????? }
??????????? //反斜
??????????? for (int x = 0; x < num * num; x++)
??????????? {
??????????????? int consecutive = 0;
??????????????? checkPoint = x;
??????????????? for (int y = 0; y < 5; y++)
??????????????? {
??????????????????? i = checkPoint % num - y;
??????????? ????????j = (int)checkPoint / num + y;
??????????????????? if (i > num - 1 || i < 0 || j > num - 1 || j < 0)
??????????????????????? break;
??????????????????? if (grid[i, j] == dotColor)
??????????????????? {
??????????????????????? consecutive++;
?????? ?????????????}
??????????????? }
??????????????? if (consecutive == 5)
??????????????????? return true;
??????????? }
??????????? return false;
??????? }
?
勝利后的提示信息改為
case "win":
??????????????????????? //格式:Win,相鄰棋子的顏色,黑方成績(jī),白方成績(jī)
??????????????????????? string winner = "";
??????????????????????? if ((DotColor)int.Parse(splitString[1]) == DotColor.Black)
??????????????????????? {
??????????????????????????? winner = "黑方出現(xiàn)五子相連,黑方勝利!";//edit
??????????????????????? }
??????????????????????? else
??????????????????????? {
??????????????????????????? winner = "白方出現(xiàn)五子相連,白方勝利!";//edit
??????????????????????? }
??????????????????????? formPlaying.ShowMessage(winner);
??????????????????????? formPlaying.Restart(winner);
??????????????????????? break;
?
?
3)雙方輪流的下棋子。在雙方都開(kāi)始后游戲正式開(kāi)始。
在前面代碼中已經(jīng)添加了turn變量,控制下棋方玩家。初始化的時(shí)候是-1,表示如果雙方?jīng)]有開(kāi)始游戲,都不能下子。FormServer類收到的message處理start時(shí),更改條件,都開(kāi)始后,把turn改為0,表示黑子先下,符合五子棋規(guī)則。關(guān)鍵代碼如下:
if (gameTable[tableIndex].gamePlayer[anotherSide].started == true)
??????????????????????? {
??????????????????????????? gameTable[tableIndex].ResetGrid();
??????????????????????????? //gameTable[tableIndex].StartTimer();
??????????????????????????? gameTable[tableIndex].Turn = 0;
??????????????????????? }
GameTable中,只有當(dāng)輪到自己時(shí),即到自己的顏色時(shí)才能下子。
public int Turn{
??????????? get{return turn;}
??????????? set { turn = value; }
??????? }
下子后,變換下子方。
turn = (turn + 1)%2;
?
4)同步改異步
程序在調(diào)用Begin…后,可以在調(diào)用線程上繼續(xù)執(zhí)行其下面的指令,同時(shí)異步操作在另一個(gè)線程上執(zhí)行。Begin…方法開(kāi)始異步操作,并返回一個(gè)實(shí)現(xiàn) IAsyncResult接口的對(duì)象。IAsyncResult對(duì)象存儲(chǔ)有關(guān)異步操作的狀態(tài)信息。這些信息包括:IAsyncState:可選的特定的對(duì)象,包含異步操作需要的信息。syncWaitHandle:用于在異步操作完成前阻止程序執(zhí)行。CompletedSynchronously:指示異步操作是否在用于調(diào)用Begin…的線程上完成,而不是在單獨(dú)的ThreadPool線程上完成。IsCompleted:一個(gè)布爾值,指示異步操作是否已完成。
服務(wù)器端:監(jiān)聽(tīng)客戶端和接收客戶端消息類似,使用異步方式調(diào)用同步方法,通過(guò)輪詢方式檢查異步調(diào)用是否完成,使用EndInvoke結(jié)束異步調(diào)用,EndInvoke方法用于檢索異步調(diào)用的結(jié)果,并結(jié)束異步調(diào)用。調(diào)用BeginInvoke之后,隨時(shí)可以調(diào)用該方法。如果異步調(diào)用尚未完成,則EndInvoke會(huì)一直阻止調(diào)用線程,直到異步調(diào)用完成。
private delegate void ListenClientDelegate(out TcpClient client);
??????? /// <summary>接受掛起的客戶端連接請(qǐng)求</summary>
??????? private void ListenClient(out TcpClient newClient)
??????? {
??????????? try
??????????? {
??????????????? newClient = myListener.AcceptTcpClient();
??????????? }
??????????? catch
??????????? {
??????????????? newClient = null;
??????????? }
??????? }
/// <summary>監(jiān)聽(tīng)客戶端請(qǐng)求</summary>
??????? private void ListenClientConnect()
??????? {
??????????? TcpClient newClient = null;
??????????? while (true)
??????????? {
??????????????? ListenClientDelegate d = new ListenClientDelegate(ListenClient);
??????????????? IAsyncResult result = d.BeginInvoke(out newClient, null, null);
??????????????? //使用輪詢方式來(lái)判斷異步操作是否完成
??????????????? while (result.IsCompleted == false)
??????????????? {
??????????????????? if (isExit)
??????????????????? {
??????????????????????? break;
??????????????????? }
??? ????????????????Thread.Sleep(250);
??????????????? }
??????????????? //獲取Begin方法的返回值和所有輸入/輸出參數(shù)
??????????????? d.EndInvoke(out newClient, result);
??????????????? if (newClient != null)
??????????????? {
??????????????????? //每接受一個(gè)客戶端連接,就創(chuàng)建一個(gè)對(duì)應(yīng)的線程循環(huán)接收該客戶端發(fā)來(lái)的信息
??????????????????? User user = new User(newClient);
??????????????????? Thread threadReceive = new Thread(ReceiveData);
??????????????????? threadReceive.Start(user);
??????????????????? userList.Add(user);
??????????????????? service.AddItem(string.Format("[{0}]進(jìn)入", newClient.Client.RemoteEndPoint));
??????????????????? service.AddItem(string.Format("當(dāng)前連接用戶數(shù):{0}", userList.Count));
??????????????? }
??????????????? else
??????????????? {
??????????????????? break;
??????????????? }
??????????? }
??? ????}
服務(wù)器端發(fā)送消息也類似上面的方法:
/// <summary>異步發(fā)送message給user</summary>
??????? public void AsyncSendToOne(User user, string message)
??????? {
??????????? SendToOneDelegate d = new SendToOneDelegate(SendToOne);
??????????? IAsyncResult result = d.BeginInvoke(user, message, null, null);
??????????? while (result.IsCompleted == false)
??????????? {
??????????????? Thread.Sleep(250);
??????????? }
??????????? d.EndInvoke(result);
??????? }
??????? public delegate void SendToOneDelegate(User user, string str);
?????? ?/// <summary>向某個(gè)客戶端發(fā)送信息</summary>
??????? /// <param name="gameTable">指定客戶</param>
??????? /// <param name="gameTable">信息</param>
??????? public void SendToOne(User user, string str)
??????? {
??????????? try
??????????? {
??????????????? user.sw.WriteLine(str);
??????????????? user.sw.Flush();
??????????????? AddItem(string.Format("向{0}發(fā)送{1}", user.userName, str));
??????????? }
??????????? catch
??????????? {
??????????????? AddItem(string.Format("向{0}發(fā)送信息失敗", user.userName));
??????????? }
??????? }
?
客戶端:用BackgroundWork組件實(shí)現(xiàn)異步編程功能
BackgroundWorker connectWork = new BackgroundWorker();
connectWork.DoWork += new DoWorkEventHandler(connectWork_DoWork);
????? connectWork.RunWorkerCompleted +=
??????????????? new RunWorkerCompletedEventHandler(connectWork_RunWorkerCompleted);
??????? /// <summary>異步方式與服務(wù)器進(jìn)行連接</summary>
??????? void connectWork_DoWork(object sender, DoWorkEventArgs e)
??????? {
??????????? client = new TcpClient();
??????????? //此處為方便演示,實(shí)際使用時(shí)要將Dns.GetHostName()改為服務(wù)器域名
??????????? IAsyncResult result = client.BeginConnect(Dns.GetHostName(), 51888, null, null);
??????????? while (result.IsCompleted == false)
??????????? {
??????????????? Thread.Sleep(100);
??????????? }
??????????? try
??????????? {
??????????????? client.EndConnect(result);
????? ??????????e.Result = "success";
??????????? }
??????????? catch (Exception ex)
??????????? {
??????????????? e.Result = ex.Message;
??????????????? return;
??????????? }
??????? }
??????? /// <summary>異步方式與服務(wù)器完成連接操作后的處理</summary>
??????? void connectWork_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
??????? {
??????????? if (e.Result.ToString() == "success")
??????????? {
??????????????? groupBox1.Visible = true;
??????????????? textBoxLocal.Text = client.Client.LocalEndPoint.ToString();
??????????????? textBoxServer.Text = client.Client.RemoteEndPoint.ToString();
??????????????? buttonConnect.Enabled = false;
??????????????? //獲取網(wǎng)絡(luò)流
??????????????? NetworkStream netStream = client.GetStream();
??????????????? //將網(wǎng)絡(luò)流作為二進(jìn)制讀寫(xiě)對(duì)象
???????????? ???sr = new StreamReader(netStream, System.Text.Encoding.UTF8);
??????????????? sw = new StreamWriter(netStream, System.Text.Encoding.UTF8);
??????????????? service = new Service(listBox1, sw);
??????????????? service.SendToServer("Login," + textBoxName.Text.Trim());
??????????????? Thread threadReceive = new Thread(new ThreadStart(ReceiveData));
??????????????? threadReceive.IsBackground = true;
??????????????? threadReceive.Start();
??????????? }
??????????? else
??????????? {
??????????????? MessageBox.Show("與服務(wù)器連接失敗 "+e.Result, "",
?????????????????? MessageBoxButtons.OK, MessageBoxIcon.Information);
??????????????? buttonConnect.Enabled = true;
??????????? }
??????? }
??????? /// <summary>【登錄】按鈕的Click事件</summary>
??????? private void buttonConnect_Click(object sender, EventArgs e)
??????? {
??????????? buttonConnect.Enabled = false;
??????????? connectWork.RunWorkerAsync();
??????? }
接受消息的方法跟服務(wù)器端相同,不重復(fù)描述。
?
三、個(gè)人總結(jié)
???? 由于這個(gè)實(shí)驗(yàn)是參考書(shū)上已有的例子修改的,所以沒(méi)有出現(xiàn)之前無(wú)從下手的情況。在實(shí)驗(yàn)的過(guò)程中,遇到了一些問(wèn)題。首先,是輪流下子的問(wèn)題,不知道怎么通知對(duì)方。后來(lái)的解決方案是當(dāng)服務(wù)器端發(fā)送下子坐標(biāo)的時(shí)候同時(shí)更新同一桌玩家雙方的棋盤(pán)布局,只需要在特定棋盤(pán)上用turn變量斷定下棋的玩家。第二是邊界的判斷比較復(fù)雜,我采取了最笨的方法,每次下子的時(shí)候都是從新判斷整個(gè)棋盤(pán)的橫豎正斜反斜,這樣程序的運(yùn)行效率降低了。第三是同步改異步的問(wèn)題,。改成異步以后,一直在想,在Form中生成了一個(gè)Service類,那么終止條件是什么。請(qǐng)教了同學(xué)之后,發(fā)現(xiàn)其實(shí)在Form退出的時(shí)候,service就自動(dòng)消失了。通過(guò)本次試驗(yàn),我感覺(jué)受益匪淺。
轉(zhuǎn)載于:https://www.cnblogs.com/maomiyy/p/3762183.html
總結(jié)
- 上一篇: 关于zendframework中的Zen
- 下一篇: 张艾迪(创始人): 梦想与未来