第一篇:五子棋網(wǎng)絡(luò)應(yīng)用程序開發(fā) 課程設(shè)計
網(wǎng)絡(luò)應(yīng)用程序開發(fā)報告 概述
游戲說明
五子棋在我國的歷史可謂是源遠流長,喜愛它的玩家也是大有人在,是很多人的娛樂首選,也是一種競技項目。但很多的五子棋軟件一般都是單機版的,游戲無法在兩個玩家之間進行?;谏鲜鲈颍遄悠遄鳛橐粋€古老而又有廣泛群眾基礎(chǔ)的游戲,他的完善性無疑是家玩家有很大的影響在互聯(lián)網(wǎng)迅速發(fā)展的今天,五子棋的流行已經(jīng)不僅僅只是在現(xiàn)實生活中的面對面的娛樂競技,在網(wǎng)上也已經(jīng)是廣大玩家的首選娛樂游戲。為適應(yīng)當(dāng)今社會五子棋軟件化的需求,通過對一般的五子棋軟件進行設(shè)計,通過本設(shè)計希望能在五子棋的軟件發(fā)展上起點微不足道的推動作用。實現(xiàn)當(dāng)今社會五子棋軟件化的需求。軟件設(shè)計采用visual studio2008版作為開發(fā)平臺,運用軟件工程的分析和編寫方法,收集主要相關(guān)文獻資料,對系統(tǒng)作詳細的分析和設(shè)計,實現(xiàn)五子棋的基本功能需求,達到一般五子棋的游戲功能和規(guī)則設(shè)置。在設(shè)計上,主要解決的問題有五子棋的各個方向判斷勝負算法的分析,以及五子棋中“聊天,重新開始”等基本功能的實現(xiàn)。
需求分析
一款游戲項目的確立是建立在各種各樣的需求上面的,這種需求往往來自于玩家的實際需求或者是出于公司自身發(fā)展和實力的情況,其中玩家的實際需求也就是說市場需求最為重要.面對游戲擁有不同知識和理解層面的玩家,項目的負責(zé)人(或者游戲制作人)對玩家需求的理解程度,在很大程度上決定了此類游戲開發(fā)項目的成敗.因此如何更好地的了解,分析,明確玩家需求,并且能夠準確,清晰以文檔的形式表達給參與項目開發(fā)的每個成員,保證開發(fā)過程按照滿足玩家需求為目的正確項目開發(fā)方向進行,是每游戲開發(fā)項目管理者需要面對的問題...而作為五子棋的設(shè)計需要考慮到的最基本的需求莫過于人機對戰(zhàn)與人人對戰(zhàn)功能的實現(xiàn),當(dāng)然還有下棋過程中的下棋悔棋功能以及判斷游戲的勝負等方面的要求。當(dāng)然最好是要考慮到界面的友好性,作為一個娛樂軟件,還應(yīng)該考
慮到玩家在游戲時的舒適 實現(xiàn)
2.1 設(shè)計思路
2.2 具體實現(xiàn) 2.2.1 界面設(shè)計
界面主要分四大部分:棋盤、ip地址、棋子選擇和聊天窗口,程序運行開始,先要輸入要連接的主機ip地址,然后選定棋子顏色,就可以下棋了。輸入文本的控件我選擇了comboBox空間,在其下拉表中預(yù)先輸入一些常用的語句,如:你好,嗨等,這樣方便聊天。聊天記錄窗口我用了listBox,當(dāng)聊天內(nèi)容比較多時,會自動添加滾動條,便于用戶閱讀聊天信息
2.2.2按鈕功能實現(xiàn)
(1)重新開始按鈕 public void Restart()
{
radBlack.Enabled = true;
radWhite.Enabled = true;
for(int i=0;i for(int j = 0;j < Level;j++) { pic[i, j].Image = Image.FromFile(“Board.jpg”); flag[i, j] = false; doubleFlag[i, j] = false; } this.AllowClick = true; } private void button2_Click(object sender, EventArgs e) {this.Restart();}(2)發(fā)送按鈕 private void btnSend_Click(object sender, EventArgs e) { String ForeStr = “rn” + txtSelfIP + “【” + DateTime.Now.ToShortDateString()+ “ ” + DateTime.Now.ToShortTimeString()+ “】:rn”; this.Sender.send(txtIP.Text.Trim(),txtSend.Text.Trim(), 12001); txtShow.Text += ForeStr; int BeforeIndex = txtShow.Text.Length; txtShow.Text += txtSend.Text; txtShow.Select(BeforeIndex, txtShow.Text.Length); txtShow.SelectionBackColor = Color.White; txtShow.SelectionColor = SelfFontColor; //滾動 txtShow.Select(txtShow.Text.Length, 0); txtShow.ScrollToCaret(); txtSend.Text = “"; } 2.2.3棋子位置判斷 獲得所下的棋子位置的下標: RowIndex =(p.Location.Y30)/(p.Width);判斷是否有棋子添加到棋盤上,是通過Button1是否產(chǎn)生單擊事件來判定的,在Button按鈕單擊事件中,編入了輸贏的判斷代碼,結(jié)果用messagebox的形式顯示出來,還有一些對不合規(guī)則下棋方法的判斷及給出相應(yīng)的提示 private void button1_Click(object sender, EventArgs e) { if(this.AllowClick) { this.radBlack.Enabled = false; this.radWhite.Enabled = false; PictureBox p =(PictureBox)sender; p.Image = Image.FromFile(PictureFileName); //獲得所下的棋子位置的下標 RowIndex =(p.Location.Y30)/(p.Width); String Message;Message = ”Pos“ + Splitor + RowIndex.ToString()+ Splitor + ColIndex.ToString();//發(fā)送的信息字符串 Flag[RowIndex, ColIndex] = true; //記錄 if(this.Judge1()|| this.Judge2()|| this.Judge3()|| this.Judge4())Message += Splitor + this.WinStr; try { int Port = Convert.ToInt32(txtPort.Text.Trim()); if(doubleFlag[RowIndex, ColIndex]) { pic[RowIndex, ColIndex].Image = Image.FromFile(OtherPictureFileName);MessageBox.Show(”不能在有棋子的地方下棋“, ”消息“);AllowClick = true;} else { Sender.send(txtIP.Text.Trim(), Message, Port); Sender.send(txtIP.Text.Trim(), Message, Port); Sender.send(txtIP.Text.Trim(), Message, Port); Sender.send(txtIP.Text.Trim(), Message, Port); Sender.send(txtIP.Text.Trim(), Message, Port); AllowClick =false; } } catch(Exception ex) { MessageBox.Show(ex.Message, ”錯誤!“);this.Restart();} doubleFlag[RowIndex, ColIndex] = true;//雙方記錄 if(this.Judge1()|| this.Judge2()|| this.Judge3()|| this.Judge4()) { this.WinCount += 1;MessageBox.Show(”勝利!勝負情況: 勝“+ WinCount.ToString()+”負“+ LoseCount.ToString(), ”對戰(zhàn)結(jié)束“);this.Restart(); } } else MessageBox.Show(”不能搶下棋子“); } public Form1() { InitializeComponent(); for(int i = 0;i < Level;i++) { for(int j = 0;j < Level;j++) { pic[i, j] = new PictureBox(); pic[i, j].Click += new EventHandler(this.button1_Click); } } } public void StartListen() { while(true) { int Port = Convert.ToInt32(txtPort.Text.Trim()); udpclient = new UdpClient(Port); IPEndPoint ipendpoint = new IPEndPoint(IPAddress.Any, Port); try { byte[] bytes = udpclient.Receive(ref ipendpoint); String StrReceive = ”“;StrReceive = Encoding.GetEncoding(”gb2312“).GetString(bytes, 0, bytes.Length); String[] parts = StrReceive.Split(Splitor); if(parts[0].Equals(”Pos“)) { ReceivedRow = Convert.ToInt32(parts[1]); ReceivedCol = Convert.ToInt32(parts[2]); pic[ReceivedRow,ReceivedCol].Invoke(new MyDelegate(this.InvokeFun)); doubleFlag[ReceivedRow, ReceivedCol] = true;try {if(parts[parts.Length3;h++) { j=h; for(int i=0;i { if(flag[i,j]) { count++; if(count >= 5) return true; } else count = 0; } count = 0; } //下三角 for(int row = 1;row < Level-3;row++) { j = row; for(int col = 0;col < Level && j < Level;j++, col++) { if(flag[j, col]) { count++; if(count >= 5)return true; } else count = 0; } count = 0; } return false; } public bool Judge4() //左下——右上 { int count = 0; int h; //上下三角 for(int c=0;c for(int j = Level-1;j >=0;j--) { h = j; for(int i = c;i < Level && h >=0;i++) { if(flag[i,h]) { count++; if(count >= 5)return true; } else count = 0;h--; } count = 0; } return false; } 2.27、聊天監(jiān)聽 public void ListenChat() { while(true) { try { int Port = 12001; udpclient2 = new UdpClient(Port); IPEndPoint ipendpoint2 = new IPEndPoint(IPAddress.Any, Port); byte[] bytes = udpclient2.Receive(ref ipendpoint2);this.ChatMessage = Encoding.GetEncoding(”gb2312“).GetString(bytes, 0, bytes.Length); udpclient2.Close(); txtShow.Invoke(new ChatDelegate(this.ChatInvoke)); } catch(Exception){ udpclient2.Close();break;} this.ChatMessage = ”“; } } public void ChatInvoke(){ String ForeStr = ”rn“ + this.txtIP + ”【“ + DateTime.Now.ToShortDateString()+ ” “ + DateTime.Now.ToShortTimeString()+ ”】:rn"; txtShow.Text += ForeStr;//添加到聊天窗口 int Start = this.txtShow.Text.Length; txtShow.Text += this.ChatMessage; } 3 總結(jié) //字體變色 txtShow.Select(Start, this.ChatMessage.Length);txtShow.SelectionColor = OtherFontColor;txtShow.SelectionBackColor = Color.White;//滾動 txtShow.Select(txtShow.Text.Length, 0);txtShow.ScrollToCaret(); 姓 名: 劉旭 學(xué) 院: 計算機與通信學(xué)院 班 級: 通信工程101班 指導(dǎo)老師: 文志誠 目錄 一、需求分析..................................................................................................................................3 1.1 開發(fā)背景....................................................................................................................................3 2.2 功能簡介....................................................................................................................................3 二、系統(tǒng)設(shè)計..................................................................................................................................4 2.1 函數(shù)一覽....................................................................................................................................4 2.2 “封面”的設(shè)計........................................................................................................................4 2.3 二維數(shù)組與控制臺....................................................................................................................5 2.4 鍵盤操作....................................................................................................................................6 2.5判定.............................................................................................................................................7 2.6 悔棋的實現(xiàn)................................................................................................................................8 三、調(diào)試運行..................................................................................................................................9 3.1 進入界面....................................................................................................................................9 3.2 棋盤的初始狀態(tài)......................................................................................................................10 3.3 激戰(zhàn)中……..............................................................................................................................10 3.4 游戲結(jié)束..................................................................................................................................11 四、解決問題的關(guān)鍵....................................................................................................................11 五、課設(shè)總結(jié)................................................................................................................................11 六、附錄........................................................................................................................................12 6.1 畫圖代碼..................................................................................................錯誤!未定義書簽。6.2 初始化......................................................................................................錯誤!未定義書簽。6.3 Play函數(shù)..................................................................................................錯誤!未定義書簽。 一、需求分析 1.1 開發(fā)背景 學(xué)習(xí)了數(shù)據(jù)結(jié)構(gòu)該門課程,對于枯燥無味的理論知識,我們是否能夠通過所學(xué)的知識在課程設(shè)計中做出有趣味東西,然后讓我們對于數(shù)據(jù)結(jié)構(gòu)更加的感興趣呢?于是我和我的室友陳明建開始醞釀著寫些什么東西。上個學(xué)期就已經(jīng)寫了通訊錄那之類的鏈式結(jié)構(gòu),這次我們決心有所改變,我們學(xué)習(xí)了棧、隊列、樹、圖,字典樹有人選了,我們就來寫一個基于圖的小程序,五子棋,對,圖的簡單應(yīng)用,于是我們開始著手來寫這個小小的程序,祝我們好運! 2.2 功能簡介 既然是五子棋,我們要做的是時時刻刻的將整個圖(以下稱為棋局)的狀態(tài)呈現(xiàn)出來,那么界面就是必不可少的。MFC不會?沒關(guān)系,我們就用基于控制臺的字符輸出來構(gòu)建這個棋局吧,當(dāng)然這只是第一步,詳細如下: 1擁有一個良好的進入界面,以及必要的選項; ○2擁有一個二維的數(shù)組來記錄和更新實時的狀態(tài),并且能夠有一種方法在DOS界面下繪制○出整個棋局的實時狀態(tài)(包括棋盤和棋子); 3能夠通過鍵盤上的按鍵完成所選位置的移動和選定操作; ○4能夠在每一次的走棋后判定是否游戲結(jié)束(棋盤走滿或者是一方勝出); ○5能夠完成悔棋的功能,并保證這之間的棋局繪圖能夠與二維數(shù)組數(shù)據(jù)同步,做到真正意○義上的悔棋。 二、詳細設(shè)計 2.1 函數(shù)一覽 2.2 “封面”的設(shè)計 首先還是講些題外話,該程序由于與控制臺有密切的關(guān)系,于是在代碼中使用了不少 conio.h 中的函數(shù),當(dāng)然在顯示時又使用了windows.h 中的 Sleep()函數(shù),正是有了這些函數(shù)的使用,程序才得以順利完成,尤其是后面頻繁使用的gotoxy()函數(shù)。 進入正題,由于是一個小的程序,因此將每一個功能分成一個一個的函數(shù),這樣將在以后的修改和完成進度上都有很大的幫助。由上面的函數(shù)一覽可以知道這個“封面”就是在Logo()函數(shù)里面實現(xiàn)的,函數(shù)實現(xiàn)過程中使用了Sleep()函數(shù),使之有動態(tài)效果: void Logo(){ char Wel[30]= { “Made By Lyush&& Mirs Chen” }; printf(“ttt 歡迎試用五子棋系統(tǒng)n”); printf(“tt ”); for(int i= 0;i< strlen(Wel);++i) { putchar(Wel[i]); Sleep(200);// 可使字符一個一個的輸出 } putchar(10);// 換行對應(yīng)的 ASCII 碼值為十進制的 10 } 2.3 二維數(shù)組與控制臺 二維數(shù)組是用來使得整個棋盤的信息全部記錄下來,因此在結(jié)構(gòu)體中二維數(shù)組的聲明是最關(guān)鍵的。 struct { int Status[MAX/2+2][MAX/2+2]; int MINBOX; int Step; char Graph[3][3]; char *FillGraph[9]; Sta Stack;} ChessBoard; 聲明全局變量是為了使得各函數(shù)能夠更方便地使用到這個結(jié)構(gòu)體,現(xiàn)假設(shè)某點的坐標為(1, 1),那么如何在屏幕上打印這個點呢?這就利用到了ChangeCoordinates()與gotoxy()函數(shù),前者使坐標進行轉(zhuǎn)換,后者讓光標走到所指的那個點,其實主要還是因為類似“┣、╋、●、○”在橫向上所占都是兩個英文字母的距離,因此在控制臺上反映的就是和數(shù)組下標倍數(shù)關(guān)系了。部分代碼如下: HANDLE hConsole= GetStdHandle(STD_OUTPUT_HANDLE); void ChangeCoordinates(int _X, int _Y, int *X, int *Y){ *X=(_X-1)* 2; *Y=(_Y-1)* 4;} void gotoxy(int x, int y)//這是光標的函數(shù) { COORD coord; coord.Y= x; // 在實際的應(yīng)用過程中發(fā)現(xiàn)交換x與y的賦值 coord.X= y; // 更好理解,即橫行位x,縱行為y。 SetConsoleCursorPosition(hConsole, coord); } 2.4 鍵盤操作 在剛開始寫這個五子棋的時候是以坐標來確定玩家的每一步棋,但后來發(fā)現(xiàn)這樣操作性實在是差,鍵盤操作是更好的選擇。這里又要用到一個函數(shù) getch(),其作用是無回顯的接受從鍵盤輸入的字符,讓屏幕不會出現(xiàn)你輸入的字符且等待著按回車確定…… 有了這個寶貝函數(shù),馬上得到 “↑” 對應(yīng)的ASCII碼為-32和72 兩個連著的數(shù)值,依次可得其他對應(yīng)的ASCII碼。后面在使玩家一和玩家二分離操作,玩家一則是利用 W、S、A、D + space來操作,玩家二則是 上下左右+ enter。配合ChangeCoordinates()與gotoxy()函數(shù),完成對走棋的控制。部分代碼如下: if(Opreat[0]== 13&& Ply== 2|| Opreat[0]== 32&& Ply== 1){ if(ChessBoard.Status[Move_X][Move_Y]== 0) { int TTop= ++ChessBoard.Stack.Top;ChessBoard.Status[Move_X][Move_Y]= Ply;ChessBoard.Stack.Record[TTop][0]= Move_X;ChessBoard.Stack.Record[TTop][1]= Move_Y;printf(“%s”, Graph);return true;// 該次走棋操作有效 } else { … } } if(Opreat[0]==-32&& Opreat[1]== 72|| Opreat[0]== 'w'|| Opreat[0]== 'W'){// 凡是接受了“上操作”,則Move_X的值減一,if(Currect(Move_X-1, Move_Y)) { Move_X-= 1; } } else if(…){ … } // 這是接下來的轉(zhuǎn)換操作 ChangeCoordinates(Move_X, Move_Y, &Temp_X, &Temp_Y);Gotoxy(Temp_X, Temp_Y); 2.5判定 對于每次走棋后,首先應(yīng)該做的就是判定一否有五個棋子已經(jīng)連成一線,也是一個簡單的搜索過程,由于每次走的點不一定是最外部的點,因此從每次走的點的兩頭同時搜索,當(dāng)遇到兩端同時結(jié)束時,搜索結(jié)束。當(dāng)滿足五子時游戲結(jié)束。當(dāng)然,當(dāng)棋盤被走滿時,游戲亦結(jié)束。代碼如下: bool Legal(int Point){ if(Point< 1|| Point> MAX/ 2+ 1) return false; else return true;} //搜索45度角是否為滿足ChessBoard.MINBOX 以X正軸為參考軸 if(!Flag){ Count= 1;for(int i1= X-1, j1= Y+ 1, i2= X+ 1, j2= Y-1;Legal(i1)&& Legal(j1)|| Legal(i2)&& Legal(j2);i1--, j1++, i2++, j2--){ int LastCount= Count; if(Legal(i1)&& Legal(j1)&& ChessBoard.Status[i1][j1]== Ply) { Count++; } if(Legal(i2)&& Legal(j2)&& ChessBoard.Status[i2][j2]== Ply) { Count++; } if(LastCount== Count) break; if(Count== ChessBoard.MINBOX){ Flag= 1; return true; } } } 2.6 悔棋的實現(xiàn) 雖說下棋悔棋是一種不道義的行為,但是如果雙方約定好了,未嘗不可。在沒寫悔棋之前,只是記錄了“上一次”的位置,聲明了Last_X,Last_Y;當(dāng)然既然要求悔棋,那么直接調(diào)用棧頂元素,即可定位上次走棋的位置。那么悔棋呢,取出“上一次”的位置,判定位置(不同的位置對應(yīng)不同的填充圖形類型)在二維數(shù)組中撤銷走棋時所賦予的 Ply 值(玩家一走時,其值為1,玩家二走時,其值為2),重新將 ChessBoard.Status[ Last_X ][ Last_Y ] 賦為0。代碼如下: int GetFillType(int X, int Y){ if(X== 1) { if(Y== 1) return 0; else if(Y== 16) return 2; else return 1; } else if(X== 16) { if(Y== 1) return 6; else if(Y== 16) return 8; else return 7; } else { if(Y== 1) return 3; else if(Y== 16) return 5; else return 4; } } bool Retract(int *X, int *Y){ int Temp_X, Temp_Y, TTop, FillType; if(!StackEmpty()) { TTop= ChessBoard.Stack.Top--; *X= ChessBoard.Stack.Record[TTop][0]; *Y= ChessBoard.Stack.Record[TTop][1]; ChessBoard.Status[*X][*Y]= 0;// 將該點置為真正意義上的空點 FillType= GetFillType(*X, *Y); ChangeCoordinates(*X, *Y, &Temp_X, &Temp_Y); Gotoxy(Temp_X, Temp_Y); printf(“%s”, ChessBoard.FillGraph[FillType]); return true; } else { Gotoxy(9, 65); printf(“您已不能悔棋”); Sleep(300); Gotoxy(9, 65); printf(“ ”); return false; } } 三、調(diào)試運行 3.1 進入界面 3.2 棋盤的初始狀態(tài) 3.3 激戰(zhàn)中…… 3.4 游戲結(jié)束 四、解決問題的關(guān)鍵 這個五子棋的程序并沒有什么復(fù)雜的算法,只是利用了簡單的圖知識和一個棧的應(yīng)用,在這里主要的關(guān)鍵問題就是如何將程序有條理的寫下來,有一個好的邏輯思維。將程序分成了多個功能函數(shù),盡量的讓一個函數(shù)的功能單一,只是在內(nèi)部調(diào)用了其他的函數(shù)以輔助改函數(shù)功能的實現(xiàn),比如判定坐標是否越界,坐標是否合法,悔棋的點的位置狀態(tài)…… 這樣便能做到各個擊破,程序的形成也就變得暢通許多了。 五、課設(shè)總結(jié) 剛開始寫這個程序,認為一定要用到 graphics.h, 無奈電腦TC不兼容,因此只好強行來畫這個界面了,使用輸入法里面的制表符,效果還不錯,通過一長串的if … else … 最好還是畫出來了,這個時候覺得控制臺的簡單圖形還是能夠畫出來的,并且可以盡量去美化它的界面。后面的附錄中將給出畫棋盤和棋子的源代碼。在程序設(shè)計的過程中,尤其是為源程序加上悔棋的功能,這期間總是有許多意想不到的錯誤,比如加上后,有時走了5個連子棋,但是程序并沒有判定輸贏,而是可以繼續(xù)走、有時沒有五個卻已經(jīng)結(jié)束了,光標沒有復(fù)位,悔棋后,玩家的走棋順序沒有跟著改變……通過后來的一步步修改終于使得這些問題都一一解決了,比如說對 Prompt(提示)函數(shù)引進了返回值,判斷該次操作是否成功,如果下了棋則為 true,如果是悔棋就是 false 了,這樣便使得后面的操作更規(guī)范了和統(tǒng)一了。 六、附錄 #include HANDLE hConsole= GetStdHandle(STD_OUTPUT_HANDLE); void HideCursor(){ CONSOLE_CURSOR_INFO cursor_info = {1, 0}; SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE), &cursor_info);} typedef struct { int Record[260][2]; int Base; int Top;} Sta; struct { int Status[MAX/2+2][MAX/2+2]; int MINBOX; int Step; char Graph[3][3]; char *FillGraph[9]; Sta Stack; } ChessBoard; void Gotoxy(int x, int y)//這是光標的函數(shù) { COORD coord; coord.Y= x; coord.X= y; SetConsoleCursorPosition(hConsole, coord); } void Logo(){ char Wel[30]= { “Made By Lyush&& Mirs Chen” }; printf(“ttt 歡迎試用五子棋系統(tǒng)n”); printf(“tt ”); for(int i= 0;i< strlen(Wel);++i) { putchar(Wel[i]); Sleep(200); } putchar(10);} int Login(){ int Mode, Skip= 0; char Request; if(!Skip) { printf(“nn在這兒你能DIY(Do it youself!)你的棋子,每個棋子接受一個漢字”); printf(“ Y Orz Nn”); scanf(“%c”, &Request); if(Request== 'Y'|| Request== 'y') { printf(“玩家一的 DIY 棋子-->”); scanf(“%s”, ChessBoard.Graph[1]); ChessBoard.Graph[1][2]= '
第二篇:數(shù)據(jù)結(jié)構(gòu)課程設(shè)計-五子棋