描述
开 本: 16开纸 张: 胶版纸包 装: 平装-胶订是否套装: 否国际标准书号ISBN: 9787302465638丛书名: 计算机系列教材
本书可作为计算机相关专业Java课程设计、Java实训等课程的教材,也可作为学生毕业设计以及Java程序设计爱好者的参考书。
目录
第1章单机版五子棋游戏/1
1.1五子棋游戏窗口制作/1
1.2创建棋盘类/2
1.2.1准备图片/2
1.2.2棋盘类的创建/2
1.2.3显示棋盘/3
1.3创建棋子类/4
1.3.1棋子类/4
1.3.2在棋盘上画出棋子/6
1.4实现单击鼠标下棋/6
1.5判断赢棋/8
1.6实现工具栏上的功能/11
1.7改变鼠标的形状/13
1.8作业/13第2章网络五子棋/15
2.1服务器端界面制作/16
2.2创建客户端界面/17
2.2.1创建主窗口和棋盘/17
2.2.2创建客户端界面右侧的3个类/18
2.2.3创建客户端界面下方的控制面板类/21
2.3实现“连接主机”按钮的功能/22
2.3.1连接服务器获取用户名/23
2.3.2将已经连接的客户端添加到用户列表中/26
2.4实现“加入游戏”按钮的功能/31
2.4.1客户端申请加入后对方选择同意或拒绝/31
2.4.2完成猜棋并准备好下棋/37
2.5实现下棋功能/41
2.5.1客户端向服务器发送下棋消息/41
2.5.2服务器接收消息并处理/43
2.5.3客户端接收消息并处理/43
2.6实现“放弃游戏”的功能/45
2.6.1Command类添加常量/46
2.6.2添加“放弃游戏”的响应代码/46
2.6.3在Communication类中添加giveup()
方法/46
2.6.4服务器接收giveup命令并处理/46
2.7加入计时功能/47
2.7.1设计计时线程类/47
2.7.2猜先后启动倒计时线程/48
2.8完善“关闭程序”按钮的功能/49
2.8.1在Command类中添加命令/49
2.8.2客户端向服务器发送命令/49
2.8.3服务器处理quit命令/50
2.8.4客户端处理delete命令/50
2.9作业/51第3章下棋数据的保存/52
3.1创建数据库/52
3.1.1数据库设计/52
3.1.2创建数据库/53
3.2用户管理/55
3.2.1数据库连接类/55
3.2.2用户管理/56
3.3用户注册和登录/61
3.3.1准备工作/62
3.3.2用户登录/62
3.3.3用户注册/67
3.4记录棋局和棋谱/71
3.4.1记录棋局/71
3.4.2记录棋谱/77
3.5查询棋局和棋谱欣赏/80
3.5.1查询棋局/81
3.5.2棋谱欣赏/85
3.6作业/90第4章人机对战/91
4.1准备工作/91
4.1.1在主程序中添加复选框/91
4.1.2在棋盘类中添加成员变量/92
4.1.3棋盘类中添加方法以及修改已有的
方法/92
4.2计算机智能下棋/95
4.2.1处理棋盘类中的数据成员/95
4.2.2五子棋的棋型与估值/97
4.2.3创建估值类Evaluate/100
4.2.4实现计算机智能下棋/111
4.3极小极大搜索法提高下棋水平/112
4.3.1极小极大算法与棋局的评价/113
4.3.2极小极大算法的实现/115
4.4AlphaBeta搜索方法/124
4.4.1AlphaBeta搜索方法简介/124
4.4.2AlphaBeta搜索方法实现/125
4.5作业/128
作业参考答案/130
参考文献/152
作者2017年3月
User user3=dao.getUser(“test3”, “dddd”);if(user3!=null){System.out.println(“找到该用户!”);}else{System.out.println(“未找到该用户!”);}}}根据测试结果,对照数据库中的user表,如果有不合理的地方,则需要修改User类或UserDao类,测试成功后,可将测试类Test删除。3.3用户注册和登录在客户端提供一个注册和登录界面,用户填好数据后向服务器发送注册或登录命令,服务器处理后,将处理结果通知客户端。运行界面如图3.2所示。图3.2注册和登录界面在客户端界面单击“登录”按钮,出现“用户登录”对话框,可以填写用户名和密码,单击“登录”按钮,向服务器发送login命令。如果用户还没有注册,可以单击“注册”按钮,在出现的“用户注册”对话框中输入相关信息后,单击“注册”按钮,向服务器发送register命令。客户端与服务器之间发送命令的流程如图3.3所示。图3.3注册/登录流程注册时,客户端发送register命令,参数是User对象,服务器通过UserDao类向用户表添加记录。如果成功,则向客户端发送参数为true的register命令;如果失败,则向客户端发送参数为false的register命令。登录时,客户端发送login命令,参数是用户名和密码,服务器通过UserDao类从用户表中查找用户名和密码指定的记录,如果找到,则登录成功,向客户端发送参数为true的login命令;如果没找到,则登录失败,向客户端发送参数为false的login命令。3.3.1准备工作[4/5]1. Command类添加命令常量在Command类中添加下面两个命令常量:public static final String REGISTER=”register”; public static final String LOGIN=”login”; 2. 修改PanelControl类将PanelControl类中的“连接主机”按钮改为“登录”按钮,即将connectButton改为loginButton,“连接主机”改为“登录”,然后将程序中的所有connectButton替换为loginButton。3. FiveClient类中添加方法由于在“用户登录”对话框类中要用到FiveClient类中的Communication属性c,而登录对话框类和FiveClient类不在一个包中,因此需要在FiveClient类中添加getC()方法,以便在其他类中获取Communication对象,代码如下。public Communication getC(){return c;}3.3.2用户登录[4/5]1. Communication类的登录方法由于“连接服务器”已经被“登录”代替了,因此可以删除FiveClient类中的connect()方法。将Communication类中的connect()方法改为login()方法,代码如下: public boolean login(String ip, String userName, String passWord) {try {s=new Socket(ip,FiveServer.TCP_PORT);dis=new DataInputStream(s.getInputStream());dos=new DataOutputStream(s.getOutputStream());dos.writeUTF(Command.LOGIN “:” userName “:” passWord);String msg=dis.readUTF();String[] words=msg.split(“:”);if(words[0].equals(Command.LOGIN) && words[1].equals(“true”)){fc.isConnected=true;new ReceaveThread(s).start();fc.control.exitGameButton.setEnabled(true);fc.control.loginButton.setEnabled(false);fc.control.joinGameButton.setEnabled(true);fc.control.cancelGameButton.setEnabled(false);return true;}else{s.close();return false;}} catch (UnknownHostException e) {e.printStackTrace();} catch (IOException e) {if(s!=null){try {s.close();} catch (IOException e1) {e1.printStackTrace();}}e.printStackTrace();}return false;}连接服务器后,向服务器发送login命令,然后等待服务器返回的结果。如果返回“login: true”,说明登录成功,为相关变量设置值,创建接收消息的线程并启动,设置按钮的状态;如果返回“login: false”,说明登录失败。2. 登录对话框在user包中创建登录对话框DialogLogin类,代码如下: public class DialogLogin extends JDialog implements ActionListener{JTextField tfUserName=new JTextField(14);JTextField tfPassword=new JTextField(14);JButton jbLogin=new JButton(“登录”);JButton jbRegister=new JButton(“注册”);JButton jbCancel=new JButton(“取消”);String ip;FiveClient fc;public DialogLogin(Window parent, String ip) {super(parent, “用户登录”,Dialog.ModalityType.APPLICATION_MODAL);fc=(FiveClient) parent;this.ip=ip;createGUI();}public void actionPerformed(ActionEvent e) {String str=e.getActionCommand();if (“登录”.equals(str)) {String userName=tfUserName.getText();String passWord=tfPassword.getText();if((userName==null)||(userName.isEmpty()) || (passWord==null)||(passWord.isEmpty())){JOptionPane.showMessageDialog(this, “各项数据不能为空!”);return;}else {if(fc.getC().login(ip, userName, passWord)){JOptionPane.showMessageDialog(this, “登录成功!”);this.dispose();}else{JOptionPane.showMessageDialog(this, “用户名或密码不符!”);}}}else if (“注册”.equals(str)) {//DialogRegister rd=new DialogRegister(this,ip);}else if(“取消”.equals(str)){this.dispose();}} public void createGUI() {this.setLayout(new BorderLayout());JPanel jpWest=new JPanel();JPanel jpCenter=new JPanel();JPanel jpSouth=new JPanel();jpWest.setLayout(new GridLayout(3, 1));jpCenter.setLayout(new GridLayout(3, 1));jpSouth.setLayout(new FlowLayout());jpWest.add(new JLabel(“用户名:”));jpWest.add(new JLabel(“密 码:”));jpCenter.add(tfUserName);jpCenter.add(tfPassWord);this.add(new JPanel(),BorderLayout.NORTH);this.add(jpWest,BorderLayout.WEST);this.add(jpCenter,BorderLayout.CENTER);jpSouth.add(jbLogin);jpSouth.add(jbRegister);jpSouth.add(jbCancel);jbLogin.addActionListener(this);jbRegister.addActionListener(this);jbCancel.addActionListener(this);this.add(jpSouth,BorderLayout.SOUTH);this.setDefaultCloseOperation(DISPOSE_ON_CLOSE);this.setLocation(450,250);this.pack();this.setVisible(true);}}由于对话框本身负责事件监听,因此DialogLogin类实现了ActionListener接口。为了便于看清程序的逻辑结构,我们将创建界面的代码放在了一个单独方法createGUI()中。在构造方法中设置对话框的父窗口(由参数指定)、标题以及显示模式,然后调用createGUI()方法创建对话框界面并显示。在actionPerformed()方法中,处理3个按钮的单击事件。如果是“登录”按钮,则首先获取编辑框中的用户名和密码,如果用户名和密码都不为空,则调用Communication类中的login()方法登录,如返回true,则登录成功,返回false,则登录失败。如果是“注册”按钮,则显示注册对话框,完成注册功能(注册功能的实现稍后给出)。如果是“取消”按钮,则使对话框消失。3. 显示登录对话框在fiveClient中修改登录按钮的监听程序段,显示登录对话框,代码如下: else if(e.getSource()==control.loginButton){c=new Communication(FiveClient.this);DialogLogin dr=new DialogLogin(FiveClient.this, FiveClient.this.control.inputIP.getText());}在创建DialogLogin对象时,要通过构造方法的参数指定对话框的父窗口和服务器的ip地址。4. 服务器处理login命令 由于现在已经不需要服务器为用户起名了,因此可以删除服务器类的静态成员clientNameNum,然后修改startServer()方法。修改后的代码如下: public void startServer(){try {ss=new ServerSocket(TCP_PORT);while(true){Socket s=ss.accept();InputStream is=s.getInputStream();OutputStream os=s.getOutputStream();DataInputStream dis=new DataInputStream(is);DataOutputStream dos=new DataOutputStream(os);String msg=dis.readUTF();String[] words=msg.split(“:”);if(words[0].equals(Command.LOGIN)){String userName=words[1];String passWord=words[2];UserDao ud=new UserDao();if(ud.getUser(userName, passWord)!=null){dos.writeUTF(Command.LOGIN “:true”);clientNum ;Client c=new Client(userName, s); clients.add(c);lStatus.setText(“连接数” clientNum);taMessage.append(s.getInetAddress().getHostAddress() ” ” userName “\n”);tellName(c);addAllUserToMe(c);addMeToAllUser(c);new ClientThread(c).start();}else{dos.writeUTF(Command.LOGIN “:false”);s.close();}}}} catch(IOException e) {e.printStackTrace();}}现在客户端连接服务器有注册和登录两个目的,因此服务器在接收到客户端连接时,要分别处理这两种情况。如果是login命令,则将用户名和密码取出,利用UserDao的getUser()方法在user表中查找指定的用户,如果返回值不为空,则登录成功,向客户端发送登录成功的命令,然后进行相应的处理,创建接收该客户端命令的线程并启动。为了不改变以前的程序结构,我们仍通过tellName()方法向客户端发送客户名,当然这个客户名就是客户端传过来的名字。如果登录失败,则向客户端发送登录失败的命令,然后将该Socket关闭。3.3.3用户注册与登录后就准备下棋不同,注册是一个相对独立的功能,因此对于注册功能,我们不使用Communication类与服务器通信,而是在注册对话框类中添加自己的Socket属性实现与服务器的连接。1. 注册对话框类在user包中创建注册对话框DialogRegister类,代码如下: public class DialogRegister extends JDialog implements ActionListener {JTextField tfUserName=new JTextField(20);JTextField tfPassword=new JTextField(20);JTextField tfRePassword=new JTextField(20);JTextField tfEmail=new JTextField(20);JButton jbRegister=new JButton(“注册”);JButton jbCancel=new JButton(“取消”);String ip;public DialogRegister(Window parent, String ip) {super(parent, “用户注册”, Dialog.ModalityType.APPLICATION_MODAL);this.ip=ip;createGUI();}public void actionPerformed(ActionEvent e) {String str=e.getActionCommand();if (“注册”.equals(str)) {String userName=tfUserName.getText();String passWord=tfPassWord.getText();String rePassWord=tfRePassWord.getText();String email=tfEmail.getText();if((userName==null)||(userName.isEmpty()) || (passWord==null)||(passWord.isEmpty()) || (rePassWord==null)||(rePassWord.isEmpty()) || (email==null)||(email.isEmpty())){JOptionPane.showMessageDialog(this, “各项数据不能为空!”);return;}if (!(passWord.equals(rePassWord))) {JOptionPane.showMessageDialog(this, “两次密码不一致!”);return;} else {if(register(new User(userName,passWord,email,1,new Date()))){JOptionPane.showMessageDialog(this, “注册成功!”);}else{JOptionPane.showMessageDialog(this, “注册失败!可能重名。”);}}}else if(“取消”.equals(str)){this.dispose();}} public boolean register(User u){Socket s=null;InputStream is=null;OutputStream os=null;DataInputStream dis=null;DataOutputStream dos=null;ObjectOutputStream oos=null;try {s=new Socket(ip, FiveServer.TCP_PORT);is=s.getInputStream();os=s.getOutputStream();dis=new DataInputStream(is);dos=new DataOutputStream(os);dos.writeUTF(Command.REGISTER);oos=new ObjectOutputStream(os);oos.writeObject(u);String msg=dis.readUTF();if(msg.equals(Command.REGISTER “:true”)){return true;}else{return false;}} catch(UnknownHostException e) {e.printStackTrace();return false;} catch(IOException e) {e.printStackTrace();return false;}finally{try {oos.close();dos.close();dis.close();s.close();} catch(IOException e) {e.printStackTrace();}}}public void createGUI() {this.setLayout(new BorderLayout());JPanel jpWest=new JPanel();JPanel jpCenter=new JPanel();JPanel jpSouth=new JPanel();jpWest.setLayout(new GridLayout(4, 1));jpCenter.setLayout(new GridLayout(4, 1));jpSouth.setLayout(new FlowLayout());jpWest.add(new JLabel(“用户名:”));jpWest.add(new JLabel(“密 码:”));jpWest.add(new JLabel(“确认密码:”));jpWest.add(new JLabel(“邮 箱:”));jpCenter.add(tfUserName);jpCenter.add(tfPassWord);jpCenter.add(tfRePassWord);jpCenter.add(tfEmail);this.add(jpWest, BorderLayout.WEST);this.add(jpCenter, BorderLayout.CENTER);jpSouth.add(jbRegister);jpSouth.add(jbCancel);jbRegister.addActionListener(this);jbCancel.addActionListener(this);this.add(jpSouth, BorderLayout.SOUTH);this.setDefaultCloseOperation(DISPOSE_ON_CLOSE);this.setLocation(450, 250);this.pack();this.setVisible(true);}}在actionPerformed()方法中,如果是“注册”按钮的事件,则先检查用户名、密码、重复密码以及邮箱是否有空的,如果没有空的再检查两次密码输入是否一致,如果一致,则调用register()方法注册,如果方法返回true,表示注册成功,如果返回false,表示注册失败。在register()方法中,首先连接服务器,随后向服务器发送register命令,接着再发一个User对象。之后,再从服务器读取字符串,如果是“register: true”,表示注册成功,如果是“register: false”,表示注册失败。在登录对话框类DialogLogin中,通过单击“注册”按钮显示注册对话框。将原来类中注释掉的一行的注释去掉,代码如下:else if (“注册”.equals(str)) {DialogRegister rd=new DialogRegister(this,ip);}2. 服务器处理注册命令在服务器类的startServer()方法中,与登录代码并列,加入注册的处理代码如下:if(words[0].equals(Command.REGISTER)){ObjectInputStream ois=null;try {ois=new ObjectInputStream(is);User u=(User) ois.readObject();UserDao ud=new UserDao();if(ud.addUser(u)){dos.writeUTF(Command.REGISTER “:true”);}else{dos.writeUTF(Command.REGISTER “:false”);}} catch (ClassNotFoundException e) {e.printStackTrace();}finally{dis.close();ois.close();dos.close();s.close();}}如果服务器收到register命令,则继续读一个User对象,然后调用UserDao类的addUser()方法向数据库中添加一个记录,如果返回值为true,表明添加成功,则向客户端发送“register: true”,否则,向客户端发送“register: false”。3.4记录棋局和棋谱下棋结束后,将棋局信息保存到数据库中,将棋谱信息保存到棋谱文件中。这里我们只记录连成五子赢棋后的棋局,对于认输和超时的情况没有记录,方法都是一样的。记录棋局和棋谱的流程如图3.4所示,当客户端向服务器发送win命令后,服务器将棋局信息保存到数据库,同时也将棋谱信息保存到棋谱文件中。图3.4保存棋局和棋谱的流程图3.4.1记录棋局[4/5]1. 棋局管理为了方便处理棋局数据,我们首先创建棋局类Game,以及保存棋局和查找棋局的类GameDao。建立一个game包,在game包中创建Game类和GameDao类。Game类用于保存棋局数据,代码如下: public class Game implements Serializable {private static final long serialVersionUID=-1301902854300541648L;String bUser;//黑方用户String wUser;//白方用户Date date; //下棋日期, java.util.DateString winner; //赢棋用户String fileName; //保存棋谱的文件名public Game(String bUser, String wUser, Date date,String winner) {this.bUser=bUser;this.wUser=wUser;this.date=date;this.winner=winner;fileName=initFileName();
评论
还没有评论。