上一个
下一个

互联网五子棋对战

0.前言

放假已经一段时间了,前一阶段除了家教主要在调理身心,简而言之就是摆烂——摆烂生活真是让人流连忘返啊。作为康复运动,打算把这学期一些有趣的小程序传上来。

这个开学初做的,难度不大。当初我其实还传到服务器上了,可惜现在服务器到期了。要是愿意折腾,我感觉应该可以做出那种QQ游戏大厅的感觉。

1.服务器端代码

				
					import javax.swing.*;
import java.awt.*;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Date;

public class TicTacToeServer extends JFrame implements TicTacToeConstants {
    public static void main(String[] args){
        TicTacToeServer frame = new TicTacToeServer();
    }
    public TicTacToeServer(){
        JTextArea jtaLog = new JTextArea();
        JScrollPane scrollPane = new JScrollPane(jtaLog);//创建滚动条
        add(scrollPane, BorderLayout.CENTER);//添加至Frame

        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setSize(300,300);
        setTitle("井字棋");
        setVisible(true);

        try{
            //创建socket服务
            ServerSocket serverSocket = new ServerSocket(8000);
            jtaLog.append(new Date()+":服务端端口号为8000\n");
            //会话号码
            int sessionNo = 1;
            //准备为两个玩家创建会话
            while (true){
                jtaLog.append(new Date()+":等待玩家加入会话"+sessionNo+'\n');
                //链接玩家1
                Socket player1 = serverSocket.accept();
                jtaLog.append(new Date()+":玩家1加入了会话"+sessionNo+'\n');
                jtaLog.append("玩家1的IP地址为:"+player1.getInetAddress().getHostAddress()+'\n');
                //告诉玩家1是一号玩家
                new DataOutputStream(player1.getOutputStream()).writeInt(PLAYER1);
                //链接玩家2
                Socket player2 = serverSocket.accept();
                jtaLog.append(new Date()+":玩家2加入了会话"+sessionNo+'\n');
                jtaLog.append("玩家2的IP地址为:"+player1.getInetAddress().getHostAddress()+'\n');
                //告诉玩家2是二号玩家
                new DataOutputStream(player2.getOutputStream()).writeInt(PLAYER2);

                //展示添加线程的会话号
                jtaLog.append(new Date()+":为会话"+ sessionNo +"创建线程"+"\n");
                sessionNo += 1;
                //创建线程
                HandleASession task = new HandleASession(player1,player2);
                new Thread(task).start();

            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}
//定义如何处理玩家线程
class HandleASession implements Runnable,TicTacToeConstants{
    private Socket player1;    private Socket player2;
    //创建并初始化棋盘
    private char [][] cell = new char[mapSize][mapSize];

    private int newGame = 0;
    private boolean player1First = false;

    private DataInputStream fromPlayer1;
    private DataInputStream fromPlayer2;
    private DataOutputStream toPlayer1;
    private DataOutputStream toPlayer2;

    private boolean continueToPlay = true;

    //构造函数
    public HandleASession(Socket p1,Socket p2){
        this.player1 = p1;
        this.player2 = p2;
        //初始化地图
        readyStart();
    }
    private void readyStart(){
        //初始化地图
        for(int i=0;i<mapSize;i++)
            for(int j=0;j<mapSize;j++)
                cell[i][j] = ' ';
        player1First=!player1First;
    }
    private void gameStep(DataInputStream fp1,DataInputStream fp2,DataOutputStream tp1,DataOutputStream tp2) throws IOException, InterruptedException {
        //从玩家1中得到一步
        int row = fp1.readInt();
        int column = fp1.readInt();
        System.out.println(player1First?"p1 :":"p2 :"+row+"  "+column);
        cell[row][column] = player1First ? 'X':'O';

        //判断游戏局势
        if(isWon('X',row,column)){
            tp1.writeInt(PLAYERX_WON);
            tp2.writeInt(PLAYERX_WON);
            sendMove(tp2,row,column);
            newGame = 1;
        }
        else if(isWon('O',row,column)){
            tp1.writeInt(PLAYERO_WON);
            tp2.writeInt(PLAYERO_WON);
            sendMove(tp2,row,column);
            newGame = 1;
        }
        else if (isFull()) {
            tp1.writeInt(DRAW);
            tp2.writeInt(DRAW);
            sendMove(tp2,row,column);
            newGame = 1;
        }
        else{
            tp2.writeInt(CONTINUE);
            sendMove(tp2,row,column);
            //然后游戏继续,所以下面代码在while中
            //从玩家2中得到移动
            int row2 = fp2.readInt();
            int column2 = fp2.readInt();
            System.out.println(player1First?"p2 :":"p1 :"+row2+"  "+column2);
            cell[row2][column2] = player1First ? 'O':'X';

            //检查玩家2是否赢了
            if(isWon('O',row2,column2)){
                tp1.writeInt(PLAYERO_WON);
                tp2.writeInt(PLAYERO_WON);
                sendMove(tp1,row2,column2);
                newGame = 1;
            }
            else if(isWon('X',row2,column2)){
                tp1.writeInt(PLAYERX_WON);
                tp2.writeInt(PLAYERX_WON);
                sendMove(tp1,row2,column2);
                newGame = 1;
            }
            else{
                //告诉玩家1继续,为什么不需要判断满格子,因为九宫格游戏不会出现玩家2下完下满了的情况
                tp1.writeInt(CONTINUE);
                sendMove(tp1,row2,column2);
            }
        }
        if(newGame == 1){
            Thread.sleep(1000);
            System.out.println("New Game");
            readyStart();
            newGame = 0;
        }
    }
    @Override
    public void run() {
        try{
            //添加输入流和输出流
            fromPlayer1 = new DataInputStream(player1.getInputStream());
            fromPlayer2 = new DataInputStream(player2.getInputStream());
            toPlayer1 = new DataOutputStream(player1.getOutputStream());
            toPlayer2 = new DataOutputStream(player2.getOutputStream());

            //随便写点什么让玩家1知道开始了
            toPlayer1.writeInt(1);

            //不断发送信号
            while(true){
                if(player1First)
                    gameStep(fromPlayer1,fromPlayer2,toPlayer1,toPlayer2);
                else
                    gameStep(fromPlayer2,fromPlayer1,toPlayer2,toPlayer1);
            }
        } catch (IOException | InterruptedException e) {
            if(player1.isClosed()) {
                System.out.println("玩家1退出");
            }
            else if(player2.isClosed()) {
                System.out.println("玩家2退出");
            }
            else{
                System.out.println("不知道什么问题");

            }
        }
    }
    private void sendMove(DataOutputStream out,int row, int column) throws IOException{
        System.out.println(row+"  "+column);
        out.writeInt(row);
        out.writeInt(column);
    }

    private boolean isFull(){
        for(int i=0;i<mapSize;i++)
            for(int j=0;j<mapSize;j++)
                if(cell[i][j]==' ')
                    return false;
        return true;
    }
    public int left_right(char token,int r, int c) {
        // 定义一个计数器
        int count = 0;
        // 遍历判断 先向右 列向右遍历,行不变
        // 遍历起点: 当前棋子的列+1
        for (int i = c + 1; i < mapSize; i++) {
            // 判断右边棋子是否与当前棋子一致
            if (cell[r][i] == token) {
                count++;// 找到了一颗相同数值的棋子
            } else {
                break;// 找到了一颗不同数值的棋子  不再继续找
            }
        }
        //向左
        for(int i = c-1;i>=0;i--){
            if(cell[r][i] == token){
                count++;
            }else{
                break;
            }
        }
        if(cell[r][c]==token) count++;// 包括当前下的这颗棋子 因为在不同人下的时候都会判断一次输赢,所以这里也要做区分
        return count;
    }
    public int up_down(char token, int r, int c){
        int count = 0;
        //向上
        for(int i = r-1; i>=0;i--){
            if(cell[i][c] == token){
                count++;
            }else{
                break;
            }
        }
        //向下
        for(int i = r+1;i<mapSize;i++){
            if(cell[i][c] == token){
                count++;
            }else{
                break;
            }
        }
        if(cell[r][c]==token) count++;
        return count;
    }
    public int leftUp_RightDown(char token, int r, int c){
        int count = 0;
        //右下
        for(int i = r+1,j=c+1;i<mapSize&&j<mapSize;i++,j++){
            if(cell[i][j] == token){
                count++;
            }else{
                break;
            }
        }
        //左上
        for(int i=r-1,j=c-1;i>=0&&j>=0;i--,j--){
            if(cell[i][j] == token){
                count++;
            }else{
                break;
            }
        }
        if(cell[r][c]==token) count++;
        return count;
    }
    public int leftDown_RightUp(char token, int r, int c){
        int count = 0;
        //左下
        for(int i = r+1,j=c-1;i<mapSize&&j>=0;i++,j--){
            if(cell[i][j] == token){
                count++;
            }else{
                break;
            }
        }
        //右上
        for(int i=r-1,j=c+1;i>=0&&j<mapSize;i--,j++){
            if(cell[i][j] == token){
                count++;
            }else{
                break;
            }
        }
        if(cell[r][c]==token) count++;
        return count;
    }
    private boolean isWon(char token,int row,int column){
//        for(int i=0;i<3;i++){
//            for(int j=0;j<3;j++){
//                System.out.print(cell[i][j]!=' '?cell[i][j]:'@');
//            }
//            System.out.println();
//        }
//        for(int i=0;i<3;i++)
//            if(cell[i][0]==token&&cell[i][1]==token&&cell[i][2]==token)
//                return true;
//        for(int j=0;j<3;j++)
//            if(cell[0][j]==token&&cell[1][j]==token&&cell[2][j]==token)
//                return true;
//        if(cell[0][0]==token&&cell[1][1]==token&&cell[2][2]==token) return true;
//        if(cell[0][2]==token&&cell[1][1]==token&&cell[2][0]==token) return true;
        return left_right(token, row, column) >= winNum
                || up_down(token, row, column) >= winNum
                || leftUp_RightDown(token, row, column) >= winNum
                || leftDown_RightUp(token, row, column) >= winNum;
    }
}
				
			

2.客户端

				
					import javax.swing.*;
import javax.swing.border.LineBorder;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
import java.net.UnknownHostException;

public class TicTacToeClient extends JFrame implements Runnable,TicTacToeConstants {
    public static void main(String[] args)
    {
        TicTacToeClient game = new TicTacToeClient();
    }
    private boolean myTurn = false;//是否是我的回合
    private boolean first = false;
    private char myToken = ' ';
    private char otherToken = ' ';
    private boolean anotherGame = false;
    private Cell [][] cell = new Cell[mapSize][mapSize];
    //显示标题的
    private JLabel jlblTitle = new JLabel();
    //显示状态的
    private JLabel jlblStatus = new JLabel();
    private JLabel jlblFirst = new JLabel();
    //显示当前选中行和列
    private int rowSelected;
    private int columnSelected;
    //从服务端得到的,和发送的数据
    private DataInputStream fromServer;
    private DataOutputStream toServer;
    //继续玩吗?
    private boolean continuePlay = true;
    //等待对面玩家下棋
    private boolean waiting = true;
    public int player;
    //这里写服务器ip
    //private String host = "39.105.109.67";
    private String host = "localhost";
    public TicTacToeClient(){
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setSize(40*mapSize,40*mapSize);
        setTitle("游戏端");
        setVisible(true);
        for(int i=0;i<mapSize;i++)
            for(int j=0;j<mapSize;j++)
                cell[i][j]=new Cell(i,j);//连续赋值
        init();
        connectToServer();
    }
    private void clearBoard(){
        for(int i=0;i<mapSize;i++)
            for(int j=0;j<mapSize;j++){
                cell[i][j].setToken(' ');
            }
    }
    //初始化UI
    public void init(){
        JPanel p = new JPanel();
        p.setLayout(new GridLayout(mapSize,mapSize,0,0));
        for(int i=0;i<mapSize;i++)
            for(int j=0;j<mapSize;j++){
                cell[i][j].setToken(' ');
                p.add(cell[i][j]);
            }
        //一些设置
        p.setBorder(new LineBorder(Color.BLACK,1));
        jlblTitle.setHorizontalAlignment(JLabel.CENTER);//居中
        jlblTitle.setFont(new Font("黑体",Font.BOLD,16));
        jlblTitle.setBorder(new LineBorder(Color.BLACK,1));
        jlblStatus.setBorder(new LineBorder(Color.BLACK,1));
        jlblFirst.setBorder(new LineBorder(Color.BLACK,1));

        add(jlblTitle,BorderLayout.NORTH);
        add(p,BorderLayout.CENTER);
        add(jlblStatus,BorderLayout.SOUTH);
        add(jlblFirst,BorderLayout.EAST);

    }
    private void connectToServer(){
        try{
            Socket socket;
            socket = new Socket(host,8000);
            fromServer = new DataInputStream(socket.getInputStream());
            toServer = new DataOutputStream(socket.getOutputStream());

        } catch (UnknownHostException e) {
            throw new RuntimeException(e);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        Thread thread = new Thread(this);
        thread.start();
    }

    @Override
    public void run() {
        try{
            player = fromServer.readInt();
            if(player==PLAYER1){
                first = true;
                myToken='X';
                otherToken='O';
                jlblTitle.setText("你是玩家1,使用 X");
                jlblStatus.setText("等待玩家2加入");
                //从服务端接受开始信号
                fromServer.readInt();
                //游戏开始
                jlblStatus.setText("玩家2已加入,由您先行,请下棋");
                myTurn=true;
                jlblFirst.setText("先手");
            }
            else if(player==PLAYER2){
                first = false;
                myToken='O';
                otherToken='X';
                jlblTitle.setText("你是玩家2,使用 O");
                jlblStatus.setText("等待玩家1下棋");
                jlblFirst.setText("后手");
            }
            //不断游戏过程中
            while (continuePlay){
                System.out.println(player+"    "+myToken+"  myTurn  "+myTurn);
                if(first){
                    if(anotherGame){
                        receiveInfoFromServer();
                        anotherGame=false;
                    }
                    waitForPlayerAction();
                    sendMove();
                    receiveInfoFromServer();
                }
                else {
                    receiveInfoFromServer();
                    waitForPlayerAction();
                    sendMove();
                }
            }
        } catch (IOException | InterruptedException e) {
            throw new RuntimeException(e);
        }
    }

    private void waitForPlayerAction() throws InterruptedException{
        while(waiting){
            Thread.sleep(100);
        }
        waiting = true;
    }
    private void sendMove() throws IOException{
        System.out.println("sendMove");
        toServer.writeInt(rowSelected);
        toServer.writeInt(columnSelected);
    }
    public void newGame() throws IOException {
        System.out.println("New Game");
//        int status = fromServer.readInt();
//        if(status==5) myTurn=false;
//        else if(status==6) myTurn=true;
//        else return;
        first = !first;
        myTurn = first;
        jlblFirst.setText(first?"先手":"后手");
        clearBoard();
        continuePlay=true;
        anotherGame = true;
    }
    private void receiveInfoFromServer() throws IOException{
        int status = fromServer.readInt();
        System.out.println("Status: "+status);

        if(status==PLAYERX_WON){
            //continuePlay=false;
            if(myToken=='X'){
                jlblStatus.setText("简简单单,不过如此");
            }else if(myToken=='O'){
                jlblStatus.setText("略输一筹,来日再战");
                receiveMove();
            }
            newGame();
        }
        else if(status==PLAYERO_WON){
            //continuePlay=false;
            if(myToken=='O'){
                jlblStatus.setText("简简单单,不过如此");
            }else if(myToken=='X'){
                jlblStatus.setText("略输一筹,来日再战");
                receiveMove();
            }
            newGame();
        }
        else if(status==DRAW){
            //continuePlay=false;
            jlblStatus.setText("不分上下,皆大欢喜");
            if(myToken=='O') receiveMove();
            newGame();
        }
        else{
            receiveMove();
            jlblStatus.setText("我的回合");
            myTurn = true;
        }
    }
    private void receiveMove() throws IOException{
        int row = fromServer.readInt();
        int column = fromServer.readInt();
        cell[row][column].setToken(otherToken);
    }
    public class Cell extends JPanel{
        private int row;
        private int column;
        private char token=' ';
        public Cell(int i,int j){
            this.row=i;
            this.column=j;
            //setBorder(new LineBorder(Color.BLACK,1));
            addMouseListener(new ClickListener());
        }
        public char getToken(){
            return token;
        }
        public void setToken(char c){
            token = c;
            repaint();
        }
        protected void paintComponent(Graphics g){
            super.paintComponent(g);
            if(row==0) {

                g.drawLine(getWidth()/2,getHeight()/2,getWidth()/2,getHeight());
            }
            else if(row==mapSize-1){

                g.drawLine(getWidth()/2,0,getWidth()/2,getHeight()/2);
            }
            else{

                g.drawLine(getWidth()/2,0,getWidth()/2,getHeight());
            }
            if(column==0){
                g.drawLine(getWidth()/2,getHeight()/2,getWidth(),getHeight()/2);

            }
            else if(column==mapSize-1){
                g.drawLine(0,getHeight()/2,getWidth()/2,getHeight()/2);

            }
            else{
                g.drawLine(0,getHeight()/2,getWidth(),getHeight()/2);

            }
            if((row==3&&column==3)||(row==3&&column==9)||(row==3&&column==15)
            ||(row==9&&column==3)||(row==9&&column==9)||(row==9&&column==15)
            ||(row==15&&column==3)||(row==15&&column==9)||(row==15&&column==15)){
                g.fillOval(getWidth()/2-ovalMargin*4,getHeight()/2-ovalMargin*4,ovalMargin*8,ovalMargin*8);
            }


            if (token == 'X') {
                g.setColor(Color.BLACK);
                //g.fillOval(getWidth()-ovalSize/2,getHeight()-ovalSize/2,getWidth()-ovalSize,getHeight()-ovalSize);
                g.fillOval(ovalMargin,ovalMargin,getWidth()-2*ovalMargin,getHeight()-2*ovalMargin);
            }
            else if(token=='O'){
                g.setColor(Color.WHITE);
                g.fillOval(ovalMargin,ovalMargin,getWidth()-2*ovalMargin,getHeight()-2*ovalMargin);
                g.setColor(Color.BLACK);
                g.drawOval(ovalMargin,ovalMargin,getWidth()-2*ovalMargin,getHeight()-2*ovalMargin);
            }
        }
        private class ClickListener extends MouseAdapter{
            public void mouseClicked(MouseEvent e){
                if((token==' ')&&myTurn){
                    setToken(myToken);
                    System.out.println(""+row+"   "+column);
                    myTurn=false;
                    rowSelected=row;
                    columnSelected=column;
                    jlblStatus.setText("等待对面玩家下棋");
                    waiting=false;
                }
            }
        }
    }
}

				
			

3.接口定义

				
					public interface TicTacToeConstants {

    public static int ovalMargin =1;
    public static int mapSize = 19;
    public static int winNum = 5;
    public static int PLAYER1 = 1;
    public static int PLAYER2 = 2;
    public static int PLAYERX_WON = 1;
    public static int PLAYERO_WON = 2;
    public static int DRAW = 3;
    public static int CONTINUE = 4;
}

				
			

4.效果

有一个小小的说明,这里是TCP/IP协议,和这学期末的局域网聊天不是一个协议哦——虽然我也只是拿来主义在用,不过还是在此说明一下。

订阅评论
提醒
1 评论
最旧
最新 最多投票
内联反馈
查看所有评论
1 年 前

整挺好

《互联网五子棋对战》

1
0
希望看到您的想法,请您发表评论x