版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領
文檔簡介
緒論1.1課題研究背景 現在人們壓力日益增大,人們需要勞逸結合也獲得更高的工作效率,工作之余的娛樂對每一個人來說都是必不可少的放松方式。五子棋游戲簡單易學同時具有很大奧妙性,也非常富有趣味性與消遣性。在經過不斷發(fā)展后,五子棋已經成為棋盤娛樂的重要的組成部分,它能夠開發(fā)智力、增強思維能力,是人們休閑娛樂的不二的選擇。而網絡互動娛樂是近幾年迅速發(fā)展起來的新的娛樂形式,成為IT產業(yè)中增長最快的娛樂形式之一。 網絡五子棋游戲很多是使用Java語言開發(fā)的一款游戲。它使用SOCKET建立連接,多線程處理數據,這些特點使這款游戲無論是客戶端還是服務器的實現都相對容易。1.2選題的前提和目的隨著計算機網絡技術的不斷發(fā)展,網絡游戲已經成為最具潛力的熱點領域。各種各樣的游戲使人眼花繚亂,而特別是其中一些休閑益智類的棋牌游戲,集娛樂性、趣味性、益智性和互動性于一體,擁有很大的用戶。棋類游戲能鍛煉人的思維,起到修身養(yǎng)性的作用。五子棋游戲可以分為兩類:一類是單機版五子棋游戲,另一類是網絡五子棋游戲。隨著計算機網絡和JAVA技術的不斷發(fā)展,使用Java開發(fā)網絡五子棋游戲將是一個實用性很強的畢業(yè)設計項目。網絡五子棋游戲的總體功能是要設計出具有精美界面的,具備人工智能的,支持網絡對弈的五子棋游戲。本系統(tǒng)最終的目的是建立一個有具體規(guī)則的網絡五子棋平臺,使兩臺不同計算機的使用者通過網絡連接,達到網絡對弈的目的。1.3五子棋簡要介紹五子棋相傳起源于四千多年前的堯帝時期,比圍棋的歷史還要悠久,可能早在“堯造圍棋”之前,民間就已有五子棋游戲。有關早期五子棋的文史資料與圍棋有相似之處,因為古代五子棋的棋具與圍棋是完全相同的。在上古的神話傳說中有“女媧造人,伏羲做棋”一說,《增山海經》中記載:“休輿之山有石焉,名曰帝臺之棋,五色而文狀鶉卵。”李善注引三國魏邯鄲淳《藝經》中曰:“棋局,縱橫各十七道,合二百八十九道,白黑棋子,各一百五十枚”。這段雖沒明講是何種棋類,但至少知道遠古就以漂亮的石頭為棋子。因而規(guī)則簡單的五子棋也可能出自當時,并是用石子作棋子。亦有傳說,五子棋最初流行于少數民族地區(qū),以后漸漸演變成圍棋并在炎黃子孫后代中遍及開來。在古代,五子棋棋具雖然與圍棋相類同,但是下法卻是完全不同的。正如《辭海》中所言,五子棋是“棋類游戲,棋具與圍棋相同,兩人對局,輪流下子,先將五子連成一行者為勝”。1.4主要完成內容本課題為了分析了五子棋規(guī)則及技巧,以及研究客戶端界面的布局,決定用Java開發(fā)五子棋游戲。主要完成客戶端界面設計和網絡對戰(zhàn)2個功能模塊??蛻舳素撠熓占脩舻男畔⒑徒缑婢S護,以及錯誤處理;服務器的建立以及對戰(zhàn)用戶的基本信息和兩個對戰(zhàn)用戶的棋盤信息,判斷輸贏。2JAVA開發(fā)環(huán)境2.1JAVA開發(fā)工具Java的開發(fā)工具很多,比如JBuilder、Eclipse、NetBeansIDE、JCreator等等,不同的工具可能對開發(fā)不同的程序有針對的優(yōu)勢。我選擇Eclipse進行編寫,我們說一下Eclipse的優(yōu)勢。★Eclipse是一種具有可擴展的開放源代碼IDE。Eclipse可以在同一IDE中集成了來自不同供應商的工具,并實現了工具之間的相互操作,從而顯著簡化了項目工作流程?!顴clipse框架的靈活性來自于它的擴展點。在XML中定義的已知接口,并充當插件的耦合點。擴展點的范圍包括從簡單字符串,到一個Java類的描述。★Eclipse的最大特點是它能接受由Java開發(fā)者自己編寫的開放源代碼插件。2.2Java簡介 Java是一種簡單的、分布式的、面向對象的、可移植、解釋的、安全的的多線程語言。它以其平臺無關性、硬件結構無關性、強安全性、面向對象、語言簡潔的特點,在網絡編程語言中占據了無可比擬的優(yōu)勢,成為實現電子商務系統(tǒng)的首選語言。2.3Java的起源和發(fā)展 Java是Sun公司在1995年推出的新興的編程語言,它是一種跨平臺的、應用于當前高速發(fā)展的網絡的編程語言。在編程語言中,可認為Basic語言促使了C語言的出現,C語言促進了C++的出現,而C++又促使了Java語言的出現。 自從Java正式推出后,以它特有的優(yōu)勢迅速發(fā)展,經過幾年的發(fā)展,Java已經在軟件開發(fā)等領域占有很大的市場。Java分為三種:J2SE、J2EE和J2ME。J2SE是Java平臺標準版[3],主要應用于桌面程序和Java小應用程序開發(fā);J2EE主要用于企業(yè)級開發(fā)和大型網站的開發(fā);J2ME主要用于手機等移動設備程序的開發(fā)。2.4Java特點★平臺無關性Java的平臺無關性能讓這種軟件在每個平臺上都能正常地運行,就是"只要寫一次程序,在任何地方、任何時間該程序永遠都能夠運行"。Java解釋器生成與體系無關的字節(jié)碼指令,這些指令對應于Java虛擬機里表示,Java解釋器得到字節(jié)碼后,對它進行轉換,只要安裝Java運行系統(tǒng),Java就可以在任何處理器上運行。 ★簡單性 Java語言是一種面向對象的語言,它通過提供最基本的方法來完成指定的任務,開發(fā)者只要知道一些概念就能夠編寫一些應用程序。★多線程 設計Java的多線程,是為了滿足人們對創(chuàng)建交互式網上程序的需要。用Java編寫出來的應用程序可以同時執(zhí)行很多任務。多線程機制使應用程序可以并行執(zhí)行,而且同步機制保證了對共享數據的正確操作。★健壯性 Java語言被病毒感染和破壞得最少。Java丟棄了難學和危險的指針功能,而大部分病毒程序常用的方法就是通過巧妙地運用地址變量如指針來獲取計算機的資源,從而使程序不安全。★面向對象語言 Java的設計集中于對象及其接口,它提供了簡單的類機制以及動態(tài)的接口模型。對象中封裝它的狀態(tài)變量和相應的方法,實現了模塊化和信息的隱藏;而類則是提供對象的原型,并且通過繼承的機制,子類可以使用父類所提供的方法,以實現代碼的復用。 ★自動內存管理也可以說自動內存管理是Java健壯性的體現,內存管理是很多種應用程序內的很關鍵因素。在網絡上的其他地方讀取大量的數據,之后把該數據寫入硬盤上的數據庫內,一般的設計就是把數據讀入內存中的某些集合內,對這些數據執(zhí)行某些操作,之后把數據寫入數據庫。在數據寫入數據庫后,在下一批處理之前,臨時存儲數據的集合必須清空舊數據,或被刪除后再建。這種操作可能執(zhí)行很多次,很多語言要手工清空或刪除集合數據結構。Java的自動內存管理[4]正好解決這一點,它使程序員不用再為內存管理寫大量的代碼。2.5JavaSocket網絡編程簡介2.5.1JavaSocket網絡編程基礎網絡編程,簡單的理解就是兩臺計算機相互通信。其基本模型就是客戶機/服務器模型,也就是通信雙方中的一方必須提供一個固定的端口,而另一方則只需要知道這個端口,并去建立兩者之間的聯系,然后完成數據交換。這里提供固定位置的一方通常稱為服務器,而建立聯系的一方通常稱為客戶端[5]?;诳蛻魴C/服務器的Socket通信模型如下圖所示:接收服務結果向服務器發(fā)出服務請求向服務器發(fā)送連接請求將處理結果反饋給客戶端解釋并處理請求信息接收用戶請求信息阻塞,等待建立連接在Socket上監(jiān)聽客戶端請求在制定服務器端公認的地址和端口服務器端創(chuàng)建Socket對象創(chuàng)建Socket對象客戶端建立連接請求信息反饋信息接收服務結果向服務器發(fā)出服務請求向服務器發(fā)送連接請求將處理結果反饋給客戶端解釋并處理請求信息接收用戶請求信息阻塞,等待建立連接在Socket上監(jiān)聽客戶端請求在制定服務器端公認的地址和端口服務器端創(chuàng)建Socket對象創(chuàng)建Socket對象客戶端建立連接請求信息反饋信息圖2.3.1-1基于客戶機/服務器的Socket通信模型圖 Java為這個模型的實現提供了簡化的Socket編程接口。在程序中只要導入java.io包就可以方便的使用java的Socket編程接口。Java中Socket通信模型如下圖所示:ServerSocketss(port#)ServerSocketss(port#)ss.accept()//等待連接ServerOutputStreamInputStreamss.close()OutputStreamInputStreams.close()Sockets(host,port#)//請求連接服務器Client圖2.3.1-2Java中的Socket編程模型圖2.5.2Socket編程的實現Socket類表示了系統(tǒng)的IP地址和端口號的結合,可以理解為客戶端或者服務器端的一個特殊對象。它包含兩個處理流的方法,一個是getInputStream(),另一個是getOutputStream(),分別用來獲得網絡的輸入流和輸出流。構造Socket對象的代碼如下: Sockets=newSocket(IP,port); IP,port分別為服務器端的IP地址和服務器端的端口號。 ServerSocket類是一個專門建立Socket服務器的類,它可以用服務器需要使用的端口號作為參數來創(chuàng)建ServerSocket對象。示例代碼如下: ServerSocketss=newServerSocket(TCP_PORT); 當一個客戶端程序建立一個Socket連接,所連接的端口號為對應TCP_PORT時,服務器對象ss便響應這個連接,在用上述ss對象調用accept()創(chuàng)建一個代表服務器的Socket對象。創(chuàng)建后服務器便可以利用這個Socket對象與客戶端進行數據交換。示例代碼如下: SocketclientSocket=ss.accept();在本系統(tǒng)的實現過程中,在支持網絡對弈的服務器類的定義中需要使用ServerSocket類來響應多個客戶端的連接請求。能夠同時建立多個服務器,玩家可以選擇不同的服務器參與游戲。2.6Java圖形編程Java中的Graphics類是一個工具類,用來繪圖和顯示格式化文本,Graphics類是在java.awt包中聲明的。在Java程序中繪圖必須在一個窗口或者是一個容器中進行,繪圖窗體經常被設計為一個組件容器。顯示格式化文本和繪圖是調用Graphics類的drawXX()方法完成的,例如drawString(String)等等。繪圖采用的坐標系[6]是原點在左上角,縱軸向下以像素為單位的坐標系。 3系統(tǒng)需求分析3.1用戶需求分析根據老師提出的要求,我們把系統(tǒng)的用戶分為兩類:普通用戶、系統(tǒng)管理員用戶,下面是我們對兩種用戶的需求進行的分析:3.1.1普通用戶需求根據分析,普通用戶具有的功能:(1)用戶登錄:用戶根據自己的密碼登錄本系統(tǒng)。(2)棋局回放:用戶根據自己的需求回放自己以前下過的棋局。(3)玩五子棋:用戶在點擊playgame后會進入游戲界面玩游戲。(4)悔棋功能:在下棋過程中,用戶根據自己的需求,選擇悔棋。(5)更改密碼:用戶可以根據自己的需求更改自己的密碼。3.1.2系級管理員需求系統(tǒng)管理員是本系統(tǒng)的最高權限的管理員,他不僅具有普通用戶的各種功能,還具有他獨有的功能一下是系統(tǒng)管理員所具備的功能:(1)用戶登錄:用戶根據自己的密碼登錄本系統(tǒng)。(2)棋局回放:用戶根據自己的需求回放自己以前下過的棋局。(3)玩五子棋:用戶在點擊playgame后會進入游戲界面玩游戲。(4)悔棋功能:在下棋過程中,用戶根據自己的需求,選擇悔棋。(5)更改密碼:用戶可以根據自己的需求更改自己的密碼。(6)管理普通用戶:可以對普通用戶進行添加和刪除。3.1.3系統(tǒng)用例圖圖3-1-3系統(tǒng)用例圖圖3-1-3系統(tǒng)用例圖 3.2五子棋對弈系統(tǒng)的數據流圖數據流圖(DataFlowDiagram):簡稱DFD,它從數據傳遞和加工角度,以圖形方式來表達系統(tǒng)的邏輯功能、數據在系統(tǒng)內部的邏輯流向和邏輯變換過程,是結構化系統(tǒng)分析方法的主要表達工具及用于表示軟件模型的一種圖示方法。下面簡要介紹部分需求的數據流圖:3.2.1用戶登錄數據流圖(DFD)在用戶登錄過程當中,用戶輸入的信息經過加工處理后,通過對數據庫的查詢后返回給程序該用戶的權限,數據流圖(圖3-1)如下:用戶用戶接受用戶信息處理信息輸入信息查詢信息用戶信息表(UserInfo)用戶產生查詢結果權限信息權限信息圖3-1用戶登錄數據流圖3.2.2用戶界面顯示數據流圖(DFD)知道用戶權限后,通過查詢用戶游戲信息,顯示用戶游戲信息。數據流圖(圖3-2)如下:用戶用戶接受權限信息處理信息用戶每次游戲信息(UserDish)產生結果用戶權限查詢信息游戲信息游戲信息圖3-2普通用戶數據顯示數據流圖4系統(tǒng)的總體設計根據玩家的需求分析,本系統(tǒng)的實現主要用到如下幾個類: ★ChessServer:主要用于玩家相關信息的傳遞。 ★ChessClient:主要用于玩家信息的初始化。 ★NetClient:主要用于實現連接網絡玩家,通過此類玩家發(fā)送自己的游戲狀態(tài)以及接收并進行處理從服務器傳遞過來的信息。 ★ChessPad:主要用于繪制棋盤及棋子。4.1網絡編程的模式和選取網絡編程模式主要分成兩類:一種是基于C/S(客戶機/服務器)模式,另一種是B/S(瀏覽器/服務器)式;C/S程序具有好的交互性,功能強大,但是客戶端必須安裝客戶端軟件,限制了其應用;B/S模式下要求客戶端具有瀏覽器,但瀏覽器在安全方面有一些限制,交互性與功能有一些限制網絡五子棋系統(tǒng)應用,所以一般的網絡五子棋游戲都選用了C/S模式進行實現,使用TCP/IP方式利用Socket通過傳輸層提供服務。在Socket工作中,用ClientSocket類建立客戶端連接,用ServerSocket類建立服務器端連接,這兩個組件分別用來操縱客戶端和服務器端的Socket對象進行連接和傳輸數據。在通信過程中,服務器端應用程序先啟動,然后等待客戶端的連接請求。在另外一臺計算機上啟動客戶端應用程序,同時輸入服務器端Socket地址和端口號進行選擇服務器,找到服務器后就向服務器端發(fā)請求連接服務器端,允許連接后即向客戶端Socket收到Accept信號。客戶端和服務器端就正式建立起了后,就可以分別調用TserveRsocket和TClientSocket的Socket屬性和客戶端進行數據傳輸??蛻舳撕头掌鞫薙ocket組件必須設置相同的端口號。4.2主要類與其作用4.2.1服務器類服務器類用于接收客戶端的連接,并為每個客戶端在服務器端啟動唯一的守護線程,線程主要功能是接收客戶端發(fā)送過來的消息并做出相應的處理。客戶端發(fā)送到服務器端的主要消息有兩部分:一部分是客戶端發(fā)送給服務器端需要服務器端進行處理的,如客戶端發(fā)送過的連接信息、客戶端改名信息、客戶端斷開連接信息等;另一部分是客戶端發(fā)送給服務器端需要服務端轉發(fā)給其他客戶端的消息,如客戶端給游戲另一方發(fā)送的棋盤狀態(tài)數據、聊天信息等。服務器類的主要對象如表4.2.1-1:表4.2.1-1服務器主要對象功能對象名稱功能TCP_PORT:服務器端用于建立套接字的端口號,為了可靠的用戶連接,因此在服務器端定義了此端口號為TCP的,主要用于建立服務器端與客戶端的TCP連接。UDP_PORT:服務器端用于向客戶端轉發(fā)數據需要綁定的用戶數據報套接字的端口號,玩家發(fā)送給服務器需要轉發(fā)的消息時,服務器端的DatagramSocket就是使用此端口號建立的UDP連接。ClientDataHash:用于保存客戶端套接字和IP套接字地址的哈希表,服務器可以通過發(fā)送消息的客戶端Socket得到其IP套接字地址,將消息轉發(fā)出去。ClientNameHash:用于保存客戶端套接字和客戶端當前名字的哈希表,同樣是為了轉發(fā)客戶端消息的。ChessPeerHash:用于保存每一對開局玩家信息的哈希表,表中鍵是服務方,值是客戶方,每一對玩家需要交換的如棋盤狀態(tài)數據,倒計時信息大都依賴于此哈希表來實現。服務器類使用狀況如下圖:接受玩家接受玩家處理消息顯示狀態(tài)轉發(fā)消息服務器端圖4.2.1-1服務器類用況圖4.2.2客戶端主類游戲客戶端的主要功能是為了初始化和組織用戶界面,并且定義了各個按鈕的功能。游戲客戶端的主要對象如下如表4.2.2-1:表4.2.2-1客戶端對象功能對象名稱功能isGameConnected 用于判定玩家是否與服務器連接。isChess 用于判定玩家是否已經在開局游戲中。isServer 用于判定在此局開局游戲中,當前玩家是不是服務方,即主方。isClient 用于判定在此局開局游戲中,當前玩家是不是客戶方,即次方。ShiCi 該類的對象,用于在玩家啟動時動態(tài)地顯示五子棋詩的信息。Sing 類該的對象,用于為客戶端啟動音樂功能。游戲客戶端使用狀況如下圖:界面組織界面組織游戲控制信息交換玩家輸入游戲玩家圖4.2.2-1游戲客戶端用況圖4.2.3客戶端副類客戶端副類的主要功能是當前玩家與其他玩家進行對弈時,網絡客戶端處理需要發(fā)送和接收的消息??蛻舳烁鳖惖闹饕獙ο笕绫?.2.3-1:表4.2.3-1網絡端對象功能對象名稱功能r隨機函數類的對象,用于為每個客戶端啟動時,為每一個客戶端隨機分配一個端口號,玩家使用此端口號來接收信息。time當前玩家方的時間信息。初始化時間為3分鐘。otherTime當前開局游戲中對方的時間信息。初始時間為3分鐘。客戶端副類類用況圖如下:網絡對戰(zhàn)玩家網絡對戰(zhàn)玩家發(fā)送消息聊天消息更名消息建立游戲消息連接消息加入消息退出消息處理消息退出消息玩家退出棋盤狀態(tài)玩家列表倒計時圖4.2.3-1客戶端副類用況圖4.2.4棋盤類棋盤類的主要功能是繪制玩家的棋盤狀態(tài)與棋子信息。棋盤類的主要對象如表4.2.4-1:表4.2.4-1棋盤對象功能對象名稱功能board[][] 玩家所維護的棋盤二維數組。結果為1表示(x,y)位置為黑子,結果為2表示(x,y)位置為白子。withComputer 玩家與電腦對弈的標志。如果當前玩家是與電腦對弈,此變量值為真;否則為假。isMouseEnable 以此變量來控制在網絡對弈的雙方中,當前玩家是否處于可以落子狀態(tài)。因為網絡對弈要求當一玩家下完后,對方才能落子,因此當此變量為真時,玩家可以落子;當此變 量為假時,玩家不可以落子。isRegretEnable 判斷當前的悔棋按鈕是否可用。為真時,玩家可以悔棋;為假時,玩家不可以悔棋。isFull判斷當前的棋盤是否已經下滿,當棋盤下滿后,分別給游戲雙方一個提示,然后進行清盤操作。棋盤類用況如下圖:先手判斷先手判斷落子繪制棋盤勝負判斷玩家圖4.2.4-1棋盤類用況圖5系統(tǒng)模塊的詳細設計5.2客戶端界面設計 游戲客戶端主要用于玩家信息的初始化,玩家的操作狀態(tài)的定義,包括結構組織游戲客戶端的界面,實現定義游戲控制按鈕,完成定義鍵盤的事件。 主要方法: ★launchFrame()方法用在啟動玩家客戶端時,為用戶發(fā)送連接服務器信息,并且加載背景音樂等。 ★actionPerformed(ActionEvente)方法用于客戶端玩家單擊控制按鈕的功能的實現。該方法主要用來實現玩家修改名字、創(chuàng)建游戲、我要參賽、系統(tǒng)設置等各個按鈕的功能。 ★keyPressed(KeyEvente)方法用于實現聊天功能,當玩家輸入聊天信息到輸入框中后,按回車鍵可以發(fā)送消息。此事件中主要處理了兩個行為,一是當玩家選擇某玩家進行私聊時的處理,二是當用戶選擇所有人進行公聊時的處理??蛻舳祟惾鐖D5.2-1:圖5.2-1游戲客戶端類類圖5.3客戶端網絡設計 網絡客戶端主要用于接收和發(fā)送服務器消息。 主要方法如下: ★connect(String,int)用于服務器的連接。第一個參數是要連接的服務器的IP地址,第二個參數是連該服務器的端口號。玩家用此方法連接上服務器,玩家啟動一個線程用來接收服務器發(fā)送過來的消息。 ★restart()用于游戲重新開始,在此方法中要求將重新初始化信息,并且進行棋盤清理操作。 ★send(int,String)用于發(fā)送此玩家的消息狀態(tài)給服務器。第一個參數是消息類型,第二個參數是消息內容。消息類型有如下幾種如表5.3-1: 表5.3-1消息類型類型發(fā)送消息類型0代表修改客戶端名字1代表客戶端建立游戲2代表客戶端加入游戲3代表向游戲對方通告客戶端已加入4代表客戶端放棄游戲5代表客戶端對游戲對方發(fā)送過來的放棄操作進行斷開6代表游戲一方向另一方發(fā)送棋盤狀態(tài)7代表游戲一方向另一方發(fā)送時間信息 ★doMessage(DatagramPacket)用于接收從服務器端發(fā)送過來的消息。5.4棋盤類設計棋盤類主要用于繪制棋盤的?!飌aint(Graphics)方法用來繪制棋盤、棋盤的背景圖片和棋子位置。因為采用了雙緩沖技術,有效的消除加載圖片時的窗口閃爍?!颿learBoard()方法用于初始棋盤和一些變量的操作?!飐elect()方法設置其按鈕狀態(tài)的?!飅sFull()方法用于判斷當前棋盤是否已無法落子。如果當前棋盤已滿,則通知玩家此局游戲流局?!飆udge(int,int,int)方法用于判斷當前棋子落下時是否游戲勝負已分。如果游戲獲勝,則方法返回真值;否則返回假值。棋盤類圖如下所示:圖5.4-1棋盤類類圖5.5系統(tǒng)各模塊之間的關系ChessServer類,ChessClient類,NetClient類,ChessPad類等類之間的交互實現了系統(tǒng)的主要功能。服務器端模塊的實現,主要的類是ChessPad類和ChessClient類。ChessClient類實現了玩家單擊按鈕功能及界面的組織;ChessPad類用于繪制棋盤和玩家的落子位置。客戶端主要用到的是這三個類:ChessServer類、ChessClient類和NetClient類。服務器類和客戶端類用Socket編程實現的信息交換。其他功能都是在用戶連接服務器后由游戲客戶端類自動調用其類來實現的。各模塊關系如下圖所示:圖5.5-1各模塊關系圖6系統(tǒng)演示6.1打開服務器端圖6.1服務器端啟動部分關鍵代碼如下: publicServerThread(SocketclientSocket,HashtableclientDataHash, HashtableclientNameHash,HashtablechessPeerHash, ServerMsgPanelserver) { this.clientSocket=clientSocket; this.clientDataHash=clientDataHash; this.clientNameHash=clientNameHash; this.chessPeerHash=chessPeerHash; this.serverMsgPanel=server; }6.2棋盤的初始化圖6.2棋盤的初始化我們可以看到,我們只有在連接起服務器之后,才能夠進行游戲的初始化操作,否則;按鈕是灰色的。6.3用戶加入圖6.3加入2個對戰(zhàn)者從上圖我們可以看到:一旦有人加入,那么服務器端會立即得到提示。6.4用戶對戰(zhàn)圖6.4對戰(zhàn)狀態(tài)下的系統(tǒng)由上圖我們可以看到,一旦初始化工作完成,那么對戰(zhàn)系統(tǒng)就開始了工作;我們就可以在自己的電腦上看到2個窗口,分別是兩個用戶在對戰(zhàn)??偨Y通過幾個月的對軟件系統(tǒng)的設計和實現,一個能夠完成網絡對弈的五子棋游戲基本實現。全部的總結工作,具體概括如下: 論文首先研究對比了網絡五子棋游戲的具體優(yōu)勢,分析了C/S和B/S模式的各自的優(yōu)缺點。對應系統(tǒng)的具體要求,構建了基于C/S模式的網絡五子棋對戰(zhàn)的基本設計。 采用了網絡編程中的一種,Socket編程,分析了Socket通信的基本原理及實現。因此選擇了TCP協(xié)議作為通訊方式,為了確保數據傳輸的高效性、實時性和安全性,對玩家信息的交換及聊天信息都采用的是UDP協(xié)議進行傳輸。 完成了網絡五子棋對弈系統(tǒng)的總體的方案設計,以Java為編程語言基礎,以Eclipse為五子棋游戲的開發(fā)工具,完成了系統(tǒng)各模塊的設計,其中包括服務器模塊、客戶端模塊以及美化界面方面的設計。 雖然網絡五子棋這款游戲實現了基本的設計,但是需要改進的地方還有很多,我個人認為需要改進的地方有以下幾點: 可以在游戲初始界面加入一個游戲大廳的列表,這樣用戶在進入游戲時可以選擇可以看見的服務器列表,而省去了用戶直接輸入服務器的麻煩。 對游戲邏輯模塊進行優(yōu)化,進一步提高可擴展性,以適應將來新的需求和變化。致謝我不會忘記這難忘的幾個月的時間。畢業(yè)論文的制作給了我難忘的回憶。在我徜徉書海查找資料的日子里,面對無數書本的羅列,最難忘的是每次找到資料時的激動和興奮;親手設計方案的時間里,記憶最深的是每一步小小思路實現時那幸福的心情;為了論文我曾趕稿到深夜,但看著親手打出的一字一句,心里滿滿的只有喜悅毫無疲憊。這段旅程看似荊棘密布,實則蘊藏著無盡的寶藏。我從資料的收集中,掌握了很多網絡編程方面的知識,讓我對我所學過的知識有所鞏固和提高,并且讓我對當今JAVA對戰(zhàn)游戲的發(fā)展有所了解。在整個過程中,我學到了新知識,增長了見識。在今后的日子里,我仍然要不斷地充實自己,爭取在所學領域有所作為。腳踏實地,認真嚴謹,實事求是的學習態(tài)度,不怕困難、堅持不懈、吃苦耐勞的精神是我在這次設計中最大的收益。我想這是一次意志的磨練,是對我實際能力的一次提升,也會對我未來的學習和工作有很大的幫助。在這次畢業(yè)設計中也使我們的同學關系更進一步了,同學之間互相幫助,有什么不懂的大家在一起商量,聽聽不同的看法對我們更好的理解知識,所以在這里非常感謝幫助我的同學。在此更要感謝我的導師和專業(yè)老師,是你們的細心指導和關懷,使我能夠順利的完成畢業(yè)論文。在我的學業(yè)和論文的研究工作中無不傾注著老師們辛勤的汗水和心血。老師的嚴謹治學態(tài)度、淵博的知識、無私的奉獻精神使我深受啟迪。從尊敬的導師身上,我不僅學到了扎實、寬廣的專業(yè)知識,也學到了做人的道理。在此我要向我的導師致以最衷心的感謝和深深的敬意。最后再一次感謝所有在畢業(yè)設計中曾經幫助過我的良師益友和同學,以及在設計中被我引用或參考的論著的作者。參考文獻[1].張海藩編著《軟件工程導論》(第五版)[M]清華大學出版社2008[2]王小春編著《Pc游戲編程(人機博弈)》[M]重慶大學出版社2005[3]李寧等編著《JavaWeb開發(fā)技術大全》[M]清華大學出版社2009[4]賀松平編著《基于MVC模式的B/S架構的研究及應用》[M]武漢理工大學出版2004[5]耿祥義,張躍平編著《JSP實用教程》[M]清華大學出版社2003[6]吳其慶編著《Java程序設計實例教程》[M]冶金工業(yè)出版社,2006[7]馮博、應群:面向對象的Java網絡編程。清華大學出版社,2004.11,P152-P155[8]孫更新等:Java畢業(yè)設計指南與項目實踐,科學出版社,2008.03,P78-P88[9]單文仁:Java在我國的應用現狀和發(fā)展趨勢,科技成果縱橫,2007.02,P132-P155[10]孫衛(wèi)琴:JAVA面向對象編程,電子工業(yè)出版社,2006.07,P65-P69[11]朱福喜、黃昊著:Java項目開發(fā)與畢業(yè)設計指導,北京:清華大學出版社,2008.05,P54-P65[12]郭驊:Java網絡編程中的若干問題,電子與電腦,2007.11,P36-P40[13]雨楓技術教程網:五子棋算法探討,2009.09,P230-P235附錄:關鍵代碼服務器端packagecom.dikey.server;importjava.io.DataInputStream;importjava.io.DataOutputStream;importjava.io.IOException;import.Socket;importjava.util.Enumeration;importjava.util.Hashtable;importjava.util.StringTokenizer;publicclassServerThreadextendsThread{ SocketclientSocket;//保存客戶端套接口信息 HashtableclientDataHash;//保存客戶端端口與輸出流對應的Hash HashtableclientNameHash;//保存客戶端套接口和客戶名對應的Hash HashtablechessPeerHash;//保存游戲創(chuàng)建者和游戲加入者對應的Hash ServerMsgPanelserverMsgPanel; booleanisClientClosed=false; publicServerThread(SocketclientSocket,HashtableclientDataHash, HashtableclientNameHash,HashtablechessPeerHash, ServerMsgPanelserver) { this.clientSocket=clientSocket; this.clientDataHash=clientDataHash; this.clientNameHash=clientNameHash; this.chessPeerHash=chessPeerHash; this.serverMsgPanel=server; } publicvoiddealWithMsg(StringmsgReceived) { StringclientName; StringpeerName; if(msgReceived.startsWith("/")) { if(msgReceived.equals("/list")) {//收到的信息為更新用戶列表 Feedback(getUserList()); } elseif(msgReceived.startsWith("/creatgame[inchess]")) {//收到的信息為創(chuàng)建游戲 StringgameCreaterName=msgReceived.substring(20);//取得服務器名 synchronized(clientNameHash) {//將用戶端口放到用戶列表中 clientNameHash.put(clientSocket,msgReceived.substring(11)); } synchronized(chessPeerHash) {//將主機設置為等待狀態(tài) chessPeerHash.put(gameCreaterName,"wait"); } Feedback("/yourname"+clientNameHash.get(clientSocket)); sendGamePeerMsg(gameCreaterName,"/OK"); sendPublicMsg(getUserList()); } elseif(msgReceived.startsWith("/joingame")) {//收到的信息為加入游戲時 StringTokenizeruserTokens=newStringTokenizer(msgReceived,""); StringuserToken; StringgameCreatorName; StringgamePaticipantName; String[]playerNames={"0","0"}; intnameIndex=0; while(userTokens.hasMoreTokens()) { userToken=(String)userTokens.nextToken(""); if(nameIndex>=1&&nameIndex<=2) { playerNames[nameIndex-1]=userToken;//取得游戲者命 } nameIndex++; } gameCreatorName=playerNames[0]; gamePaticipantName=playerNames[1]; if(chessPeerHash.containsKey(gameCreatorName) &&chessPeerHash.get(gameCreatorName).equals("wait")) {//游戲已創(chuàng)建 synchronized(clientNameHash) {//增加游戲加入者的套接口與名稱的對應 clientNameHash.put(clientSocket, ("[inchess]"+gamePaticipantName)); } synchronized(chessPeerHash) {//增加或修改游戲創(chuàng)建者與游戲加入者的名稱的對應 chessPeerHash.put(gameCreatorName,gamePaticipantName); } sendPublicMsg(getUserList()); //發(fā)送信息給游戲加入者 sendGamePeerMsg(gamePaticipantName, ("/peer"+"[inchess]"+gameCreatorName)); //發(fā)送游戲給游戲創(chuàng)建者 sendGamePeerMsg(gameCreatorName, ("/peer"+"[inchess]"+gamePaticipantName)); } else {//若游戲未創(chuàng)建則拒絕加入游戲 sendGamePeerMsg(gamePaticipantName,"/reject"); try { closeClient(); } catch(Exceptionez) { ez.printStackTrace(); } } } elseif(msgReceived.startsWith("/[inchess]")) {//收到的信息為游戲中時 intfirstLocation=0,lastLocation; lastLocation=msgReceived.indexOf("",0); peerName=msgReceived.substring((firstLocation+1),lastLocation); msgReceived=msgReceived.substring((lastLocation+1)); if(sendGamePeerMsg(peerName,msgReceived)) { Feedback("/error"); } } elseif(msgReceived.startsWith("/giveup")) {//收到的信息為放棄游戲時 StringchessClientName=msgReceived.substring(8); if(chessPeerHash.containsKey(chessClientName) &&!((String)chessPeerHash.get(chessClientName)) .equals("wait")) {//勝利方為游戲加入者,發(fā)送勝利信息 sendGamePeerMsg((String)chessPeerHash.get(chessClientName), "/youwin"); synchronized(chessPeerHash) {//刪除退出游戲的用戶 chessPeerHash.remove(chessClientName); } } if(chessPeerHash.containsValue(chessClientName)) {//勝利方為游戲創(chuàng)建者,發(fā)送勝利信息 sendGamePeerMsg((String)getHashKey(chessPeerHash, chessClientName),"/youwin"); synchronized(chessPeerHash) {//刪除退出游戲的用戶 chessPeerHash.remove((String)getHashKey(chessPeerHash, chessClientName)); } } } else {//收到的信息為其它信息時 intlastLocation=msgReceived.indexOf("",0); if(lastLocation==-1) { Feedback("無效命令"); return; } } } else { msgReceived=clientNameHash.get(clientSocket)+">"+msgReceived; serverMsgPanel.msgTextArea.append(msgReceived+"\n"); sendPublicMsg(msgReceived); serverMsgPanel.msgTextArea.setCaretPosition(serverMsgPanel.msgTextArea.getText() .length()); } } //發(fā)送公開信息 publicvoidsendPublicMsg(StringpublicMsg) { synchronized(clientDataHash) { for(Enumerationenu=clientDataHash.elements();enu .hasMoreElements();) { DataOutputStreamoutputData=(DataOutputStream)enu.nextElement(); try { outputData.writeUTF(publicMsg); } catch(IOExceptiones) { es.printStackTrace(); } } } } //發(fā)送信息給指定的游戲中的用戶 publicbooleansendGamePeerMsg(StringgamePeerTarget,StringgamePeerMsg) { for(Enumerationenu=clientDataHash.keys();enu.hasMoreElements();) {//遍歷以取得游戲中的用戶的套接口 SocketuserClient=(Socket)enu.nextElement(); if(gamePeerTarget.equals((String)clientNameHash.get(userClient)) &&!gamePeerTarget.equals((String)clientNameHash .get(clientSocket))) {//找到要發(fā)送信息的用戶時 synchronized(clientDataHash) { //建立輸出流 DataOutputStreampeerOutData=(DataOutputStream)clientDataHash .get(userClient); try { //發(fā)送信息 peerOutData.writeUTF(gamePeerMsg); } catch(IOExceptiones) { es.printStackTrace(); } } returnfalse; } } returntrue; } //發(fā)送反饋信息給連接到主機的人 publicvoidFeedback(StringfeedBackMsg) { synchronized(clientDataHash) { DataOutputStreamoutputData=(DataOutputStream)clientDataHash .get(clientSocket); try { outputData.writeUTF(feedBackMsg); } catch(Exceptioneb) { eb.printStackTrace(); } } } //取得用戶列表 publicStringgetUserList() { StringuserList="/userlist"; for(Enumerationenu=clientNameHash.elements();enu.hasMoreElements();) { userList=userList+""+(String)enu.nextElement(); } returnuserList; } //根據value值從Hashtable中取得相應的key publicObjectgetHashKey(HashtabletargetHash,ObjecthashValue) { ObjecthashKey; for(Enumerationenu=targetHash.keys();enu.hasMoreElements();) { hashKey=(Object)enu.nextElement(); if(hashValue.equals((Object)targetHash.get(hashKey))) returnhashKey; } returnnull; } //剛連接到主機時執(zhí)行的方法 publicvoidsendInitMsg() { sendPublicMsg(getUserList()); Feedback("/yourname"+(String)clientNameHash.get(clientSocket)); Feedback("Java五子棋客戶端"); Feedback("/list--更新用戶列表"); Feedback("/<username><talk>--私聊"); Feedback("注意:命令必須對所有用戶發(fā)送"); } publicvoidcloseClient() { serverMsgPanel.msgTextArea.append("用戶斷開連接:"+clientSocket+"\n"); synchronized(chessPeerHash) {//如果是游戲客戶端主機 if(chessPeerHash.containsKey(clientNameHash.get(clientSocket))) { chessPeerHash.remove((String)clientNameHash.get(clientSocket)); } if(chessPeerHash.containsValue(clientNameHash.get(clientSocket))) { chessPeerHash.put((String)getHashKey(chessPeerHash, (String)clientNameHash.get(clientSocket)), "tobeclosed"); } } synchronized(clientDataHash) {//刪除客戶數據 clientDataHash.remove(clientSocket); } synchronized(clientNameHash) {//刪除客戶數據 clientNameHash.remove(clientSocket); } sendPublicMsg(getUserList()); serverMsgPanel.statusLabel.setText("當前連接數:"+clientDataHash.size()); try { clientSocket.close(); } catch(IOExceptionexx) { exx.printStackTrace(); } isClientClosed=true; } publicvoidrun() { DataInputStreaminputData; synchronized(clientDataHash) { serverMsgPanel.statusLabel.setText("當前連接數:"+clientDataHash.size()); } try { //等待連接到主機的信息 inputData=newDataInputStream(clientSocket.getInputStream()); sendInitMsg(); while(true) { Stringmessage=inputData.readUTF(); dealWithMsg(message); } } catch(IOExceptionesx){} finally { if(!isClientClosed) { closeClient(); } } }}客戶端packagecom.dikey.client;importjava.awt.*;importjava.awt.event.*;importjava.io.*;import.*;importjavax.swing.JTextField;publicclassWuZiQiPadextendsPanelimplementsMouseListener,ActionListener{ //鼠標是否能使用 publicbooleanisMouseEnabled=false; //是否勝利 publicbooleanisWinned=false; //是否在下棋中 publicbooleanisGaming=false; //棋子的x軸坐標位 publicintchessX_POS=-1; //棋子的y軸坐標位 publicintchessY_POS=-1; //棋子的顏色 publicintchessColor=1; //黑棋x軸坐標位數組 publicintchessBlack_XPOS[]=newint[200]; //黑棋y軸坐標位數組 publicintchessBlack_YPOS[]=newint[200]; //白棋x軸坐標位數組 publicintchessWhite_XPOS[]=newint[200]; //白棋y軸坐標位數組 publicintchessWhite_YPOS[]=newint[200]; //黑棋數量 publicintchessBlackCount=0; //白棋數量 publicintchessWhiteCount=0; //黑棋獲勝次數 publicintchessBlackVicTimes=0; //白棋獲勝次數 publicintchessWhiteVicTimes=0; //套接口 publicSocketchessSocket; publicDataInputStreaminputData; publicDataOutputStreamoutputData; publicStringchessSelfName=null; publicStringchessPeerName=null; publicStringhost=null; publicintport=4331; publicTextFieldstatusText=newTextField("請連接服務器!"); publicWuZiQiThreadfirThread=newWuZiQiThread(this); publicWuZiQiPad() { setSize(540,540); setLayout(null); setBackground(Color.pink); addMouseListener(this); add(statusText); statusText.setBounds(newRectangle(40,5,360,24)); statusText.setEditable(false); } //連接到主機 publicbooleanconnectServer(StringServerIP,intServerPort)throwsException { try { //取得主機端口 chessSocket=newSocket(ServerIP,ServerPort); //取得輸入流 inputData=newDataInputStream(chessSocket.getInputStream()); //取得輸出流 outputData=newDataOutputStream(chessSocket.getOutputStream()); firThread.start(); returntrue; } catch(IOExceptionex) { statusText.setText("連接失敗!\n"); } returnfalse; } //設定勝利時的棋盤狀態(tài) publicvoidsetVicStatus(intvicChessColor) { //清空棋盤 this.removeAll(); //將黑棋的位置設置到零點 for(inti=0;i<=chessBlackCount;i++) { chessBlack_XPOS[i]=0; chessBlack_YPOS[i]=0; } //將白棋的位置設置到零點 for(inti=0;i<=chessWhiteCount;i++) { chessWhite_XPOS[i]=0; chessWhite_YPOS[i]=0; } //清空棋盤上的黑棋數 chessBlackCount=0; //清空棋盤上的白棋數 chessWhiteCount=0; add(statusText); statusText.setBounds(40,5,360,24); if(vicChessColor==1) {//黑棋勝 chessBlackVicTimes++; statusText.setText("黑方勝,黑:白"+chessBlackVicTimes+":"+chessWhiteVicTimes +",游戲重啟,等待白方..."); } elseif(vicChessColor==-1) {//白棋勝 chessWhiteVicTimes++; statusText.setText("白方勝,黑:白"+chessBlackVicTimes+":"+chessWhiteVicTimes +",游戲重啟,等待黑方..."); } } //取得指定棋子的位置 publicvoidsetLocation(intxPos,intyPos,intchessColor) { if(chessColor==1) {//棋子為黑棋時 chessBlack_XPOS[chessBlackCount]=xPos*20; chessBlack_YPOS[chessBlackCount]=yPos*20; chessBlackCount++; } elseif(chessColor==-1) {//棋子為白棋時 chessWhite_XPOS[chessWhiteCount]=xPos*20; chessWhite_YPOS[chessWhiteCount]=yPos*20; chessWhiteCount++; } } //判斷當前狀態(tài)是否為勝利狀態(tài) publicbooleancheckVicStatus(intxPos,intyPos,intchessColor) { intchessLinkedCount=1;//連接棋子數 intchessLinkedCompare=1;//用于比較是否要繼續(xù)遍歷一個棋子的相鄰網格 intchessToCompareIndex=0;//要比較的棋子在數組中的索引位置 intcloseGrid=1;//相鄰網格的位置 if(chessColor==1) {//黑棋時 chessLinkedCount=1;//將該棋子自身算入的話,初始連接數為1 //以下每對for循環(huán)語句為一組,因為下期的位置能位于中間而非兩端 for(closeGrid=1;closeGrid<=4;closeGrid++) {//遍歷相鄰4個網格 for(chessToCompareIndex=0;chessToCompareIndex<=chessBlackCount;chessToCompareIndex++) {//遍歷棋盤上所有黑棋子 if(((xPos+closeGrid)*20==chessBlack_XPOS[chessToCompareIndex]) &&((yPos*20)==chessBlack_YPOS[chessToCompareIndex])) {//判斷當前下的棋子的右邊4個棋子是否都為黑棋 chessLinkedCount=chessLinkedCount+1;//連接數加1 if(chessLinkedCount==5) {//五子相連時,勝利 returntrue; } } } if(chessLinkedCount==(chessLinkedCompare+1)){ chessLinkedCompare++; } else{//若中間有一個棋子非黑棋,則會進入此分支,此時無需再遍歷 break; } } for(closeGrid=1;closeGrid<=4;closeGrid++) { for(chessToCompareIndex=0;chessToCompareIndex<=chessBlackCount;chessToCompareIndex++) { if(((xPos-closeGrid)*20==chessBlack_XPOS[chessToCompareIndex]) &&(yPos*20==chessBlack_YPOS[chessToCompareIndex])) {//判斷當前下的棋子的左邊4個棋子是否都為黑棋 chessLinkedCount++; if(chessLinkedCount==5) { returntrue; } } } if(chessLinkedCount==(chessLinkedCompare+1)){ chessLinkedCompare++; } else{ break; } } //進入新的一組for循環(huán)時要將連接數等重置 chessLinkedCount=1; chessLinkedCompare=1; for(closeGrid=1;closeGrid<=4;closeGrid++) { for(chessToCompareIndex=0;chessToCompareIndex<=chessBlackCount;chessToCompareIndex++) { if((xPos*20==chessBlack_XPOS[chessToCompareIndex]) &&((yPos+closeGrid)*20==chessBlack_YPOS[chessToCompareIndex])) {//判斷當前下的棋子的上邊4個棋子是否都為黑棋 chessLinkedCount++; if(chessLinkedCount==5) { returntrue; } } } if(chessLinkedCount==(chessLinkedCompare+1)){ chessLinkedCompare++; } else{ break; } } for(closeGrid=1;closeGrid<=4;closeGrid++) { for(chessToCompareIndex=0;chessToCompareIndex<=chessBlackCount;chessToCompareIndex++) { if((xPos*20==chessBlack_XPOS[chessToCompareIndex]) &&((yPos-closeGrid)*20==chessBlack_YPOS[chessToCompareIndex])) {//判斷當前下的棋子的下邊4個棋子是否都為黑棋 chessLinkedCount++; if(chessLinkedCount==5) { returntrue; } } } if(chessLinkedCount==(chessLinkedCompare+1)){ chessLinkedCompare++; } else{ break; } } chessLinkedCount=1; chessLinkedCompare=1; for(closeGrid=1;closeGrid<=4;closeGrid++) { for(chessToCompareIndex=0;chessToCompareIndex<=chessBlackCount;chessToCompareIndex++) { if(((xPos-closeGrid)*20==chessBlack_XPOS[chessToCompareIndex]) &&((yPos+closeGrid)*20==chessBlack_YPOS[chessToCompareIndex])) {//判斷當前下的棋子的左上方向4個棋子是否都為黑棋 chessLinkedCount++; if(chessLinkedCount==5) { returntrue; } } } if(chessLinkedCount==(chessLinkedCompare+1)){ chessLinkedCompare++; } else{ break; } } for(closeGrid=1;closeGrid<=4;closeGrid++) { for(chessToCompareIndex=0;chessToCompareIndex<=chessBlackCount;chessToCompareIndex++) { if(((xPos+closeGrid)*20==chessBlack_XPOS[chessToCompareIndex]) &&((yPos-closeGrid)*20==chessBlack_YPOS[chessToCompareIndex])) {//判斷當前下的棋子的右下方向4個棋子是否都為黑棋 chessLinkedCount++; if(chessLinkedCount==5) { returntrue; } } } if(chessLinkedCount==(chessLinkedCompare+1)){ chessLinkedCompare++; } else{ break; } } chessLinkedCount=1; chessLinkedCompare=1; for(closeGrid=1;closeGrid<=4;closeGrid++) { for(chessToCompareIndex=0;chessToCompareIndex<=chessBlackCount;chessToCompareIndex++) { if(((xPos+closeGrid)*20==chessBlack_XPOS[chessToCompareIndex]) &&((yPos+closeGrid)*20==chessBlack_YPOS[chessToCompareIndex])) {//判斷當前下的棋子的右上方向4個棋子是否都為黑棋 chessLinkedCount++;
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯系上傳者。文件的所有權益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
- 4. 未經權益所有人同意不得將文件中的內容挪作商業(yè)或盈利用途。
- 5. 人人文庫網僅提供信息存儲空間,僅對用戶上傳內容的表現方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
- 6. 下載文件中如有侵權或不適當內容,請與我們聯系,我們立即糾正。
- 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 散客旅游合同范本在線查看
- 企業(yè)車輛交易協(xié)議書模板
- 2024體育賽事場地租賃合同
- 2024版土石方運輸合同
- 農村個人購房合同范例
- 合伙協(xié)議書范例
- 如何簽訂借款合同避免風險
- 個人汽車買賣合同樣本模板
- 2023年高考地理專題復習新題典題精練-洋流(原卷版)
- 致大海選擇性必修中冊 第四單元課件
- 服務質量、保證措施
- (必練)廣東省軍隊文職(經濟學)近年考試真題試題庫(含答案)
- 含羞草天氣課件
- 2024年安全生產知識競賽考試題庫及答案(共五套)
- 22《鳥的天堂》課件
- 農業(yè)灌溉裝置市場環(huán)境與對策分析
- 新疆烏魯木齊市第十一中學2024-2025學年八年級上學期期中道德與法治試卷
- 2024年江西省高考地理真題(原卷版)
- 部編版小學五年級上冊道法課程綱要(知識清單)
- 經濟法學-計分作業(yè)一(第1-4章權重25%)-國開-參考資料
- 山東省臨沂市(2024年-2025年小學四年級語文)人教版期中考試(上學期)試卷及答案
評論
0/150
提交評論