1. 程式人生 > 實用技巧 >結對程式設計4——黃金點小遊戲實現區域網聯機

結對程式設計4——黃金點小遊戲實現區域網聯機

一、專案成員

2018141461085 龔澤楠
2018141461012 蔡鏵榮

二、專案名稱

黃金點小遊戲

三、專案簡介

遊戲規則: N個同學( N通常大於 10 ),每人寫一個 0~100 之間的有理數 (不包括 0或100) ,交給裁判算出所有數字的平均值然後乘以 0.618 (所謂黃金分割常數),得到 G值。提交的數字最靠近 G(取絕對值)的同 學得到 N分,離 G最遠的同學得到- 2分,其他同學得 0分。

黃金點遊戲其實是一個博弈論G值(博弈值,在黃金點遊戲中為黃金點;在市場競標中為標線;在拍賣中為成交額)的資料收集實驗,因此該專案為社會公益專案,普遍情況黃金點遊戲專案都會將最後資料或實時資料上傳至網路公開使用。
這個遊戲規定第一名得到全部的分數, 第二名(不管多接近)到倒數第二名都是 0 分,最後一名還要倒扣分。

四、彙報概要

本次主要實現功能

1、提供了檢視歷史記錄的功能(顯示歷史對局的黃金點以及每人提交點數的資訊)以便玩家分析

2、實現了局域網對局,玩家在區域網下可以聯機對戰

本次主要完善功能

1、完善了客戶端程式,將計算黃金點,排名等功能移植到伺服器端,減輕了客戶端負擔。

2、完善了互動過程,增加了準備階段,全部玩家準備後才開始遊戲。

五、功能實現

1、檢視歷史記錄

實現方法:

在每一輪遊戲後將資料儲存到History類中,然後需要顯示時呼叫相關函式顯示視窗。

實現程式碼如下:

History類:

package gp.Client;

public class History {
    String point;//每個人提交的點數
    String GoldenPoint;//黃金點
    String score;//本人的得分
    String MyPoint;//本人提交的點數
    String distance;//本人距離黃金點的距離

    
public History(String point, String GoldenPoint, String score, String MyPoint, String distance){ this.point = point; this.GoldenPoint = GoldenPoint; this.score = score; this.MyPoint = MyPoint; this.distance = distance; } public void setDistance(String distance) {
this.distance = distance; } public void setScore(String score) { this.score = score; } public void setPoint(String point) { this.point = point; } public void setGoldenPoint(String goldenPoint) { GoldenPoint = goldenPoint; } public void setMyPoint(String myPoint) { MyPoint = myPoint; } public String getPoint() { return point; } public String getDistance() { return distance; } public String getGoldenPoint() { return GoldenPoint; } public String getMyPoint() { return MyPoint; } public String getScore() { return score; } }

新增歷史記錄函式:

//新增歷史記錄
    public static void addHistory(Vector<History> histories, Player[] players, int num, String gp){
        String point = players[0].point;
        String GoldenPoint = gp;
        String score = "0";
        String MyPoint = players[num].point;
        String distance = String.valueOf(players[num].distance);
        if(players[num].rank==1){
            score = String.valueOf(players.length);
        }else if(players[num].rank == players.length){
            score = String.valueOf(-2);
        }
        for (int i = 1; i < players.length; i++){
            point = point + "," + players[i].point;
        }
        histories.add(new History(point, GoldenPoint, score, MyPoint, distance));
    }

面板顯示:

//檢視歷史記錄點選事件(顯示歷史記錄介面)
        history_Button.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseClicked(MouseEvent e) {
                super.mouseClicked(e);
                JFrame HistoryUI = new JFrame();
                String[] columnName= {"輪數", "黃金點", "您的得分", "您提交的點數", "您點數距離黃金點的距離", "其他人點數"};
                String[][] data = new String[histories.size()][6];
                for(int i = 0; i < histories.size(); i++) {
                    for(int j = 0; j < 6; j++) {
                        if(j == 0) {
                            data[i][j] = String.valueOf(i + 1);
                        }
                        if (j == 1) {
                            data[i][j] = histories.get(i).getGoldenPoint();
                        }
                        if (j == 2) {
                            data[i][j] = histories.get(i).score;
                        }
                        if (j == 3) {
                            data[i][j] = histories.get(i).MyPoint;
                        }
                        if (j == 4) {
                            data[i][j] = histories.get(i).distance;
                        }
                        if (j == 5) {
                            data[i][j] = histories.get(i).point;
                        }
                    }
                }
                JTable table = new JTable(data, columnName);
                table.setEnabled(false);
                JScrollPane jsp = new JScrollPane(table);
                HistoryUI.add(jsp);
                HistoryUI.setTitle("History");
                HistoryUI.setPreferredSize(new Dimension(600, 800));
                HistoryUI.pack();
                HistoryUI.setVisible(true);
            }
        });

2、實現區域網對局


使用Java Socket實現區域網對戰

實現程式碼如下:

客戶端接收伺服器訊息執行緒:

//獲取伺服器發來訊息執行緒
    @Override
    public void run() {

        try {

            DataInputStream dis = new DataInputStream(socket.getInputStream());
            DataOutputStream dos = new DataOutputStream(socket.getOutputStream());

            dos.write(player_name.getBytes());

            while (true) {
                //repaint();
                byte[] b = new byte[3000];
                dis.read(b);
                String msg = new String(b).trim();
                System.out.println("服務端發來:" + msg);
                JSONObject json = new JSONObject(msg);
                int commandType = json.getInt("type");
                switch (commandType){
                    case 1: //收到確認玩家提交資料,更新其他人狀態 (如果是本機提交則提示玩家等待其他人提交)
                        int num = json.getInt("num");
                        upDatePlayer_table_isSubmitted(num);
                        if(num == LocalNumber){
                            submit_textField.setText("");
                            cardLayout.show(card_panel, "Card2");
                            submit_Button.setVisible(false);
                        }
                        break;
                    case 2: //準備階段接受和更新玩家資訊
                        playerlist = new Player[json.getInt("num")];
                        PlayerCtrl.getLocalPlayer(json, playerlist);//從json中解析玩家列表
                        getLocalNumber(playerlist);//獲取本機玩家序號
                        UpDateReady(playerlist);
                        break;
                    case 3: //收到全部準備,更新狀態輪數等資訊
                        setPlayer_table(playerlist);
                        round = json.getInt("round");
                        round_label.setText("當前遊戲輪數為:" + round);
                        cardLayout.show(card_panel, "Card1");
                        submit_Button.setVisible(true);
                        break;
                    case 4: //收到遊戲結果
                        PlayerCtrl.getResult(json, playerlist);
                        if(json.getInt("num") == playerlist.length - 1){
                            upDateResult_Table(playerlist);
                        }
                        upDatePlayer_table_score(playerlist);
                        point_label.setText("您的總得分為:" + playerlist[LocalNumber].score);
                        rank_label.setText("您的總排名為:" + PlayerCtrl.getRank(playerlist, LocalNumber));
                        break;
                    case 5://初始化玩家準備狀態
                        String playerReady = json.getString("msg");
                        String[] s = playerReady.split(" ");//從json中解析玩家準備狀態
                        if(s.length>1){
                            UpDateReady(s);
                        }else if(s.length == 1 && !s[0].isEmpty()){
                            UpDateReady(s);
                        }
                        break;
                    case 6://更新玩家準備資訊
                        int num2 = json.getInt("msg");
                        UpDateReady(num2);
                        if(num2 == LocalNumber){
                            ready_button.setVisible(false);
                        }
                        break;
                    case 7://接受黃金點資訊,並記錄歷史
                        String GoldenPoint = json.getString("GoldenPoint");
                        gp_label.setText("本輪遊戲黃金點為:" + GoldenPoint);
                        PlayerCtrl.addHistory(histories, playerlist, LocalNumber, GoldenPoint);
                        break;
                    case 8://重置 開始下一輪準備
                        cardLayout.show(card_panel, "Card3");
                        submit_Button.setVisible(false);
                        ready_button.setVisible(true);
                        UpDateReady(playerlist);
                        break;
                    default:
                        System.out.println("json資料不合法");
                        break;
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }   catch (JSONException e) {
            e.printStackTrace();
        }
    }

客戶端傳送訊息程式程式碼:

//提交按鈕點選事件(向伺服器傳送type1)
        submit_Button.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseClicked(MouseEvent e) {
                super.mouseClicked(e);
                String point = submit_textField.getText();
                if (point.isEmpty()) {
                    JOptionPane.showMessageDialog(null, "輸入數字為空,請重新輸入");
                }else if(!isString(point)) {
                    JOptionPane.showMessageDialog(null, "請輸入0-100有理數,不能為其他字元");
                    submit_textField.setText("");
                }
                else {
                    try {
                        JSONObject json = new JSONObject();
                        json.put("type", 1);
                        json.put("mark", LocalNumber);
                        json.put("point", point);
                        ToServer.sendMsg(json.toString(), new DataOutputStream(socket.getOutputStream()));
                    } catch (JSONException e1) {
                        e1.printStackTrace();
                    } catch (IOException ioException) {
                        ioException.printStackTrace();
                    }
                    /*submit_textField.setText("");
                    cardLayout.show(card_panel, "Card2");
                    submit_Button.setVisible(false);*/
                }
            }
        });

        //準備按鈕點選事件(向伺服器傳送type2)
        ready_button.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseClicked(MouseEvent e) {
                super.mouseClicked(e);

                try{
                    JSONObject json = new JSONObject();
                    json.put("type", 2);
                    json.put("mark", LocalNumber);
                    ToServer.sendMsg(json.toString(), new DataOutputStream(socket.getOutputStream()));
                } catch (JSONException e1) {
                    e1.printStackTrace();
                } catch (IOException ioException) {
                    ioException.printStackTrace();
                }
            }
        });

伺服器接受客戶端訊息程式碼:

@Override
    public void run() {
        try {
            while (true) {
                byte [] b = new byte[10240];
                this.dis.read(b);
                String clientMsg = new String(b).trim();// 來自客戶端的命令
                JSONObject json = new JSONObject(clientMsg);// 轉化為json資料格式
                System.out.println("伺服器收到:"+clientMsg);
                switch (json.getInt("type")) {
                    case 1://接收提交黃金點資訊
                        receivePoint(json);
                        break;
                    case 2://接受準備資訊
                        receiveReady(json);
                        break;
                    default:
                        System.out.println("json資料不合法");
                        break;
                }
            }
        } catch (Exception e) {
            // TODO: handle exception
        }
    }
    //接收提交黃金點資訊處理程式
    private void receivePoint(JSONObject json) throws JSONException, IOException {
        int num = json.getInt("mark");
        String point = json.getString("point");
        sc.player.get(num).point = point;
        sc.player.get(num).setSubmitted(true);
        sc.UpDateSubmit(num);
        sc.CheckSubmit();
        if(sc.getAllSubmitted()){
            sc.sendResult();
        }
    }
    //接收準備資訊處理程式
    private void receiveReady(JSONObject json) throws JSONException, IOException{
        int num = json.getInt("mark");
        sc.player.get(num).setReady(true);
        sc.CheckReady();
        sc.upDateReady(num);
        if (sc.player.size() > 3 && sc.getReady()) {
            String player_name = "";
            for(int i = 0; i < sc.player.size(); i++){
                player_name = player_name + " " + sc.player.get(i).name;
            }
            sc.setPlayer(player_name);
        }
    }

伺服器傳送訊息程式程式碼:

 json.put("round", round);
        System.out.println(json.toString());
        sendMsg(json);
    }

    //傳送更新新加入玩家的資訊
    public void upDatePlayer(String playername) throws JSONException, IOException {
        JSONObject json = new JSONObject();
        json.put("type", 2);
        json.put("msg", playername.trim());
        json.put("num", player.size());
        System.out.println(json.toString());
        sendMsg(json);
    }

    //傳送玩家提交點數訊息
    public void UpDateSubmit(int num) throws JSONException, IOException {
        JSONObject json = new JSONObject();
        json.put("type", 1);
        json.put("num", num);
        player.get(num).setSubmitted(true);
        System.out.println("伺服器傳送更新狀態:" + json.toString());
        sendMsg(json);
    }

    //傳送最終結果訊息(排名、點數、分數等)
    public void sendResult() throws JSONException, IOException {
        para para = new para();
        String[] PointList = new String[player.size()];
        for(int i = 0; i < player.size(); i++){
            PointList[i] = player.get(i).point;
        }
        para.Operate(para, PointList, player.size());
        for(int i = 0; i < player.size(); i++){
            JSONObject json = new JSONObject();
            json.put("type", 4);
            json.put("num", i);
            json.put("rank", para.getRank()[i]);
            json.put("distance", para.getDistance()[i]);
            json.put("point", player.get(i).point);
            System.out.println("伺服器傳送第" + i + "位選手成績:" + json.toString());
            sendMsg(json);
            //防止資料堆疊 客戶端無法識別 一條一條傳送
            try {
                Thread.currentThread().sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        //傳送黃金點資訊
        sendGoldenPoint(String.valueOf(para.getMsum()));
        //等待時間
        try {
            Thread.currentThread().sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //傳送開始下一輪訊息
        JSONObject json = new JSONObject();
        json.put("type", 8);
        sendMsg(json);
        Reset();
    }

    //傳送玩家準備訊息
    public void upDateReady(String playerReady) throws JSONException, IOException{
        JSONObject json = new JSONObject();
        json.put("type", 5);
        json.put("msg", playerReady.trim());
        System.out.println("伺服器傳送更新狀態:" + json.toString());
        sendMsg(json);
    }

    //傳送玩家準備訊息
    public void upDateReady(int num) throws JSONException, IOException{
        JSONObject json = new JSONObject();
        json.put("type", 6);
        json.put("msg", num);
        System.out.println(json.toString());
        sendMsg(json);
    }

    //傳送黃金點結果
    public void sendGoldenPoint(String GoldenPoint) throws JSONException, IOException{
        JSONObject json = new JSONObject();
        json.put("type", 7);
        json.put("GoldenPoint", GoldenPoint);
        System.out.println("黃金點是:" + json.toString());
        sendMsg(json);
    }

    private void sendMsg(JSONObject json) throws IOException {
        for (int i = 0; i < player.size(); i++) {
            dos.get(i).write(json.toString().getBytes());
        }
        System.out.println("伺服器傳送" + json.toString());
    }

六、功能完善

1、伺服器端計算程式

public para Operate(para para,String list[],int n)
    {
        for(int i = 0;i < n;i++)
        {//檢測資料是否異常,並複製到另一個double陣列
            list2[i] = Double.parseDouble(list[i]);
        }

        //計算黃金點
        for(int i = 0;i < n;i++)
        {
            sum += list2[i];
        }
        Msum = (sum / n) * 0.618;

        //每位選手離黃金點的距離
        for(int i = 0;i < n;i++)
        {
            Distance[i] = abs(list2[i] - Msum);
            list3[i] = Distance[i];
        }

        //排名
        //Arrays.sort(list3);           //距離的升序排序
        for(int i = 0; i < n - 1;i++)
        {
            for(int j = 0; j < n - 1;j++)
            {
                if(list3[j] > list3[j + 1])
                {
                    double temp = list3[j];
                    list3[j] = list3[j+1];
                    list3[j+1] = temp;
                }
            }
        }
        ExRank[0] = 1;
        for(int i = 1;i < n;i++)
        {
            NumberFormat nf = NumberFormat.getInstance();
            nf.setGroupingUsed(false);
            String DATA1 = nf.format(list3[i]);
            String DATA2 = nf.format(list3[i-1]);
            if(DATA1.equals(DATA2))

            {
                ExRank[i] = number;
                number++;
            }
            else{
                ExRank[i] = ++number;
            }
            System.out.println("ex:" + ExRank[i]);
        }

        //索引回原先數字

        for(int i = 0;i < n;i++)
        {
            for(int j = 0;j < n;j++)
            {
                NumberFormat nf = NumberFormat.getInstance();
                nf.setGroupingUsed(false);
                String DATA1 = nf.format(list3[j]);
                String DATA2 = nf.format(Distance[i]);
                if(DATA1.equals(DATA2))
                {
                    Rank[i] = ExRank[j];
                    System.out.println("玩家" + i +"rank:" + Rank[i]);
                    break;
                }
            }
        }
        return para;
    }

2、完善互動準備功能

實現程式碼如下:

//準備按鈕點選事件(向伺服器傳送type2)
        ready_button.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseClicked(MouseEvent e) {
                super.mouseClicked(e);

                try{
                    JSONObject json = new JSONObject();
                    json.put("type", 2);
                    json.put("mark", LocalNumber);
                    ToServer.sendMsg(json.toString(), new DataOutputStream(socket.getOutputStream()));
                } catch (JSONException e1) {
                    e1.printStackTrace();
                } catch (IOException ioException) {
                    ioException.printStackTrace();
                }
                }
        });

//檢測玩家是否全部都準備
    public void CheckReady(){
        for(int i = 0;i<player.size();i++){
            if(player.get(i).isReady() && i < player.size() - 1)
                continue;
            else if(!player.get(i).isReady()){
                isAllReady = false;
                break;
            }
            else if(player.get(i).isReady() && i == player.size() - 1)
                isAllReady = true;
        }
    }
//傳送玩家準備訊息
    public void upDateReady(String playerReady) throws JSONException, IOException{
        JSONObject json = new JSONObject();
        json.put("type", 5);
        json.put("msg", playerReady.trim());
        System.out.println("伺服器傳送更新狀態:" + json.toString());
        sendMsg(json);
    }

    //傳送玩家準備訊息
    public void upDateReady(int num) throws JSONException, IOException{
        JSONObject json = new JSONObject();
        json.put("type", 6);
        json.put("msg", num);
        System.out.println(json.toString());
        sendMsg(json);
    }

七、階段總結

本週基本完成了整個程式的設計,實現了預期功能,後期需要對程式進行測試除錯,檢視有無bug,並對程式進一步完善(如優化Ui等等),如有需求還可新增其他功能。