实习报告
实习性质:JA V A程序设计学生姓名:胡桂豪
专业班级:计应103
指导教师:万青、陈戈、谢先伟实习时间:2011-12-31—2012-1-6实习地点:4312
重庆工程职业技术学院
目录
1 实习目的 (4)
2 实习内容 (4)
2.1 设计文档 (4)
2.2需求分析 (6)
2.3设计方法 (6)
2.3.1 ChessFrame主要功能 (6)
2.3.2 游戏主窗体 (7)
2.3.3 主菜单 (8)
2.3.4 创建待机室 (9)
2.3.5 服务器端 (10)
2.3.6 客户端 (12)
3 实习心得 (14)
1 实习目的
根据教学计划的安排,本课程在后半学期将进行一周的课程实习安排,完成《JA VA 小游戏:五子棋》的开发。通过实习,可以使学生进一步掌握Java面向对象设计方法、JDK常用类、Java窗口界面设计、数据库访问等方面的知识和技能,将前面学习的基础知识应用到实际的软件开发中去,积累应用程序分析、设计、编码、调试的经验。Java的广泛流行是与它所编写的程序可以在网络上运行且可以跨平台。事实上,支持Java的浏览器内置Java虚拟机,使得Java的小程序能够在网络上完全地传送和运行。这样使得程序人员即使不具备有关的网络知识,也能编写出高质量的网络通信程序。“网上五子棋游戏”便是其最好的例子。
本程序是一个基于网络协议为TCP/IP的网上对弈游戏,采用服务器-客户端的网络架构模式,允许网络上的多台机器(可能是两台机器)同时运行,一台用作服务器,始终处理网络上要求它进行服务的请求。如果有机器请求与它进行连接,用作服务器的机器接受之后就可以进行相互之间的通信。
通过本程序,我们初步熟悉并掌握java语言的基本知识,掌握服务器-客户端的网络架构模式,为以后学习高级web开发课程做准备。
2 实习内容
2.1 设计文档
(1)界面棋盘设计
在对棋盘界面设计方面要考虑简洁友好,符合游戏者需求。棋子的设计方面系统中设置了两种棋子颜色,white或者black, 游戏者可自行选择。棋子怎样画出来,怎样使棋子按我们所想的方式来绘制出来是设计的主要难题。运行时要求当每次点击鼠标的时候就在点击鼠标的地方画一个棋子,所以得定义一个棋子的类使点击鼠标时系统把棋子绘制出来。这样主界面里的棋子就确定了,而且也确定了几个所需要的类。可以先定义好这些类了。
有了以上的准备就可以把棋盘绘制出来了,下棋时每次在适当位置点击鼠标的时候就在所点击的位置上画出你这个角色的颜色,然后计算机会自动的下棋,计算机自动下棋要有一个控制变量来控制能不能下棋。人可以随意的下子,可是计算机就要计算在哪
个位置下子了。这里由计算机下子的算法来确定,人人对战的话就不象单机游戏一样,要计算机来判断人下子之后计算机下那一个地方了,人人对战只要在双方下子的时候判断赢棋情况,当然还要同步的控制两个人下棋的顺序,这样的话就要用到线程了,各自都继承或实现线程类或接口,以便各自能随时单独控制接发消息。
(2)算法设计
对于五子棋游戏,无论人机对战,还是双人对战,都需要判断棋盘上是否存在五子连珠情况,这既是游戏名字的由来,也是游戏结束的标志。判断五子连珠的原理是从横,竖,左斜线,右斜线4条线上判断是否存在5个相连的同类棋子。
对战一方落子后,在该处向8个方向检测连续的同类棋子,如果检测到直线方向上存在5个连续的同类棋子(包含本位置棋子),则判断为“连五”并结束检测循环。基于检测结果,可以判断游戏是否结束,并根据获胜方的落子代码判断获胜方是谁。
由于不考虑禁手问题,在实现“连五”过程中,我们可以考虑完成“双四”,“四三”,“双三”,“冲四”,“活三”,“活四”来实现目的。
活四:有两个点能形成“连五”的四就是活四;
冲四:只有一个点能形成“连五”的四叫做“冲四”,或者叫做“单四”;
活三:己方在落一子就能形成“活四”的三叫做“活三”。“活三”分为“连活三”和“跳活三”。连活三:在棋盘某一条阳线或阴线上有同色三子相连,且在此三子两端延长线上有一端至少有一个,另一端至少有两个无子的交叉点与此三子紧密相连。跳活三:中间仅间隔一个无子交叉点的连三,但两端延长线均至少有一个无子的交叉点与此三子相连。
双三:由于黑方走一着在无子交叉点上同时形成二个或二个以上黑方" 活" 的局面。
双四:由于黑方走一着在无子交叉点上同时形成二个或二个以上黑方" 四" 的局面。
四三:指某一方同时具备两个先手,其中一个是" 四" ,一个是" 活三" 。
在实现上面这些高级算法的时候,我们需要一个基础,就是对制定位置周围情况进行检测。在棋盘上面,棋子存在8个移动方向,检测时需要对每个方向进行检测。为了简单直观,这里将每步棋子的移动生成一个新的棋子。
2.2需求分析
一款小游戏的确立是建立在各种各样的需求上面的,这种需求往往来自于玩家的实际需求,其中玩家的实际需求最为重要.面对游戏拥有不同知识和理解层面的玩家,游戏制作人对玩家需求的理解程度,在很大程度上决定了此类游戏开发的成败.因此如何更好地的了解,分析,明确玩家需求,并且能够准确,清晰以文档的形式表达给游戏制作人,保证开发过程按照满足玩家需求为目的正确开发方向进行,是每游戏游戏制作人需要面对的问题。
作为五子棋的设计需要考虑到的最基本的需求莫过于人机对战与人人对战功能的实现,当然还有下棋过程中的下棋悔棋功能以及判断游戏的胜负等方面的要求。当然最好是要考虑到界面的友好性,作为一个娱乐软件,还应该考虑到玩家在游戏时的舒适性。
我们通过使用套接字SOCKET来实现游戏之间的通讯,它是基于点对点的通讯,开始让服务器初始化建立服务器套接字SOCKET,基于某个端口PORT,然后打开客户端套接字SOCKET,连接到服务器端的地址ADDRESS和端口PORT,在这之间可能发生异常EXCEPTION。
“网络五子棋”是由服务器端和客户端组成的。游戏分为黑方与白方,只要其中一方的棋子形成“五子连珠”之势,即可获胜。
网络五子棋游戏服务器端主要有三个类:OmokServer类、Omok_ Thread类(线程类)和BManager类(消息广播者)。它的主要功能是连接客户端,使多个客户端连接在一起,最终实现联网。
2.3设计方法
2.3.1 ChessFrame主要功能
类ChessFrame主要功能是创建五子棋游戏主窗体和菜单,主要代码如下:class ChessFrame extends JFrame implements ActionListener {
private String[] strsize={"20x15","30x20","40x30"};
private String[] strmode={"人机对弈","人人对弈"};
public static boolean iscomputer=true,checkcomputer=true;
private int width,height;
private ChessModel cm;
private MainPanel mp;
2.3.2 游戏主窗体
构造五子棋游戏的主窗体,主要代码如下:
public ChessFrame() {
this.setTitle("五子棋游戏");
cm=new ChessModel(1);
mp=new MainPanel(cm);
Container con=this.getContentPane();
con.add(mp,"Center");
this.setResizable(false);
this.addWindowListener(new ChessWindowEvent());
MapSize(20,15);
JMenuBar mbar = new JMenuBar();
this.setJMenuBar(mbar);
JMenu gameMenu = new JMenu("游戏");
mbar.add(makeMenu(gameMenu, new Object[] {
"开局", "棋盘","模式", null, "退出"
}, this));
JMenu lookMenu =new JMenu("视图");
mbar.add(makeMenu(lookMenu,new Object[] {
"Metal","Motif","Windows"
},this));
JMenu helpMenu = new JMenu("帮助");
mbar.add(makeMenu(helpMenu, new Object[] {
"关于"
}, this));
}
2.3.3 主菜单
构造五子棋游戏的主菜单,代码如下:
public JMenu makeMenu(Object parent, Object items[], Object target){ JMenu m = null;
if(parent instanceof JMenu)
m = (JMenu)parent;
else if(parent instanceof String)
m = new JMenu((String)parent);
else
return null;
for(int i = 0; i < items.length; i++)
if(items[i] == null)
m.addSeparator();
else if(items[i] == "棋盘"){
JMenu jm = new JMenu("棋盘");
ButtonGroup group=new ButtonGroup();
JRadioButtonMenuItem rmenu;
for (int j=0;j rmenu=makeRadioButtonMenuItem(strsize[j],target); if (j==0) rmenu.setSelected(true); jm.add(rmenu); group.add(rmenu); } m.add(jm); }else if(items[i] == "模式"){ JMenu jm = new JMenu("模式"); ButtonGroup group=new ButtonGroup(); JRadioButtonMenuItem rmenu; for (int h=0;h rmenu=makeRadioButtonMenuItem(strmode[h],target); if(h==0) rmenu.setSelected(true); jm.add(rmenu); group.add(rmenu); } m.add(jm); }else m.add(makeMenuItem(items[i], target)); return m; } 2.3.4 创建待机室 该游戏创建了待机室,供其他未处于下棋状态的人们使用,也可以创建另一个房间,供下棋的两个人使用。 我们可以通过标记号码来区分待机室与下棋室。即用0标记待机室,其他房间使用0以上的自然数来标记。事实上,我们并不时真正创建了房间,而只是将房间号码赋予了客户机而已。以此,我们可以实现拥有相同号码的客户机彼此间可以相互通信。 例如,客户机向服务器提出进入某个房间的请求后,服务器必须确认所请求的房间是否允许它进入,然后,发送相应的请求给客户机。对于消息,我们可以采用“[消息类 型]+内容”的形式。假若客户机想进入20号房间,那么,它需要向服务器发送消息“[ROOM]20”.如果第20号房间允许进入,那么,服务器在进行了某些处理后会向客户机传送消息“[ROOM]20”。相反,若20号房间禁止入内,则服务器将向客户机发送“[NULL]”消息。 客户机请求:[ROOM]20 服务器的应答:[ROOM]20,或[NULL] 又例如,假设两个玩家同时进入某个房间,要进行对弈。首先,一名玩家点击开始按钮,向服务器发送“[STAERT]”消息,另一名玩家也向服务器发送了“[STAERT]”消息,然后服务器将会认为这两名玩家要进行对弈,它会随时向玩家指派棋子,并分别向玩家发送“[COLOR]BLACK”与“[CLOR]WHITE”消息。 客户机1的请求:“[STAERT]” 客户机2的请求:“[STAERT]” 服务器的应答:[COLOR]BLACK”与“[CLOR]WHITE 2.3.5 服务器端 网络五子棋游戏服务器端主要有三个类:OmokServer类、Omok_ Thread类(线程类)和BManager类(消息广播者)。 OmokServer类在受到客户机的连接请求后,将创建新的套接字,并使其与客户机连接,然后启动线程,并将线程添加至BManager对象中。 Private BManager bMan=new BManager(); //消息广播者对象........... While(true){ Socket socket=server.accept(); //获取套接字 Omok_Thread ot=new Omok_Thread(socket); //创建线程 ot.start(); //启动线程 bMan.add(ot); //添加线程........... } Omok_Thread对象不仅可以获取套接字、输入输出流,而且它还提供了有关客户机的消息。例如客户机的姓名、房间号、游戏开始与否等。这是因为我们需要确定消息的来源及主要在某个房间传播的消息。并且,消息广播者也需要参照线程对象的房间号,以便将消息发送至某个房间中。 BManager类继承了Vector类,它的元素为Omok_ Thread线程对象,这样才能自由地参照Omok_ Threadd对象的房间号及玩家的名字等。为了实现向指定的房间发送消息,需要比较Bmanager各元素的房间号和给定的房间号,若两者相同,则向此房间发送消息。 SendToRoom()方法用于向指定的房间(roomnum)发送消息(msg),代码如下。 void sendToRoom(int roomNum,String msg){ for(int i=0;i if(roomNum==第i个线程的房间号) //向第i个线程连接的客户机发送消息} } } SendToOthers()方法用于向同房间的其他用户发送消息(msg)这个方法与SendToRoom ()一样,将使用for循环检查元素(线程)的房间号。但是,并不会与ot连接的客户机发送消息。 void sendToOthers(Omok_Thread ot,String msg){ for(int i=0;i if(第i个线程的房间号==ot的房间号&&第i个线程!=ot){ } } } sendToRoom()与sendToOthers()方法的不足之处在于,房间越多,它们就会增重服务器的负担,消息的传播将变得十分缓慢。 2.3.6 客户端 网络五子棋游戏客户端主要是实现五子棋的界面和功能的程序,我们可以通过服务器端连接多个客户端实现最终游戏的目的。 首先要实现五子棋盘(OmokBoard类)。五子棋盘继承了画布类,它内嵌于框架窗口中,拥有15*15(255)个方格。为了响应用户的下棋动作,五子棋盘必须能够响应鼠标事件。并且它必须拥有输出流,用以向对手传递棋手的下棋信息。程序中也有多种方法用来实现绘图操作。然而,程序中最重要的部分时如何决定胜负。横,竖或者对角线方向上拥有五颗同样的棋子时,即为胜利。 请看图表3。假设五个黑子从左向右分别标记为1、2、3、4、5,那么我们可以先放置1、2、3、4棋子,如果黑方最后一颗棋子5号棋放在如图所示的位置,则黑方在横向上五子连珠,黑方胜。这个过程如何用程序实现呢?在放5号棋子后,首先程序要计算出他左边连在一起的黑色棋子的个数。其左边已放有2号与1号棋子,且这两颗棋子连在一起,所以5号棋子左边相连的黑色棋子有两颗。然后,再计算5号棋子右边连在一起的黑色棋子的个数。如图,5号棋子右边有3号。4号两颗棋子。因此,5号棋子左边、右边及5号棋子合起来共5颗黑色棋子,黑方获胜。当然在竖直方向与对角线方向上也采取相同的方法进行检查。 图表 3 Map是一个二元数组,用于保存棋子的位置。如果在(2,3)位置上有一颗黑色棋子,则向数组元素map[2][3]赋入常数BLACK(=1);则将WHITE(=-1)值代入。并且,如果某个五子棋盘左上端坐标就变为(1,1),而非(0,0)。 为了计算方便,我这里将map的大小定义为17*17,而并非是先前的15*15。因此,五子棋盘左上就坐标就变为(1,1),而非(0,0) count()方法用来查找并计算连在一起的棋子的个数。P为基准坐标,dx、dy分别为表示横向于竖向的变量。col代表棋子颜色。弱dx为-1,dy为0,则计算左边连续棋子的个数;若dx为1,dy为1,则计算左下到右上连续棋子的个数。dx与dy只取-1、0、1三个值之一。下面是count()方法的代码。 Private int count(Point p,int dx,int dy,int col) { int i=0; for(;map[p.x+(i+1)*dx][p.y+(i+1)*dy]==col;i++) {} return i; } check()方法会调用count()方法,用来判断是否五子连珠。其代码如下。 Private Boolean check(point p,int cool) { if(count(p,1,0,col)+count(p,-1,0,col)==4) // 横向检查 return true; if(count(p,0,1col)+count(p,0,-1,col)==4) // 竖向检查 return true; if(count(p,-1,-1,col)+count(p,1,1col)==4) // 对角线方向检查 return true; if(count(p,1,-1,col)+count(p,-1,1,col)==4) // 对角线方向检查 return true; } 若用户将color颜色的棋子放在(x,y)位置上,程序将会进行如下检查。如果获胜,则向服务器发送“[WIN]”消息。同时,服务器也会向其对手发送“[LOSE]”消息。 Map[x][y]=color; If(check(new Point(x,y),color)) {writer.println(“[WIN]”); } 3 实习心得 通过这次课程设计,我进一步加深对基础理论的理解,扩大专业知识面,对收集资料、查阅文献、方案制定等实践方面得到了很好的锻练,促进对所学知识应用能力的提高。同时我渐渐的复习了Java使用方法和编程语法,之后的编程过程也相对得心应手,基本完成了预期计划的要求。