java实现J联机五子棋
在完成java单机五子棋后,我开始尝试写联机五子棋(局域网内,因为没有公网IP)。上次的五子棋写的很乱,全部写在一个类中,这次我采用面向对象的思想,把特定的功能和属性都写成一个类。代码分为两部分,客户端和服务端。客户端是用AWT写的,主要由一个五子棋面板和一个功能键面板构成。网络通信使用的是TCP,通过序列化和反序列化完成消息的读和写。运行的时候先运行服务端程序,接着运行两个客户端。代码放在了文章的最后
下面是客户端运行的效果:
文章图片
这里是代码包的结构:
文章图片
接着我来依次说下这些类所完成的功能
Media包
Media包:主要是放了五子棋的背景图片和播放音乐的类以及音乐内容
播放音乐这个类是我从室友那拿的,所以我也不是很懂,瞄了一眼是用Applet完成的,只能处理.wav后缀的音乐
Net包
Net包:包含两个类,细心的小伙伴应该注意到客户端是没有主方法的。客户端中其实是包含与服务端进行通信的socket的,其中包含一些读和写的方法。服务端我采用的是线程池的方法来处理客户端的请求(线程池这部分我也不是特别了解,用起来和多线程感觉差不多)。
View包
View包:包含四个类,分别是ChessBoard,ChessPanel,Pieces和WhoWin 类
ChessBoard是一个包含Main方法的JFrame,命令面板和棋盘面板都是添加到这个JFrame中的。
ChessPanel是一个棋盘面板,里面完成了如:画19*19的棋盘线条,加载背景图片,不停的接收来自服务端的消息并处理(把棋子加到面板),处理每次点击后添加棋子到面板并发送棋子到服务器,判断胜负并且给出提示消息。
Pieces是一个棋子,其中有包含如颜色,棋子半径,棋子位置和命令(和前面的命令面板配合使用,默认是发送)等属性。
WhoWin就是五子棋中判断谁输谁赢的部分。每下一步就需要判断。
播放音乐的类:
package Media.Music; import java.io.File; import java.io.IOException; import javax.sound.sampled.AudioInputStream; import javax.sound.sampled.AudioSystem; import javax.sound.sampled.Clip; import javax.sound.sampled.LineUnavailableException; import javax.sound.sampled.UnsupportedAudioFileException; public class PlayMusic {private Clip clip; public PlayMusic(String filePath) throws LineUnavailableException, UnsupportedAudioFileException, IOException {File file = new File(filePath); AudioInputStream audioInputStream = AudioSystem.getAudioInputStream(file); clip = AudioSystem.getClip(); clip.open(audioInputStream); } public void play() {clip.setFramePosition(1); clip.start(); } public void loop() {clip.loop(Clip.LOOP_CONTINUOUSLY); }public void stop() {clip.stop(); }}
TcpClient:
package net; import view.Pieces; import java.io.*; import java.net.Socket; public class TcpClient{private Socket socket; private ObjectInputStream ois; private ObjectOutputStream oos; public TcpClient(Socket socket,ObjectInputStream ois,ObjectOutputStream oos){this.socket= socket; this.ois = ois; this.oos = oos; }public Socket getSocket() {return socket; } public void setSocket(Socket socket) {this.socket = socket; } public ObjectInputStream getOis() {return ois; } public void setOis(ObjectInputStream ois) {this.ois = ois; } public ObjectOutputStream getOos() {return oos; } public void setOos(ObjectOutputStream oos) {this.oos = oos; } public void send(Pieces pieces) throws IOException {oos.writeObject(pieces); System.out.println(socket+"向服务器发送消息"); }public Pieces accept() throws IOException, ClassNotFoundException {Pieces pieces = (Pieces) ois.readObject(); System.out.println(socket+"从服务器读取消息"); return pieces; }public void close(){; }}
【java实现J联机五子棋】TcpServer:
package net; import view.Pieces; import java.io.*; import java.net.ServerSocket; import java.net.Socket; import java.net.SocketException; import java.util.ArrayList; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class TcpServer {public static void main(String[] args) {// 保存客户端处理的线程ArrayListuserList = new ArrayList<>(); // 固定大小的线程池只支持两个线程,用来处理客户端ExecutorService es = Executors.newFixedThreadPool(2); try {ServerSocket server = new ServerSocket(10086); System.out.println("服务器已经启动,正在等待客户端连接......"); while (true) {//接收客户端的Socket,如果没有客户端连接就一直卡在这里Socket socket = server.accept(); // 每来一个用户就创建一个线程UserThread user = new UserThread(socket, userList); // 开启线程es.execute(user); }} catch (IOException e) {e.printStackTrace(); }}} class UserThread implements Runnable {private Socket socket = null; private static ArrayList list; // 客户端线程集合private ObjectOutputStream oos; private ObjectInputStream ois; private boolean flag = true; // 标记 public UserThread(Socket socket, ArrayList list) {this.socket = socket; this.list = list; list.add(this); // 把当前线程加入list中} @Overridepublic void run() {UserThread user = null; try {System.out.println("客户端:" + socket.getInetAddress().getHostAddress() + "已经连接"); ois = new ObjectInputStream(socket.getInputStream()); oos = new ObjectOutputStream(socket.getOutputStream()); while(true){Pieces pieces = (Pieces) ois.readObject(); // 客户端发给服务端的消息,把他写到其它套接字中去int size = list.size(); for (int i = 0; i < size; i++) {user = list.get(i); if (user.socket != socket) {user.oos.writeObject(pieces); System.out.println("从"+socket+"向"+user.socket+"发送消息"); }}}} catch(SocketException e){ //todo 客户端掉线后,移除客户端。没想好{1.从客户端列表移除当前元素,关闭当前:socket,通知另一方:这一方已经掉线,然后关闭这一方的socket}try {int i = list.size(); if (i ==2){list.remove(user); System.out.println("已经删除了一个客户端"); list.get(0).oos.writeObject(new Pieces("对方掉线")); }else if (i==1){list.remove(0); System.out.println("又移除了另一个客户端"); }} catch (IOException ex) {ex.printStackTrace(); }} catch (IOException e) {e.printStackTrace(); } catch (ClassNotFoundException e) {e.printStackTrace(); }}}
ChessBoard:
/** 1.变量值不变的问题* 2.输入输出流先后顺序的问题(socket阻塞)* 3.socket 短连接不关闭输入输出流,为何看起来就像长连接一样(长短连接的区别是什么)* */// todo 一个提示框package view; import Media.Music.PlayMusic; import javax.sound.sampled.LineUnavailableException; import javax.sound.sampled.UnsupportedAudioFileException; import javax.swing.*; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.IOException; public class ChessBoard extends JFrame implements ActionListener {private JButton PlayMusic = new JButton("播放音乐"); private ChessPanel chessPanel; private Panel CommandPanel = new Panel(); private JButton reStart = new JButton("重新开始"); private JButton fail = new JButton("认输"); private JButton Regret = new JButton("悔棋"); private String command=null; // 触发按钮后发送的命令private PlayMusic music = null; private int count = 1; //todo 静态语句块{try {music = new PlayMusic("./src/Media/Music/bg3.wav"); } catch (LineUnavailableException e) {e.printStackTrace(); } catch (UnsupportedAudioFileException e) {e.printStackTrace(); } catch (IOException e) {e.printStackTrace(); }} public ChessBoard() {this.setTitle("欢乐五子棋"); chessPanel = new ChessPanel(); this.add(chessPanel,BorderLayout.CENTER); reStart.addActionListener(this); fail .addActionListener(this); Regret.addActionListener(this); PlayMusic.addActionListener(this); CommandPanel.add(reStart); CommandPanel.add(fail); CommandPanel.add(Regret); CommandPanel.add(PlayMusic); this.add(CommandPanel,BorderLayout.SOUTH); this.setBounds(10, 10, 800, 800); this.setVisible(true); this.setResizable(false); this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); }public static void main(String[] args) {ChessBoard Board = new ChessBoard(); }@Overridepublic void actionPerformed(ActionEvent e) {if(e.getSource()==reStart){command ="重新开始"; chessPanel.canPlay = true; }else if(e.getSource()==fail){command ="认输"; JOptionPane.showMessageDialog(chessPanel,"It's a pity,you have fail the game!"); chessPanel.canPlay = false; }else if (e.getSource()==Regret){command ="悔棋"; }else if (e.getSource()==PlayMusic){// todo 播放音乐,单数次播放;if (count%2==1){music.play(); }else {music.stop(); }count++; command = null; }if(command!=null){Pieces pieces = new Pieces(command); try {this.chessPanel.client.send(pieces); } catch (IOException ex) {ex.printStackTrace(); }} }}
ChessPanel:
package view; // 五子棋面板,就是在这里面画图。// todo 背景图片 ,也许一个背景音乐import net.TcpClient; import javax.swing.*; import java.awt.*; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.net.ConnectException; import java.net.Socket; import java.util.ArrayList; import java.util.Iterator; public class ChessPanel extends JPanel implements MouseListener{// TODO 从服务器接收来的棋子 ,值不变有问题//Pieces accept_pieces = new Pieces(); //Pieces send_pieces = new Pieces(); whoWin ifWin =new whoWin() ; // 是否胜利TcpClient client = null; // 客户端boolean canPlay = true; // 是否能继续玩boolean isBlack = true; // 是否黑子,黑1,白2ArrayList allPieces = new ArrayList<>(); // 存储棋子对象,就是通过这个画图的int [][] allChess = new int[19][19]; int PanelWidth; int PanelHeight; int width = 600; int height = 600; int temp = width / 18; int xbase,ybase; Image image = Toolkit.getDefaultToolkit().getImage("./src/Media/bg.jpeg"); // "./"表示当前项目下public ChessPanel(){this.addMouseListener(this); try {Socket socket = new Socket("172.27.29.190", 10086); //TODO 构建输出输入流,输入输出流问题,先输出后输入ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream()); ObjectInputStream ois = new ObjectInputStream(socket.getInputStream()); client = new TcpClient(socket,ois,oos); new Thread(new getMessage()).start(); // 开启读取的线程} catch (ConnectException e){System.out.println("服务器拒绝连接!"); } catch (IOException e) {e.printStackTrace(); }catch(Exception e ){e.printStackTrace(); }}// 画图部分public void paintComponent(Graphics g) {super.paintComponent(g); PanelWidth = this.getSize().width; // 这两步骤PanelHeight = this.getSize().height; xbase = (PanelWidth - width) / 2; ybase = (PanelHeight - height) / 2; Graphics2D g2d = (Graphics2D) g; //this.setBackground(new Color(246, 186, 114)); g2d.drawImage(image,0,0,this.getWidth(),this.getHeight(),this); int x1, y1, x2, y2; // 画线for (int i = 0; i < 19; i++) {if (i == 0 || i == 18) {g2d.setStroke(new BasicStroke(3.0f)); } else g2d.setStroke(new BasicStroke(1.0f)); x1 = xbase + temp * i; y1 = ybase; y2 = ybase + 18 * temp; g2d.drawLine(x1, y1, x1, y2); x1 = xbase; y1 = ybase + temp * i; x2 = xbase + temp * 18; g2d.drawLine(x1, y1, x2, y1); }// 开始画棋子int radius ; int xPos,yPos; Iterator it = allPieces.iterator(); // 迭代器遍历arraylistwhile(it.hasNext()){Pieces pieces = (Pieces) it.next(); radius= pieces.getRadius(); xPos = pieces.getxPos(); yPos = pieces.getyPos(); System.out.println(pieces.getColor()+","+pieces.getxPos()+","+pieces.getyPos()); if (pieces.getColor() == 1){g2d.setColor(Color.black); g2d.fillOval(xPos*temp+xbase-radius/2,yPos*temp+ybase-radius/2,radius,radius); }else if (pieces.getColor() == 2) {g2d.setColor(Color.white); g2d.fillOval(xPos * temp + xbase - radius / 2, yPos * temp + ybase - radius / 2, radius, radius); }} } @Overridepublic void mousePressed(MouseEvent e) {int x ,y ; if (canPlay) {x = e.getX(); y = e.getY(); //判断鼠标点击位置if (x >= xbase & x <= (xbase + 18 * temp) & y >= ybase & y < (ybase + 18 * temp)) {// 判断是不是下在空的位置int tempX = (x - xbase) / temp, tempY = (y - ybase) / temp; // todo 这里是关键判断这点坐标的数组下标是什么if ((x - xbase) % temp > temp / 2) {x = tempX + 1; } elsex = tempX; if ((y - ybase) % temp > temp / 2)y = tempY + 1; elsey = tempY; // 先判断有没有棋子,处理没有棋子的情况if (allChess[x][y] != 0) {JOptionPane.showMessageDialog(this, "这里有棋子了"); } else {Pieces send_pieces = new Pieces(); send_pieces.setxPos(x); send_pieces.setyPos(y); if (isBlack){send_pieces.setColor(1); allChess[x][y] = 1; isBlack = false; }else{send_pieces.setColor(2); allChess[x][y]=2; isBlack = true; }allPieces.add(send_pieces); // 向棋子队列加入当前棋子canPlay = false; // canPlay在true情况下, 点击一次后不能点击了this.repaint(); ifWin = new whoWin(allChess,x,y); // 如果赢了,就不能玩了,并且给出提示消息,你赢了; if(ifWin.isWin()){canPlay = false; JOptionPane.showMessageDialog(this,"Congratulations you have won tha game !"); }try {if (client!=null){client.send(send_pieces); }} catch (IOException ex) {ex.printStackTrace(); }}} }} // 读取来自服务器端的信息class getMessage implements Runnable{private boolean flag = true; public void setFlag(boolean flag) {this.flag = flag; } @Overridepublic void run() {// 循环读while(flag){if(client!=null){try {Pieces accept_pieces = client.accept(); String command = accept_pieces.getCommand(); int color = accept_pieces.getColor(); switch (command){case "发送":{canPlay = true; if (color == 1){isBlack = false; //对方为黑我为白}else{isBlack = true; }allPieces.add(accept_pieces); allChess[accept_pieces.getxPos()][accept_pieces.getyPos()]= accept_pieces.getColor(); ChessPanel.this.repaint(); ifWin.setY(accept_pieces.getyPos()); ifWin.setX(accept_pieces.getxPos()); ifWin.setAllChess(allChess); if(ifWin.isWin()){canPlay = false; JOptionPane.showMessageDialog(ChessPanel.this,"It's a pity you have fail the game!"); }break; }case "悔棋":{int i = JOptionPane.showConfirmDialog(ChessPanel.this,"对方请求悔棋,是否同意!"); // yes 0,no 1,cancel 2,closed -1Pieces pieces = new Pieces(); if (i == 0){// 同意悔棋:1.同意对方悔棋pieces.setCommand("同意悔棋"); // arraylist 去除末尾的两个值,对应allChess置0int size = allPieces.size(); for (int j = 1; j<=2; j++){allChess[allPieces.get(size-j).getxPos()][allPieces.get(size-j).getyPos()]=0; allPieces.remove(size-j); }ChessPanel.this.repaint(); }else if(i==1){pieces.setCommand("不同意悔棋"); }client.send(pieces); break; }case "认输":{ // 不能继续玩下去,你已经胜利JOptionPane.showMessageDialog(ChessPanel.this,"对方认输"); canPlay = false; JOptionPane.showMessageDialog(ChessPanel.this,"Congratulations you have won tha game !"); break; }case "重新开始":{ // 是否同意重新开始int i = JOptionPane.showConfirmDialog(ChessPanel.this,"对方请求重新开始,是否同意"); Pieces pieces = new Pieces(); if(i == 0){// allChess 和 allPieces全部置0; pieces.setCommand("同意重新开始"); int size = allPieces.size(); System.out.println("arraylist 长度:"+size); for (int j = 0; j
Pieces:
package view; import java.io.Serializable; // 存储棋子的相关信息public class Pieces implements Serializable {private int radius = 16; private int color = 0; private int xPos ; private int yPos; private String command = "发送"; public String getCommand() {return command; } public void setCommand(String command) {this.command = command; } public Pieces(int color, int xPos, int yPos){this.color = color; this.xPos = xPos; this.yPos = yPos; }public Pieces(){ }public Pieces(String command){this.command = command; }public int getRadius() {return radius; } public void setRadius(int radius) {this.radius = radius; } public int getColor() {return color; } public void setColor(int color) {this.color = color; } public int getxPos() {return xPos; } public void setxPos(int xPos) {this.xPos = xPos; } public int getyPos() {return yPos; } public void setyPos(int yPos) {this.yPos = yPos; }}
WhoWin:
package view; public class whoWin { // 判断是谁赢了private int allChess[][] = new int[19][19]; private int x = 0; private int y = 0; public whoWin(){ }public whoWin(int allChess[][],int x,int y){this.allChess = allChess; this.x = x; this.y = y; } public void setAllChess(int allChess[][]){this.allChess = allChess; }public void setX(int x){this.x = x; }public void setY(int y ){this.y = y; }public boolean isWin() {int color = allChess[x][y]; int count; count = this.Count(1, 0, color); // 对横方向的判断if (count >= 5) {return true; } else {count = this.Count(0, 1, color); // 对竖方向的判断if (count >= 5) {return true; } else {count = this.Count(1, 1, color); // 对左上方向的判断if (count >= 5)return true; else {count = this.Count(1, -1, color); // 对右上方向的判断if (count >= 5)return true; }}}return false; }private int Count(int xChange, int yChange, int color) {int count = 1; int tempX = xChange, tempY = yChange; while (color == allChess[x + xChange][y + yChange]) {count++; if (xChange != 0) {xChange++; }if (yChange != 0) {if (yChange > 0)yChange++; elseyChange--; }}xChange = tempX; yChange = tempY; while (color == allChess[x - xChange][y - yChange]) {count++; if (xChange != 0)xChange++; if (yChange != 0) {if (yChange > 0)yChange++; elseyChange--; }}return count; }}
最后,希望我的代码能对你有所帮助,大家一起加油呀!
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。
推荐阅读
- HibernateTools实现pojo类 数据库schma mapping映射的相互转换
- java android 读写西门子PLC数据,包含S7协议和Fetch/Write协议,s7支持200smart,300PLC,1200PLC,1500PLC
- Pure.CSS表单实现示例
- Java 使用 happen-before 规则实现共享变量的同步操作
- 车辆跟随滑模控制的python实现
- 前端判断是否APP客户端打开触屏,实现跳转APP原生组件交互之遐想
- Spring Boot, Java Config - No mapping found for HTTP request with URI [/???] in DispatcherServlet wi
- 安卓音视频播放功能简单实现 --Android基础
- 使用Service组件实现简单的音乐播放器功能 --Android基础
- Caused by: java.lang.IllegalStateException: Unable to complete the scan for annotations for web appl