猜拳游戏(基于TCP socket的编程)

本地版猜拳游戏(基于TCP socket的编程)

Designed by Rossi


被迫营业的产物,不过最终效果还是不错的


猜拳介绍

猜拳,又称剪刀石头布,也叫“猜丁壳”,古老而简单,这个游戏的主要目的是为了解决争议,因为三者相互制约,因此不论平局几次,总会有胜负的时候。
游戏规则中,石头克剪刀,剪刀克布,布克石头。
它的起源有两种说法:一种是说起源于中国,主要是由明朝人所写"五杂俎"记载最早剪刀石头布是起源自汉朝的手势令与豁拳。另外一种说法是起源于日本19世纪。到了二十世纪剪刀石头布的游戏开始传到了欧洲与美国,而欧美都称剪刀石头布的游戏是"日本游戏",法国人称剪刀石头布为"jeu Japonais" (意思就是日本游戏)。原因就是因为到了二十世纪,日本人大量西化以及日本人到世界各国游历的原因,也对欧美推广了这个游戏,使得很多欧美人都认为剪刀石头布就是日本的游戏。


一、运行效果展示

因为这里才用的IP是本地的127.0.0.1,所以这里是可以看到两个窗口也可以进行不同的操作。如果想实现联网对战可以找上好朋友,然后把代码中设置的IP更换成双方的IP然后同时运行即可

运行截图如下:
猜拳过程

断开连接

二、代码解析

需要先运行一次Gameserver,然后再运行Demo,才能正确运行,请勿在IDE上重复编译运行
本人的代码是在eclipse编译器上编写的,并且是分块编写,一个包里包含了以下几个类文件

1.Player

首先定义玩家的属性,所以要建立Player类

代码如下:

import javax.swing.*;

public class Player {
    private String playerName,playerIP;
    private Icon playI;

    Player(String playerName){
        setPlayerName(playerName);
    }

    public Icon getPlayI() {
        return playI;
    }

    public String getPlayerIP() {
        return playerIP;
    }

    public String getPlayerName() {
        return playerName;
    }

    public void setPlayI(Icon playI) {
        this.playI = playI;
    }

    public void setPlayerIP(String playerIP) {
        this.playerIP = playerIP;
    }

    public void setPlayerName(String playerName) {
        this.playerName = playerName;
    }
}

2.PlayerPanel

有了玩家就需要去完善设计界面,便有如下PlayerPanel类

代码如下:

import javax.swing.*;
import java.awt.*;

class PlayerPanel extends JPanel{

    private JButton playerB,answerB;
    private JLabel playerLable;
    private Icon playI,answerI;
    private Player player;

    PlayerPanel(Player player){
        this.player=player;
        setBounds(0,0,120,75);
        setLayout(null);
        setBackground(Color.blue);        
        playerB = new JButton();
        playerB.setIcon(this.player.getPlayI());
        playerB.setBounds(5,5,50,50);
        answerB = new JButton();
        answerB.setBounds(65,5,50,50);
        //answerB.setEnabled(false);
        answerB.setIcon(setAnswerI(0));
        playerLable = new JLabel();
        playerLable.setBounds(5,50,100,30);
        playerLable.setText(this.player.getPlayerName());
        add(playerB);
        add(answerB);
        add(playerLable);
        validate();
    }

    public void reloadPlayer(Player player) {
        this.player = player;
        playerB.setIcon(this.player.getPlayI());
        playerLable.setText(this.player.getPlayerName());
        validate();
    }

    public void reloadAnswerI(int answer){
        answerB.setIcon(setAnswerI(answer));
        answerB.revalidate();
    }

    public Icon setAnswerI(int playI) {
        Icon icon;
        String imageName[]={"石头.png","剪刀.png","布.png"};
        String imagePath="随机动画.gif";
         switch (playI){
            case 1:imagePath = imageName[0];break;
            case 2:imagePath = imageName[1];break;
            case 3:imagePath = imageName[2];break;
        }
        icon = new ImageIcon(imagePath);
        validate();
        return icon;
    }
}

3.AnswerPanel

有玩家就会有对手,这里同样设置另一个应答客户端AnswerPanel类

代码如下:

import javax.swing.*;

public class AnswerPanel extends JPanel {

    JButton stone,scissors,cloth;
    String imageName[]={"石头.png","剪刀.png","布.png"};
    Icon icon[]= new Icon[imageName.length];
    Box box;

    AnswerPanel(){
        setSize(60,300);
        setVisible(true);

        for (int i=0;i<icon.length;i++){
            icon[i]=new ImageIcon(imageName[i]);
        }
        stone = new JButton(icon[0]);
        stone.setSize(50,50);
        scissors = new JButton(icon[1]);
        scissors.setSize(50,50);
        cloth = new JButton(icon[2]);
        cloth.setSize(50,50);
        box=Box.createVerticalBox();
        box.add(stone);
        box.add(scissors);
        box.add(cloth);
        add(box);
        validate();
    }
}

以上两个模块就构成了玩家客户机和对手客户机两个窗口

4.Table

细节搞定了,现在需要进行游戏界面设计和连接的建立工作了,所以就有了下面的Table类

代码如下:

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.UnknownHostException;

public class Table extends JPanel implements Runnable, ActionListener {


    Player self,competitor;
    PlayerPanel playerPanel1, playerPanel2;
    JButton connection,play;

    Socket socket=null;
    DataInputStream in=null;
    DataOutputStream out=null;
    Thread thread;
    boolean isConnect;
    int answer,result;


    Table (){

        setBounds(5,5,400,400);
        setBackground(Color.PINK);
        setLayout(null);
        setVisible(true);

        isConnect=false;
        connection=new JButton("连接服务器");
        connection.setSize(100,40);
        connection.setLocation(172,400);
        connection.addActionListener(this);
        connection.setEnabled(true);
        play=new JButton("开始");
        play.setSize(150,40);
        play.setLocation(150,170);
        play.addActionListener(this);
        play.setEnabled(false);

        self =new Player("我方");
        competitor =new Player("对手");
        self.setPlayI(new ImageIcon("我的头像.png"));
        competitor.setPlayI(new ImageIcon("对手头像.png"));
        playerPanel1 = new PlayerPanel(self);
        playerPanel2 = new PlayerPanel(competitor);
        playerPanel1.setLocation(160,315);
        playerPanel2.setLocation(160,10);
        add(playerPanel1);
        add(playerPanel2);
        add(connection);
        add(play);
        validate();        
    }

    public boolean doConnect(){
        try {
            socket=new Socket();
            InetAddress address=InetAddress.getByName("127.0.0.1");
            InetSocketAddress socketAddress=new InetSocketAddress(address,8888);
            socket.connect(socketAddress);  //建立连接
            in=new DataInputStream(socket.getInputStream());
            out=new DataOutputStream(socket.getOutputStream());
            return true;
        } catch (UnknownHostException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return false;
    }

    public void sendAnswer(int answer) {
        try {
            out.writeInt(answer);
            this.answer=answer;
        }
        catch (UnknownHostException e) {
            e.printStackTrace();
        }
        catch (IOException e) {
            e.printStackTrace();
        }

    }

    public Boolean isServerClose(Socket socket){
        try{
            socket.sendUrgentData(0xFF);//发送1个字节的紧急数据,默认情况下,服务器端没有开启紧急数据处理,不影响正常通信
            return false;
        }catch(Exception se){
            return true;
        }
    }

    public void actionPerformed(ActionEvent e){
        if(e.getSource()==connection){
            if (!isServerClose(socket)) {
                try {
                    socket.close();
                    System.out.println("正在断开连接"+socket.isClosed());
                    isConnect=false;
                    play.setEnabled(false);
                    playerPanel1.reloadAnswerI(0);
                } catch (IOException ioException) {
                    ioException.printStackTrace();
                }
                connection.setText("连接服务器");
            }
            else {
                if (doConnect()){
                    thread = new Thread(this);
                    thread.start();
                    System.out.println("已连接服务器");
                    connection.setText("断开服务器");
                    play.setEnabled(true);
                    play.setText("开始");
                    isConnect=true;
                }
            }
        }

        if(e.getSource()==play){
            play.setEnabled(false);
            play.setText("请出拳");
        }
    }

    public void run() {
        System.out.println("启动监听");
        while (true) {
            try {
                result = in.readInt();  //堵塞状态,除非读取到信息
                switch (result){
                    case 2:playerPanel2.reloadAnswerI(((answer+1)%3+1));play.setText("遗憾你输了,再来一局");play.setEnabled(true);break;
                    case 1:playerPanel2.reloadAnswerI(answer);play.setText("双方打平,再来一局");play.setEnabled(true);break;
                    case 0:playerPanel2.reloadAnswerI(((answer)%3+1));play.setText("恭喜你赢了,再来一局");play.setEnabled(true);
                }
                System.out.println("收到服务端应答" + result);
            } catch (IOException e) {
                break;
            }            
        }
    }
}

5.GameServer

有了连接之后就需要启动线程激活运行,并且加入猜拳的胜负判定机制,因此构造GameServer类来实现

代码如下:

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;

public class GameServer{
    ServerSocket server=null;
    ServerThread thread;
    Socket you=null;


    GameServer (){
        while(true){
            if(server==null) {
                try {
                    server = new ServerSocket(8888);    //启动监听
                    System.out.println("开始监听");
                } catch (IOException e1) {
                    System.out.println("启动服务器失败");   //ServerSocket对象不能重复创建
                    e1.printStackTrace();//捕捉异常栈堆
                }
            }

            if(server!=null) {
                try {
                    you = server.accept();
                    System.out.println("客户地址ַ" + you.getInetAddress());
                } catch (IOException e) {
                    System.out.println("正在等待");
                }
            }
            if(you!=null){
                //为每个客户启动一个专门的线程
                thread=new ServerThread(you);
                thread.start();
             }
            else
                continue;
        }
    }

    public static void main(String[] args) {
        new GameServer();
    }

}

class ServerThread extends Thread{

    private Socket socket;
    private DataOutputStream out=null;
    private DataInputStream in=null;
    private static int firstAnswer,secondAnswer;
    private static boolean ready;
    private int answer,result;
    private boolean isConnect,isFirst;
    private String client;

    synchronized void setFirstAnswer(int firstAnswer){
        ServerThread.firstAnswer =firstAnswer;
    }

    synchronized void setSecondAnswer(int secondAnswer){
        ServerThread.secondAnswer =secondAnswer;
    }

    synchronized int getFirstAnswer(){
        return firstAnswer;
    }

    synchronized int getSecondAnswer(){
        return secondAnswer;
    }

    synchronized void setReady(boolean ready){
        ServerThread.ready=ready;
    }

    ServerThread(Socket t){
        socket=t;
        setReady(false);
        isFirst=false;
        client=socket.getInetAddress()+":"+socket.getPort();
        try{
            in=new DataInputStream(socket.getInputStream());
            out=new DataOutputStream(socket.getOutputStream());
            System.out.println("接入客户机"+client);
        }
        catch (IOException e){}
    }

    public void run(){
        while(true){
            isConnect=!isServerClose(socket);
            if (isConnect){
                try{
                    System.out.println("准备读取客户机"+client);
                        answer=in.readInt();
                        if (getFirstAnswer()==0){
                            setFirstAnswer(answer);
                            isFirst=true;
                        }
                        else {
                            setSecondAnswer(answer);
                            setReady(true);
                        }
                }
                catch (IOException e){
                    System.out.println("客户离开"+client);
                    break;
                }

                while (ready==false){
                    try {
                        Thread.sleep(800);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }

                match();

                try{
                    out.writeInt(result);
                    if (isFirst==true)  flush();
                }
                catch (IOException e){
                    System.out.println("客户离开"+client);
                    flush();
                    break;
                }
            }
            else {
                System.out.println("客户离开:"+socket.getInetAddress());
                break;
            }
        }
    }

    private void match(){   //返回本线程可得分数,胜利锝2分,打平1分

        if (isFirst==true){
            System.out.println("是第一个出拳");
            System.out.println("second:"+secondAnswer);
            System.out.println("first:"+firstAnswer);
            result=firstAnswer-secondAnswer;
        }
        else{
            System.out.println("是第二个出拳");
            result=secondAnswer-firstAnswer;
        }
        switch (result){    
            case 0:result= 1;break;//相减为零表示双方相同
            case 1:         
            case -2:result= 2;break;//相减为1或者-2表示胜利
            default:result= 0;//相减为1或者-2表示失败
        }
    }

    private synchronized void flush(){
        firstAnswer=0;
        secondAnswer=0;
        isFirst=false;
        answer=0;
        setReady(false);
    }

    public Boolean isServerClose(Socket socket){
        try{
            socket.sendUrgentData(0xFF);//发送1个字节的紧急数据,默认情况下,服务器端没有开启紧急数据处理,不影响正常通信
            return false;
        }catch(Exception se){
            return true;
        }
    }
}

6.Demo

完成以上设计后,最后需要一个主类Demo来完成最后的操作

代码如下:

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

public class Demo {

    public static void main(String[] args) {
       Play play=new Play("我方");
       Play play2=new Play("对方");
    }
}

class Play extends JPanel
        implements ActionListener {
    Table table;
    AnswerPanel answerPanel;
    JFrame jFrame;

    Play(String userName){
        table = new Table();
        answerPanel=new AnswerPanel();
        jFrame= new JFrame(userName);
        jFrame.setTitle(userName);

        answerPanel.stone.addActionListener(this);
        answerPanel.scissors.addActionListener(this);
        answerPanel.cloth.addActionListener(this);

        jFrame.setBounds(100,100,500,500);
        jFrame.add(table,BorderLayout.CENTER);
        jFrame.add(answerPanel,BorderLayout.EAST);
        jFrame.setVisible(true);
        jFrame.validate();
        jFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
    }

    public void actionPerformed(ActionEvent e) {
        if(e.getSource()==answerPanel.stone) {

            if (table.isConnect==true && (table.play.getText()=="请出拳")){
                System.out.println("石头");
                table.sendAnswer(1);
                table.playerPanel1.reloadAnswerI(1);
                table.play.setText("等待对方中");
            }
        }
        else if(e.getSource()==answerPanel.scissors) {

            if (table.isConnect==true && (table.play.getText()=="请出拳")){
                System.out.println("剪刀");
                table.sendAnswer(2);
                table.playerPanel1.reloadAnswerI(2);
                table.play.setText("等待对方中");
            }
        }
        if(e.getSource()==answerPanel.cloth) {

            if (table.isConnect==true && (table.play.getText()=="请出拳")){
                System.out.println("布");
                table.sendAnswer(3);
                table.playerPanel1.reloadAnswerI(3);
                table.play.setText("等待对方中");
            }
        }
    }
}

三、总结

像GUI界面的设计还可以再进一步去完善,这些就不多讲。当初设计的时候我还想加入一个背景音乐的,但是调试了四次都是没办法调用音乐出来,因此这个想法不得不先放一放。(如果有大佬愿意指点小弟的话还请不吝赐教)
这个东西也花了我不少时间,工程量甚至比我之前做的三子棋还要大。尽管效果和预想不太一样但是最后操作的时候感觉还是不错的。

所有的源代码我已经贴出来了,如果各位看官有什么好的想法也欢迎各位在蒟蒻的评论区下留言

  • 14
    点赞
  • 90
    收藏
    觉得还不错? 一键收藏
  • 21
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 21
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值