首页 > 文章列表 > java如何实现联机五子棋

java如何实现联机五子棋

java
445 2023-05-04

java如何实现联机五子棋

下面是客户端运行的效果:

这里是代码包的结构:

接着我来依次说下这些类所完成的功能

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(){

        ;

    }

}

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) {

        // 保存客户端处理的线程

        ArrayList<UserThread> userList = 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<UserThread> list; // 客户端线程集合

    private ObjectOutputStream oos;

    private ObjectInputStream ois;

    private boolean flag = true;// 标记

 

    public UserThread(Socket socket, ArrayList<UserThread> list) {

        this.socket = socket;

        this.list = list;

        list.add(this); // 把当前线程加入list中

    }

 

    @Override

    public 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();

 

    }

    @Override

    public 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,白2

    ArrayList<Pieces> 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(); // 迭代器遍历arraylist

        while(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);

            }

        }

 

    }

 

    @Override

    public 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;

                } else

                    x = tempX;

                if ((y - ybase) % temp > temp / 2)

                    y = tempY + 1;

                else

                    y = 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;

        }

 

        @Override

        public 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 -1

                                Pieces pieces = new Pieces();

                                if (i == 0){

                                    // 同意悔棋:1.同意对方悔棋

                                    pieces.setCommand("同意悔棋");

                                    // arraylist 去除末尾的两个值,对应allChess置0

                                    int 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<size;j++){// 移除队首元素

                                        allChess[allPieces.get(0).getxPos()][allPieces.get(0).getyPos()] = 0;

                                        allPieces.remove(0);

                                    }

                                    canPlay = true;

                                    ChessPanel.this.repaint();

                                }else if (i ==1){

                                    pieces.setCommand("不同意重新开始");

                                }

                                client.send(pieces);

                                break;

                            }

                            case "同意悔棋":{// allpieces 和 allchess 回退

                                JOptionPane.showMessageDialog(ChessPanel.this,"对方同意悔棋");

                                int 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();

                                break;

                            }

                            case "不同意悔棋":{

                                JOptionPane.showMessageDialog(ChessPanel.this,"对方不同意悔棋");

                                break;

                            }

 

                            case "同意重新开始":{ // 全部置0,调用repaint 方法

                                JOptionPane.showMessageDialog(ChessPanel.this,"对方同意重新开始");

                                int size = allPieces.size();

                                for (int j = 0;j<size;j++){ //  todo 移除队首元素arraylist 长度改变;序列也发生改变

                                    allChess[allPieces.get(0).getxPos()][allPieces.get(0).getyPos()] = 0;

                                    allPieces.remove(0);

                                }

                                canPlay = true;

                                ChessPanel.this.repaint();

                                break;

                            }

                            case "不同意重新开始":{

                                JOptionPane.showMessageDialog(ChessPanel.this,"对方不同意重新开始");

                                break;

                            }

                            case "对方掉线":{ // 对方已经掉线

                                JOptionPane.showMessageDialog(ChessPanel.this,"不好意思,对方已经掉线!");

                                canPlay = false;

                                break;

                            }

                        }

                    } catch (IOException e) {

                        e.printStackTrace();

                    } catch (ClassNotFoundException e) {

                        e.printStackTrace();

                    }

                }

            }

        }

    }

 

    @Override

    public void mouseClicked(MouseEvent e) {

 

    }

    @Override

    public void mouseReleased(MouseEvent e) {

 

    }

 

    @Override

    public void mouseEntered(MouseEvent e) {

 

    }

 

    @Override

    public void mouseExited(MouseEvent e) {

 

    }

 

}

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++;

                else

                    yChange--;

            }

        }

        xChange = tempX;

        yChange = tempY;

        while (color == allChess[x - xChange][y - yChange]) {

            count++;

            if (xChange != 0)

                xChange++;

            if (yChange != 0) {

                if (yChange > 0)

                    yChange++;

                else

                    yChange--;

            }

        }

        return count;

    }

}