• 沒有找到結果。

網路象棋遊戲

N/A
N/A
Protected

Academic year: 2021

Share "網路象棋遊戲"

Copied!
39
0
0

加載中.... (立即查看全文)

全文

(1)

資 訊 工 程 學 系 專 題 報 告

網路象棋遊戲

學 生: 朱 孝 毅 (四丁)

張 書 豪 (四丁)

指導教授: 黃 秋 煌

中華民國九十一年十二月

(2)

目 錄

第一章 導論 ---4 1.1 專題動機---4 1.2 專題目標---4 第二章 系統環境介紹--- 5 2.1 系統平台---5 2.2 BRS Web Weaver--- 5~7 2.3 JAVA I/O 概論--- 8~9 2.4 JAVA Applet 概論---9 2.4.1 Applet 的生命週期---10 2.5 Java Socket 簡介--- 11 第三章 Client 端遊戲程式--- 12 3.1 遊戲流程圖---12 3.2 棋盤與棋子的建立---13~14 3.3 移動棋子---15~16 3.4 走法檢查---17~26 3.5 遊戲結束檢查---27 3.6 音效---28

(3)

第四章 Server 端程式---34 4.1 Server 流程圖--- 34 4.2 建立 server 端 socket---35 4.3 接受用戶端連線---35~36 4.4 處理用戶端相關指令並回傳至另一 client 端---36~37 第五章 專題製作心得與總結---38 5.1 鳴謝---38 5.2 製作過程所遇到的困難--- 38~39 5.3 專題製作心得感想--- 39~40 參考資料--- 40

(4)

第一章 導論

1. 1

專題動機

:

因為小的時候很愛玩象棋,但自從電動這個東西問世後,就沒再接 觸那古老的遊戲,說到電動該怎麼形容我對他的喜愛呢?只要拿到 一片新的 game,即使不吃不喝也能玩上三天三夜,差不多就是這 樣,而且從高中開始就在玩電腦的 game,所以對電腦也情有獨鐘, 故上了大學選擇了資工系這科。其實在決定專題題目的時候真的很 迷網,因為實在沒有一個目標,思考許久才想到小時候最古老的 game ,象棋!!! 加上大學所學寫一個象棋 game 應該是個不錯的主 意,而且順便可訓練我們寫程式的功力,因為大學四年所寫的程式 最多都沒超過一百行,終於網路象棋這個題目就這樣誕生了。

1.2 專題目標:

因為網路遊戲已經成為趨勢,我們的目標希望能寫出最基本可以 二台電腦利用連線來對奕的功能,藉此更能順應時代潮流。

(5)

第二章 系統環境介紹

2.1 系統平台:

作業系統 : Window 2000 專業版 程式語言 : Java 1.4 網頁伺服器: BRS Web Weaver

2.2 設定 BRS Web Weaver

1. 安裝 BRS Web Weaver 軟体 2. 設定 Sever IP 及 虛擬目錄 圖一 web Sever IP 設定

(6)
(7)

2.3 Java I/O 概論:

JAVA 把 I/O 動作(包含讀寫檔、存取記憶体、網路資源或是將 資料顯示在燭幕上都算是 I/O 串流)視為串流。JAVA 裡面的串流是位 元或字元的組合。程式編撰人員可以透過串流來讀取或寫入資料。甚 至可以透過串流連到資料來源並將資料以位元或字元陣列的形式儲 存起來。

Stream, Reader 與 Writer 元件

透過串流的作法可以使得撰寫資料來源的程式更為一致,我們 只需要利用 JAVA 中的 java.io 的類別庫就可以將各種檔案處理、記憶 体處理或是網路處理都視為串流來做處理即可。

在 JAVA 裡面我譬可以透過他的 InputStream,OutputStream, Reader , 和 Writer 類 別 來 處 理 串 流 I/O 。 其 中 InputStream 和 OutputStream 主要用在以位元為主的串流中,而 Reader 和 Writer 類 別則用來處理以字元為主的串流。有的串流元件可以做記憶体的緩衝 處理,有的則沒有這樣的功能。 一般都透過串流類別的建構來建立串流位元,但串流類別區分為 輸入串流和輸出串流,因此程式撰寫人員需選用適合的串流類別加以 使用才行。建好串流物件之後,皆下來則是用到他裡面的 read 和 write 方法來讀取或傳輸資料。而使用完一個串流物件之後,還需透過它的

(8)

close 方法來關閉串流,並結束對資料來源的處理。

2.4 Java Applet 概論:

Applet 能動態的將 web sever 中的資料下載到瀏覽器之中。接著 在瀏覽器所在的環境下去執行 Applet。然而從網路上下載程式並於用 戶端電腦中執行事件頗具危險。因此,Applet 並沒有和其他具潛在它 危險的應用程式一樣,它們被限制在某個範圍內運作。換句話來說, 它們不可以在超出範圍處執行程式碼。 舉例來說,Applet 通常是不被允許做執行讀取或寫入的動作。因 為此行為存在著摧毀磁碟中所有資料的危險性。Applet 也不可以執行 電腦中原有的程式碼,因此被限制無法啟動用戶端應用程式。 或許對於許多使用者照成莫大的不方便,然而這些預設的限制乃 在於保護使用者避免掉不必要的損失。不過,JAVA 的安全架構正逐 漸成形,而有另一種機制,它允許使用者釋放對 Applet 的限制,使 得 Applet 可以執行可信賴的程式碼,而被用來區分可信賴與不可信 賴的程式碼為一數字符號,每個 Applet 會有一個數字符號,如果當 使用者相信此程式是沒有問題時,那他便可以選擇釋放 Applet 的限 制。

(9)

2.4.1 Applet 的生命週期

在我們一般所撰寫的 JAVA 應用程式當中我們是透過 main()開 始執行。但 Applet 則 必 需 透 過 web 瀏覽器或其他工具,例如 Appletviewer 來執行,因為 Applet 中沒有 main()方法。

Applet 的生命週期由四個方法所組成,分別為 init()、start()、stop() 及 destroy()。這些方法都可以在 java.applet.Applet 類別中定義。而每 個 Applet 都有此特性。

在 Applet 開始執行時會先呼叫 init()方法。在 init()方法中程式碼 僅會執行一次。例如:讀取 HTML 檔案中所定義的參數。

在 init()方法完成執行後,start()方法開始執行。透過 Appletviewer 或 Web 瀏覽器最小化時,會去呼叫 stop 方法。而在 Applet 瀏覽器最 大化時,則會呼叫 start 方法。

若使用者正在瀏覽一個含有 Applet 的網頁。接著跳到另一個網 頁,則會呼叫 stop 方法。但若使用者再度返回原網頁時,則瀏覽器 會呼叫 start()方法。最後,在 applet 執行結束之前,appletviewer 或 web 瀏覽器會去呼叫 destroy()方法。不過,在呼叫 destroy()方法之前, 還會先去呼叫 stop()方法。

(10)

2.5 JAVA Socket 簡介

Socket 是網路應用程式的核心,不論 Sever 端或 Client 端網路應 用程式 Socket 皆為不可或缺之要素,二台電腦若要相連,首先 server 端及 Client 端都要建立一個 socket,當 server 端偵測到來自 client 端的連結請求時,則接受此請求並藉此建立 client 端之 socket,此 socket 將做為此 client 端連線及後續處理傳送及接收資 料的依據,至此則完成 server 端與 client 端的 socket 通訊連結。 以下為大致上的流程圖: ServerSocket Connection Client Accept Request read write Close Server 圖三 socket 流程圖

(11)

第三章 Client 端遊戲程式

3.1 遊戲流程圖

開始 接收黑 子移動 檢查棋步 是否合法 輸出錯 誤音效 否 輸出移 動音效 是 檢查遊戲 是如結束 接收紅 子移動 檢查棋步 是否合法 輸出錯 誤音效 否 輸出移 動音效 是 否 檢查遊戲 是如結束 輸出結 束音效 結束 是 是

(12)

3.2 棋盤與棋子的建立

利用一個 class 來建立棋盤上 90 個點的座標及其目前的狀態 class Chess_tab { int X,Y; // 棋盤座標 int turn; int kind; int my_color; int state; int pic_num; //1~16 紅棋,17~32 黑棋 Point pt = new Point(0,0); //screen 定位

public Chess_tab(int x,int y,int n,int sta,int kind,int my_cr) { pt.x = x; pt.y = y; this.my_color = my_cr; this.kind = kind ; this.pic_num = n; this.state = sta; } } 這個 class 包含了: X,Y 為螢幕上的座標點,一個棋子的大小為 40 X 40。 kind 為棋子的種類,記錄棋子是將、士、象、車、馬、包或兵。 my_color 記錄著棋子的顏色,用來判斷是否為自已方的棋子。 state 記錄著棋盤上 90 個點,每一個點上面是否存在著棋子。 pic_num 則是用來判斷要把哪張圖貼上用的。

(13)

初始值為:

tab[0][0] = new Chess_tab( 0, 0, 5,1,5,0); tab[0][1] = new Chess_tab( 40, 0, 4,1,4,0); tab[0][2] = new Chess_tab( 80, 0, 3,1,3,0); tab[0][3] = new Chess_tab(120, 0, 2,1,2,0); tab[0][4] = new Chess_tab(160, 0, 1,1,1,0); tab[0][5] = new Chess_tab(200, 0, 2,1,2,0); tab[0][6] = new Chess_tab(240, 0, 3,1,3,0); tab[0][7] = new Chess_tab(280, 0, 4,1,4,0); tab[0][8] = new Chess_tab(320, 0, 5,1,5,0); tab[1][0] = new Chess_tab( 0, 40, 0,0,0,0); …

… … . … … … .

tab[8][8] = new Chess_tab(320,320, 0,0,0,0); tab[9][0] = new Chess_tab( 0,360,15,1,5,1); tab[9][1] = new Chess_tab( 40,360,14,1,4,1); tab[9][2] = new Chess_tab( 80,360,13,1,3,1); tab[9][3] = new Chess_tab(120,360,12,1,2,1); tab[9][4] = new Chess_tab(160,360,11,1,1,1); tab[9][5] = new Chess_tab(200,360,12,1,2,1); tab[9][6] = new Chess_tab(240,360,13,1,3,1); tab[9][7] = new Chess_tab(280,360,14,1,4,1); tab[9][8] = new Chess_tab(320,360,15,1,5,1);

記錄著一開始棋盤的貼圖狀況,之後每移動一步這個 tab 將會跟著 改變,直到遊戲結束。

(14)

3.3 移動棋子

在 mouseDown 函式中,先檢查了 lock 和 gameover 這二個變 數,lock 是用來檢查是否是自已可以移動的回合,而 gameover 則是 檢查是否遊戲是否已經結束了。mouseDown 函式中會取得按下左鍵 時的座標位置,然後透過 find_witch 函式

public void find_which(int x,int y) { int cx,cy; for(int i=0;i<10;i++) { for(int j=0;j<9;j++) { cx = tab[i][j].pt.x + 20; cy = tab[i][j].pt.y + 20; if(Math.abs(x - cx)<20 && Math.abs(y - cy)<20) { row = i; col = j; } } } } 找出其對應在棋盤上 90 個點中的哪一個,再把其值傳回去 mouseDown 然後檢查是否是自已方的棋子、和傳出是哪一顆棋子以 便移動動作完成後去作棋步是否合法的檢查。

(15)

在 mouseDrag 中,邊拖曳時必需 repaint ,這邊我們只重貼以 棋子為中心長寬個 90,就是下面的程式: repaint(x-30,y-30,90,90); 這樣可以避免整個畫面因為 repaint 過於頻煩而產生閃爍的感覺。 最後,在 mouseUp 中,同樣的經過 find_which 函式找出棋盤上 的點,然後去檢查是否按下和放開的點是同樣一個點,也就是原地不 動這項,原地不動代表著玩家放棄了移動這顆棋子的想法,而去移動 別顆棋。如果兩個點不同,則將 棋子的種類、移動前的點、移動後 的點送出檢查,如檢查合法,則完成移動並檢查是否有 將軍對將軍 的情況產生,如果有,則遊戲結束,如果沒有則將移動的訊息送給對 方,並將畫可重新貼過。

(16)

3.4 走法檢查

象棋中,包函了:將、士、象、車、馬、包和卒等七種不同類形 的棋子,每種的走法都不盡相同,所以我們利用 switch(k) 也就是先 檢查 kind 這一項,將、士、象、車、馬、包、卒 分別是 kind 的 1、 2、3、4、5、6、7,然後再去檢查棋步是否合法。 將、士、象,都屬於不能過河的棋子,所以並沒有去檢查是否有 吃掉對方的將,檢查步驟較其他的棋子少了一步,車、馬、包和卒再 檢查移動是否合法時,也必需同時檢查是否吃掉了對方的將。

(17)

將:可上下左右移動一步,但只能在九宮中移動。

if(Math.abs(f_x - to_x) + Math.abs(f_y - to_y) == 1) {

if(to_y < 6 && to_y > 2 && to_x > 6) { if(tab[to_x][to_y].my_color == 0) { clip0.play(); return true; } clip7.play(); return false; } clip7.play(); return false; } 先檢查是否是往橫或直的方向移動一格,再去檢查移動完是否還在九 宮格內,最後去檢查要目地的點是否有自已方的棋子,如果都合法則 傳回移動完成,並送出移動音效。

(18)

士:只能在九宮格中的斜線中往斜向移動一格。

if(Math.abs(f_x - to_x) == 1 && Math.abs(f_y - to_y) == 1) {

if(to_y < 6 && to_y > 2 && to_x > 6) { if(tab[to_x][to_y].my_color == 0) { clip1.play(); return true; } clip7.play(); return false; } clip7.play(); return false; } 先檢查是否是往斜向的方向移動一格,再檢查移動完後是否還在九宮 格內,最後去檢查要目地的點是否有自已方的棋子,如果都合法則傳 回移動完成,並送出移動音效。

(19)

象:只能移動田字形,並且不能過河,且不能憋象眼。

if(Math.abs(f_x - to_x) == 2 && Math.abs(f_y - to_y) == 2) {

if(( (to_x > f_x) && (to_y > f_y) && tab[f_x+1][f_ y+1].state == 0 ) || ( (to_x > f_x) && (to_y < f_y) && tab[f_x+1][f_y-1].state == 0 ) || ( (to_x < f_x) && (to_y > f_y) && tab[f_x-1][f_y+1].state == 0 ) || ( (to_x < f_x) && (to_y < f_y) && tab[f_x-1][f_y-1].state == 0 ) ) { if(tab[to_x][to_y].my_color == 0) { if(to_x > 4) { clip2.play(); return true; } clip7.play(); return false; } clip7.play(); return false; } clip7.play(); return false; } 先檢查是否是移動田字,再檢查是否有憋象眼,然後檢查要目地的點 是否有自已方的棋子,最後檢查是否有過河,如果都合法則傳回移動 完成,並送出移動音效。

(20)

馬:走日字形,但不能拐馬腳。

if( (Math.abs(f_x - to_x) == 1 && Math.abs(f_y - to_y) == 2) || (Math.abs(f_x - to_x) == 2 && Math.abs(f_y - to_y) == 1) ) {

if(( (to_x - f_x)== 1 && (to_y - f_y)== 2 && tab[f_x][f_y+1].state == 0 ) || ( (to_x - f_x)== 1 && (to_y - f_y)==-2 && tab[f_x][f_y-1].state == 0 ) || ( (to_x - f_x)==-1 && (to_y - f_y)== 2 && tab[f_x][f_y+1].state == 0 ) || ( (to_x - f_x)==-1 && (to_y - f_y)==-2 && tab[f_x][f_y-1].state == 0 ) || ( (to_x - f_x)== 2 && (to_y - f_y)== 1 && tab[f_x+1][f_y].state == 0 ) || ( (to_x - f_x)== 2 && (to_y - f_y)==-1 && tab[f_x+1][f_y].state == 0 ) || ( (to_x - f_x)==-2 && (to_y - f_y)== 1 && tab[f_x-1][f_y].state == 0 ) || ( (to_x - f_x)==-2 && (to_y - f_y)==-1 && tab[f_x-1][f_y].state == 0 ) ) {

if(tab[to_x][to_y].my_color == 0) {

if (tab[row][col].kind == 1 && tab[row][col].my_color == 0) { send_state(from_x,from_y,row,col); gameover = 1; clip8.play(); } clip4.play(); return true; } clip7.play(); return false; } clip7.play(); return false; } 先檢查是否走的是日字形,再檢查是否拐馬腳,然後檢查目地的點是 否有自已方的棋子,最後檢查是否有將對方的將軍吃掉的情況。如果 吃掉對方將軍,則送出訊息並把 gameover 參數設為 1 並送出結束 的音效,如果沒有則送出移動音效。

(21)

車:能往直或橫的移動,沒有限定移動的格數。

if(f_x - to_x == 0 && to_y > f_y + 1) {

for(int i = to_y - 1;i > f_y ;i--) { if (tab[f_x][i].state != 0) retur n false; } if(tab[to_x][to_y].my_color == 0) {

if (tab[row][col].kind == 1 && tab[row][col].my_color == 0) { send_state(from_x,from_y,row,col); gameover = 1; clip8.play(); } clip3.play(); return true; } return false; } 以往右移動數格的程式碼作例子,先檢查原始點和目地點中間是否有 其他的棋子,如果有,則是非法的移動,如果沒有則去檢查目地點是 否有自已方的棋子,最後再去檢查是否有吃掉對方的將軍。 如果是往直或橫移動一格的話,檢查方法和將類似。

(22)

包:能往直或橫移動,沒有限定格數,但在要吃掉對方棋子時,中間 必需要有一顆棋子作媒介。

if(f_x - to_x == 0 && to_y > f_y + 1) {

for(int i = to_y - 1;i > f_y ;i--) { if (tab[f_x][i].state != 0) count++; } if (count == 1) {

if(tab[to_x][to_y].state == 1 && tab[to_x][to_y].my_color == 0) {

if (tab[row][col].kind == 1 && tab[row][col].my_color == 0) { send_state(from_x,from_y,row,col); gameover = 1; clip8.play(); } clip5.play(); return true; } clip7.play(); return false; } if (count == 0) { if(tab[to_x][to_y].state == 0) { clip5.play(); return true; } } clip7.play(); return false; } 以往右移動為例,先檢查原始點和目地點中間的棋子數,假如棋子數

(23)

行。假如棋子數為 0 的話,那就是普通移動的狀態,那目地點就不 能有任何的棋子才行。

(24)

卒:在自已的領地中,只能往前不能後退;在對方的領地中,可以往 前、右或左,也不能往後走。一次只能移動一格。

if((f_x == 5 || f_x ==6) && to_y - f_y ==0 && f_x - to_x == 1) { if(tab[to_x][to_y].my_color == 0) { clip6.play(); return true; } clip7.play(); return false; } 這是在自已領地中,只能往前的檢查程式,首先檢查是否只移動一 格,再檢查目地點是否有自已方的棋子。 if(f_x < 5) {

if(f_x - to_x == 0 && Math.abs(f_y - to_y) == 1) {

if(tab[to_x][to_y].my_color == 0) {

if (tab[row][col].kind == 1 && tab[row][col].my_color == 0) { send_state(from_x,from_y,row,col); gameover = 1; clip8.play(); } clip6.play(); return true; } }

if(f_y - to_y == 0 && to_x - f_x == -1) {

if(tab[to_x][to_y].my_color == 0) {

(25)

gameover = 1; clip8.play(); } clip6.play(); return true; } } clip7.play(); return false; } 這是在對方領地中的檢查程式,檢查是否是往前、右或左移動一格, 再來檢查目地點是否有自已方的棋子。

(26)

3.5 遊戲結束檢查

在象棋遊戲中,一場遊戲的結束有二種情況,第一就是一方吃掉另一 方的將軍,而第二種情況就是將軍對將軍(也就是雙方的將軍在同一 條直線上,且中間沒有其他的棋子),吃掉另一方的將軍的情況,我 們在棋步走法是否合法裡面就順便檢查了是否有吃掉對方的將軍這 一項了,剩下將軍對將軍的情況,我們建立了一個 class :checkKind1() 在 mouseUp 裡,檢查完棋步是否合法後去呼叫這個 class 做將軍對 將軍的檢查,檢查的方法分成二種狀況:第一種就是移動的棋子是將 軍的時後,這時必需用目地點去做檢查,先檢查目地點的直線上面是 否同時存在有二顆將軍的棋子,如果同時存在二顆將軍則去檢查將軍 和將軍中是否有其他的棋子,如果沒有則遊戲就結束了;另一種情況 就是移動的棋子並不是將軍,這時後就要用起始點去作檢查,先去檢 查起始點是不是在九宮格的直線上,也就是我們棋盤上 y = 3,4,5 這三條直線上,如果是在這三條直線上再去檢查他是否是橫向的移 動,如果是往橫的移動則去檢查起始點的直線上是否有二顆將軍,然 後再去檢查二顆將軍中間是否有其他的棋子,如果沒有則遊戲結束。

(27)

3.6 音效

關於音效部分,總共包含了七種棋子的移動音效、錯誤音效和遊戲結 束的音效,先在 index.html 裡面作讀檔的動作:

<applet code="netchess.class" height=400 width=360 clip0= "將.wav" clip1= "士.wav" clip2= "象.wav" clip3= "車.wav" clip4= "馬.wav" clip5= "包.wav" clip6= "兵.wav" clip7= "錯誤.wav" clip8= "贏.wav"> </applet> 然後在 init() 也就是 applet 一開始執行的地方作宣告: String fileName0 = getParameter("clip0");

clip0 = getAudioClip(getDocumentBase(),fileName0);

String fileName1 = getParameter("clip1");

clip1 = getAudioClip(getDocumentBase(),fileName1); …

… .. … … .

String fileName7 = getParameter("clip7");

clip7 = getAudioClip(getDocumentBase(),fileName7);

String fileName8 = getParameter("clip8");

clip8 = getAudioClip(getDocumentBase(),fileName8);

從 clip0 到 clip8 共九個,在棋步檢查中如果檢查移動合法,則會送 出各個棋子的移動音效,如果檢查不合法,則送出錯誤音效,遊戲結 束的音效則是在吃掉對方的將軍或將軍對將軍的情況下送出。

(28)

3.7 訊息的傳送與接收

關於棋步資訊的傳送,因為我們自訂了九十個 chess_tab 的物件, 而每個物件紀綠了目前有無棋子、棋子種類… 等,故我們傳送四個 data, 分別為移動前的座標及移動後的座標,因為要考慮到對方收到 棋子時,畫面是更新上方,也就是說 client 都是移動下方的棋子,所 以要對棋子的座標做轉換,以及棋子顏色的轉換: 傳送

public void send_state(int f_r,int f_c,int t_r,int t_c) { lock = 1; int from_row,from_col,to_row,to_col; from_row = f_r; //來源座標 from_col = f_c; // 來源座標 to_row = t_r; //目的座標 to_col = t_c; //目的座標 try{

StringBuffer sss=new StringBuffer(); sss.append("$m"); sss.append(9-from_row ); sss.append(8-from_col); sss.append(9-to_row); sss.append(8-to_col); sss.append("\n");

(29)

}catch (Exception e){}; }

接收

public void receive_state(String x) { int i; for (i=0;i<4;i++) { if(i==0) { if (x.charAt(i)=='0') from_x = 0; else if (x.charAt(i)=='1') from_x = 1;

else if (x.charAt(i)=='2') from_x = 2; else if (x.charAt(i)=='3') from_x = 3; else if (x.charAt(i)=='4') from_x = 4; else if (x.charAt(i)=='5') from_x = 5; else if (x.charAt(i)=='6') from_x = 6; else if (x.charAt(i)=='7') from_x = 7; else if (x.charAt(i)=='8') from_x = 8; else if (x.charAt(i)=='9') from_x = 9; }

else if(i==1) {

if (x.charAt(i)=='0') from_y = 0; else if (x.charAt(i)=='1') from_y = 1; else if (x.charAt(i)=='2') from_y = 2; else if (x.charAt(i)=='3') from_y = 3; else if (x.charAt(i)=='4') from_y = 4; else if (x.charAt(i)=='5') from_y = 5; else if (x.charAt(i)=='6') from_y = 6; else if (x.charAt(i)=='7') from_y = 7; else if (x.charAt(i)=='8') from_y = 8; }

(30)

{

if (x.charAt(i)=='0') row = 0; else if (x.charAt(i)=='1') row = 1; else if (x.charAt(i)=='2') row = 2; else if (x.charAt(i)=='3') row = 3; else if (x.charAt(i)=='4') row = 4; else if (x.charAt(i)=='5') row = 5; else if (x.charAt(i)=='6') row = 6; else if (x.charAt(i)=='7') row = 7; else if (x.charAt(i)=='8') row = 8; else if (x.charAt(i)=='9') row = 9; }

else if(i==3) {

if (x.charAt(i)=='0') col = 0; else if (x.charAt(i)=='1') col = 1; else if (x.charAt(i)=='2') col = 2; else if (x.charAt(i)=='3') col = 3; else if (x.charAt(i)=='4') col = 4; else if (x.charAt(i)=='5') col = 5; else if (x.charAt(i)=='6') col = 6; else if (x.charAt(i)=='7') col = 7; else if (x.charAt(i)=='8') col = 8; } } int k=tab[from_x][from_y].kind; switch (k) { case 1: clip0.play(); break; case 2: clip1.play(); break;

(31)

clip2.play(); break; case 4: clip3.play(); break; case 5: clip4.play(); break; case 6: clip5.play(); break; case 7: clip6.play(); break; } tab[row][col].pic_num = tab[from_x][from_y].pic_num; tab[from_x][from_y].pic_num = 0; tab[row][col].state = 1; tab[from_x][from_y].state= 0; tab[row][col].my_color = 0; repaint(); checkKind1();

if (tab[row][col].kind == 1 && tab[row][col].my_color == 0) { send_state(from_x,from_y,row,col); gameover = 1; clip8.play(); } tab[row][col].kind = tab[from_x][from_y].kind; tab[from_x][from_y].kind = 0; repaint(); }

(32)

座標轉換 這裡用到的技巧就是: sss.append(9- from_row ); sss.append(8- from_col); sss.append(9-to_row); sss.append(8-to_col); 也就是說,假設已方的炮位於第六列,第二行,那麼對應到對方的棋 盤則是第 9-6=3 列 ,第三 8-2= 6 行。 棋子顏色的轉換 寫一 change_color 副程式將存圖片陣列內的圖片調換 public void change_color()

{

int i;

Image temp[]= new Image[33]; for(i=1;i<8;i++) { temp[i] = pic[i+10]; } for(i=11;i<18;i++) { temp[i] = pic[i-10]; } for(i=1;i<18;i++) { pic[i] = temp[i]; } repaint(); }

(33)

第四章 Server 端程式

此章介紹伺服器端的程式,因為伺服端要能處理多人連線,因此需使 用 Thread 的機制,對每一連線用戶端建立獨立的執行緒。另外,伺 服端需能處理各種預先定義的指令與遊戲邏輯,並且能將訊息分送至 所有的連線 client 端。

4.1 流程圖

建立 server socket 等待及接受用戶端連線 no 檢查是否有二個 client 連上 yes 此二 client 建立訊息 table 傳送棋步給對方 遊戲結束

(34)

4.2 建立 server socket

建立伺服端 socket,以等候用戶端之連線。首先程式建立

serverSocket 物件類別,並指定 “4444”為其 port,以建立伺服端 Socket 其程式為:

import java.net.ServerSocket ; . . .

public class gameserver { static ServerSocket SS ;

public static void main(String args[]) { SS = new ServerSocket (4444) ; . . . } }

4.3 接受用戶端連線

欲接受用戶端的連線請求,使用 ServerSocket 的 accept : Socket theSocket = SS.accept ( ) ;

由於伺服端需能夠處理多人連線,因此程式中自定 client_data 與 ServerThread 類別,用來儲存及處理各別用戶端連線之程序,其中 ServerThread:

public ServerThread(Socket m,int me,client_data[] mp_client,table_data[] mp_table) throws IOException

{ int g; mySocket = m; myId = me; client = mp_client; table = mp_table;

(35)

datain = new DataInputStream (mySocket.getInputStream()); dataout = new DataOutputStream(mySocket.getOutputStream()); }

client_data 用以定義用戶端連線之各項參數設定: class client_data

{

ServerThread thread; static int Idle=0; static int playing=1; int myId; int otherId; int mode;

int table_id;

client_data(int id) throws IOException { myId=id;otherId=0; mode=Idle; table_id=-1; } } 當伺服端接收用戶端連結時,則分別建立各別之 Thread 以區隔各用 戶端:

client[g] = new client_data(g);

client[g].thread = newServerThread(theSocket,g,client,table); client[g].thread.start();

4.4 處理用戶端相關指令並回傳至另一 client 端

(36)

// 接收

datain = new DataInputStream ( mySocket.getInputStream( )) ;

// 回傳

dataout = new DataOutputStream( mySocket.getOutputStream( ));

取得用戶端指令:

String x = new String( ) ; X = datain.readLine( ) ;

由 server 端傳送資訊至各用戶端,利用 DataOutputStream 的方法, 方法有很多,我們這邊用的是 writeBytes 及 flush 例如:

client[I].thread.dataout.writeBytes( … ); client[I].thread.dataout.flush( ) ;

(37)

第五章 專題製作心得與總結

5.1 嗚謝

在這裡非常感謝一路指導我們的指導教授黃秋煌老師,說真的 因為我們二個原本是程度不是很好的學生,對於第一次要寫這個上千 行的程式真的一點把握也沒有,當此專題要開始 coding 時,真的不 知如何下手,幸好老師您對我們不放棄,就像聖? 老公公般並且對我 們很有耐心的鼓勵與不厭其煩的指導其方向,才有現今些許的成果, 在此特別致上我們無上的謝意。

5.2 製作過程所遇到的困難

對我們而言,整個過程所遇到的困難真的相當的多,因為基礎 不好,而且之學又沒學過 Java 更別提 Java Socket,所以整個專題的 製作真可說是從零開始,從無至有,所以先前的準備工作就是念書念 書念書,直到有點概念後開始想要如何去訂義整個棋盤、棋子、棋步 羅輯,直到網路的連線,由其是 Socket 部份,因為從來沒有學過這 方面的東西,因此用錯悟嘗試法花了不少的時間去研究,雖然不是功 能很大,但也算是個成品了。 APPLET 部份

(38)

一個很大的問題就是,當在移動棋子時要 repaint();,但是這樣造成整 個畫面嚴重的閃動,我們想辦法要解決這個問題,而書本上卻找不到 解答,最後總算在網路上找到如何在限定的範圍內作畫面重畫,才解 決此問題。 Server 端部份 因為書中 socket 介紹的範例都是小範例,簡單的二端互傳資料, 若要應用到象棋的程式,的確花了不小的工夫,一開始我只先寫一個 小程式,測試 Server 能收到 client 所發出的訊息,解決此問題後再來 就是面對要如何用 thread 來處理多人連線?以及對奕者二人的資訊互 傳,在此時才真正感受到物件導向的威力與重要性啊!!!

5.3 心得與感想

在大學四年裡,真的難得有一次這樣的機會接觸一個大型的程 式,並且和同學一起合作,這專題真的讓自已進步不少,回想著從一 開始的迷迷糊糊到成果的發表,這一路上的努力都不是白費的,從中 不但應用了大學四年所學的理論,也建立了我們對於問題的解決能 力,而 Java 又是現今熱手的語言,本來一竅不通,現在卻已小有心 得,這些收獲都不是當初預期的,所以對於能有這些收獲,真的非常

(39)

之前看過一些上千行的程式,不過到頭來都是看不懂居多,實在 沒有想到有一天自已也得動手寫上這樣一個大形的程式,從著手開始 都遇到了不少的困難,尤其 java 語言之前並不是很熟悉,所以很多 地方都還是得從基本的地上開始學習,從這個專題裡,不僅讓我學到 了和同組的同學作溝通,並且學習把一個程式分工進行的方法,這是 之前程式作業從頭到尾通通自已來所沒辦法學到的。 書豪

參考資料

Java2 徹底研究 Steven Holzner 張裕益、劉春成譯 Java 如何設計 Java 程式 林邦傑

Java 網際網路程式設計(TCP/IP 與 Internet Programming 篇 黃嘉輝編著

參考文獻

相關文件

機器人、餐飲服務、花藝、雲端運算、網路安全、3D 數位遊戲藝術、旅 館接待、行動應用開發、展示設計、數位建設

(A)因為用 Terminal Services 可以不用安裝 ERP 的程式在 Client 端上可以減少 MIS 維護系 統的時間(B)沒有防毒軟體 (C)建置防火牆的系統 (D) APP-Server 與 DB

● 每間學校訂購 myTV SUPER 應用程式版 /網頁版 通行證最 低限額: 50張。.. 1 OTT 網路串流平台

熟悉 MS-OFFICE

例如 : http ( 網頁伺服器所用的協定 ) 定義了 client 如何向 server request 網頁及 server 如何 將網頁及其中的各種內容回傳給 client 。. 提供服務給 application layer

熟悉 MS-OFFICE

 一般我們如過是透過分享器或集線器來連接電腦 的話,只需要壓制平行線即可(平行線:兩端接 頭皆為 EIA/TIA 568B ), 如果是接機器對機器 的話,需要製作跳線( Crossover :一端為

雜誌 電台 數碼廣播 期刊 漫畫 電影 手機短訊 圖書 手機通訊應用程式 即時通訊工具 網路日誌(blog) 車身廣告 霓虹燈招牌 電子書