Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android 藍牙對戰五子棋項目實現(含人機對戰功能)

Android 藍牙對戰五子棋項目實現(含人機對戰功能)

編輯:關於Android編程

上周花了一周時間做的課程設計的項目,實現的功能如下:

基本功能:
(1) 該APP能夠通過藍牙自動搜索周圍其他使用了該APP的手機,用戶可選擇其中某一個APP發起對戰的要求,被發起方可以同意或者拒絕;
(2) 雙方可以實現五子棋藍牙對戰;
(3) 具備悔棋等功能。
(4) 實現人機對戰。提供難度選擇。
(5)提供用戶戰績排名系統。

項目已經上傳到Github:https://github.com/jiangzhengnan/PumpkinGoBang.git Github跳轉 下面是界面截圖和實現原理代碼分析。 實現部分分為3點: 1、簡單人機算法實現。 2、普通人機算法實現。 3、藍牙模塊客戶端服務端實現。   一、運行截圖: 主界面:   \   人人對戰界面:   \   藍牙搜索功能:   \   藍牙對戰界面:   \\   簡單人機功能:   \   普通人機功能:   \   二、實現原理: 1、簡單難度電腦算法實現: 因為時間比較趕,所以人機算法這塊實現得比較簡單,沒有去學習使用專業的五子棋人機算法,比如五元組分值比較法或者正規的博弈算法。 簡單難度算法就是隨機在落子點周圍生成一個點:
/**
 * 簡單模式自動下棋的電腦
 *
 * @param x
 * @param y     玩家下的坐標
 * @param Color 玩家下的棋的顏色    黑1白2
 *              <p/>
 *              x&y<GRID_SIZE - 1
 */
private void naocanautomatic(int x, int y, int Color) {
    int[][] temp = {{x - 1, y - 1}, {x, y - 1}, {x + 1, y - 1}, {x - 1, y}, {x + 1, y}, {x - 1, y + 1}, {x, y + 1}, {x + 1, y + 1}};
    ArrayList<int[]> templist = new ArrayList<>();
    for (int i = 0; i < temp.length; i++) {
        if (temp[i][0] >= 0 && temp[i][0] < 13 && temp[i][1] >= 0 && temp[i][1] < 13) {
            templist.add(temp[i]);
        }
    }
    //判斷是否已經下過
    panduanshifouyijingxiaguo(templist);
    int num = (int) (Math.random() * templist.size());
    int a = templist.get(num)[0];
    int b = templist.get(num)[1];
    putChess(a, b, Color);
}
這裡要保存已經下過的點位,並判斷是否已經下過,難點是在於如果下的位置在角落處(或周圍已經沒有棋子可以自動生成位置了,這時要遞歸判斷並隨機生成一個新的位置,直到全圖都沒有位置了為止終止遞歸):
/**
 * 遞歸判斷是否已經下過
 *
 * @param templist
 */
private void panduanshifouyijingxiaguo(ArrayList templist) {
    for (int i = 0; i < storageHadChess.size(); i++) {
        //如有重復,則刪掉
        for (int j = 0; j < templist.size(); j++) {
            if (storageHadChess.get(i)[0] == templist.get(j)[0] && storageHadChess.get(i)[1] == templist.get(j)[1]) {
                templist.remove(j);
                //遞歸防止周圍沒有字落下時直接崩掉
                if (templist.size() == 0) {
                    templist.add(new int[]{(int) (Math.random() * (GRID_SIZE - 2)), (int) (Math.random() * (GRID_SIZE - 2))});
                    //  Log.d("whalea", " " + (int) (Math.random() * (GRID_SIZE - 2)));
                    panduanshifouyijingxiaguo(templist);
                }
            }
        }
    }
}
2、普通難度算法實現: 普通難度的算法實現地比較糾結,在簡單人機的基礎上(這裡可以稱之為“隨機落子算法”~!),加入了三點判斷功能,就是黑白雙方一旦有3點相連會自動往這三點的兩端隨機堵一個點,並繼續遞歸判斷下過的位置,這個循環寫得比較長,因為沒有把重復的代碼抽取出來。
/**
 * 普通模式自動下棋的電腦
 * 12
 *
 * @param x
 * @param y
 * @param Color
 */
private void normalautomatic(int x, int y, int Color) {
    int duishouColor = 0;//對手的顏色
    //根據我方顏色推測出對手顏色
    if (Color == 1) {
        duishouColor = 2;
    } else {
        duishouColor = 1;
    }
    //判斷我方是否有3個連成一線了
    for (int i = 0; i < GRID_SIZE - 1; i++) //i表示列(根據寬度算出來的)
        for (int j = 0; j < GRID_SIZE - 1; j++) { //i表示行(根據高度算出來的)
            //檢測橫軸三個相連
            if ((((i + 3) < (GRID_SIZE - 1)) &&
                    (mGridArray[i][j] == Color) && (mGridArray[i + 1][j] == Color) && (mGridArray[i + 2][j] == Color)) ||
                    (((i + 3) < (GRID_SIZE - 1)) &&
                            (mGridArray[i][j] == duishouColor) && (mGridArray[i + 1][j] == duishouColor) && (mGridArray[i + 2][j] == duishouColor))) {
                //如果有三個點相連了
                //先判斷是否已經測試過這三個點
                boolean aa = false;
                for (int p = 0; p < cunchusandianArraylist.size(); p++) {
                    String sandiantemp = cunchusandianArraylist.get(p);
                    String[] sandiantemps = sandiantemp.split(":");
                    //如果這三個點已經存在
                    if ((Integer.parseInt(sandiantemps[0]) == i) &&
                            (Integer.parseInt(sandiantemps[1]) == j) &&
                            (Integer.parseInt(sandiantemps[2]) == (i + 1)) &&
                            (Integer.parseInt(sandiantemps[3]) == j) &&
                            (Integer.parseInt(sandiantemps[4]) == (i + 2)) &&
                            (Integer.parseInt(sandiantemps[5]) == j)) {
                        aa = true;
                    }
                }
                if (aa == true) {

                } else {
                    //在兩邊端點位置隨機下一個
                    ifsangedianxianglian(i - 1, j, i + 3, j, Color);
                    cunchusandianArraylist.add(i + ":" + j + ":" + (i + 1) + ":" + j + ":" + (i + 2) + ":" + j);
                    return;
                }
            }

            //縱軸3個相連
            if ((((j + 3) < (GRID_SIZE - 1)) &&
                    (mGridArray[i][j] == Color) && (mGridArray[i][j + 1] == Color) && (mGridArray[i][j + 2] == Color)) ||
                    (((j + 3) < (GRID_SIZE - 1)) &&
                            (mGridArray[i][j] == duishouColor) && (mGridArray[i][j + 1] == duishouColor) && (mGridArray[i][j + 2] == duishouColor))) {
                //如果有三個點相連了
                //先判斷是否已經測試過這三個點
                boolean aa = false;
                for (int p = 0; p < cunchusandianArraylist.size(); p++) {
                    String sandiantemp = cunchusandianArraylist.get(p);
                    String[] sandiantemps = sandiantemp.split(":");
                    if ((Integer.parseInt(sandiantemps[0]) == i) &&
                            (Integer.parseInt(sandiantemps[1]) == j) &&
                            (Integer.parseInt(sandiantemps[2]) == i) &&
                            (Integer.parseInt(sandiantemps[3]) == (j + 1)) &&
                            (Integer.parseInt(sandiantemps[4]) == i) &&
                            (Integer.parseInt(sandiantemps[5]) == (j + 2))) {
                        aa = true;
                    }
                }
                if (aa == true) {

                } else {
                    //在兩邊端點位置隨機下一個
                    ifsangedianxianglian(i, j - 1, i, j + 3, Color);
                    cunchusandianArraylist.add(i + ":" + j + ":" + i + ":" + (j + 1) + ":" + i + ":" + (j + 2));
                    return;
                }
            }

            //左上到右下3個相連
            if ((((j + 3) < (GRID_SIZE - 1)) && ((i + 3) < (GRID_SIZE - 1)) &&
                    (mGridArray[i][j] == Color) && (mGridArray[i + 1][j + 1] == Color) && (mGridArray[i + 2][j + 2] == Color)) ||
                    (((j + 3) < (GRID_SIZE - 1)) && ((i + 3) < (GRID_SIZE - 1)) &&
                            (mGridArray[i][j] == duishouColor) && (mGridArray[i + 1][j + 1] == duishouColor) && (mGridArray[i + 2][j + 2] == duishouColor))) {
                //如果有三個點相連了
                //先判斷是否已經測試過這三個點
                boolean aa = false;
                for (int p = 0; p < cunchusandianArraylist.size(); p++) {
                    String sandiantemp = cunchusandianArraylist.get(p);
                    String[] sandiantemps = sandiantemp.split(":");
                    if ((Integer.parseInt(sandiantemps[0]) == i) &&
                            (Integer.parseInt(sandiantemps[1]) == j) &&
                            (Integer.parseInt(sandiantemps[2]) == (i + 1)) &&
                            (Integer.parseInt(sandiantemps[3]) == (j + 1)) &&
                            (Integer.parseInt(sandiantemps[4]) == (i + 2)) &&
                            (Integer.parseInt(sandiantemps[5]) == (j + 2))) {
                        aa = true;
                    }
                }
                if (aa == true) {

                } else {
                    ifsangedianxianglian(i - 1, j - 1, i + 3, j + 3, Color);
                    cunchusandianArraylist.add(i + ":" + j + ":" + (i + 1) + ":" + (j + 1) + ":" + (i + 2) + ":" + (j + 2));
                    return;
                }
            }

            //右上到左下3個相連
            if ((((i - 3) >= 0) && ((j + 3) < (GRID_SIZE - 1)) &&
                    (mGridArray[i][j] == Color) && (mGridArray[i - 1][j + 1] == Color) && (mGridArray[i - 2][j + 2] == Color)) ||
                    (((i - 3) >= 0) && ((j + 3) < (GRID_SIZE - 1)) &&
                            (mGridArray[i][j] == duishouColor) && (mGridArray[i - 1][j + 1] == duishouColor) && (mGridArray[i - 2][j + 2] == duishouColor))) {
                //如果有三個點相連了
                //先判斷是否已經測試過這三個點
                boolean aa = false;
                for (int p = 0; p < cunchusandianArraylist.size(); p++) {
                    String sandiantemp = cunchusandianArraylist.get(p);
                    String[] sandiantemps = sandiantemp.split(":");
                    if ((Integer.parseInt(sandiantemps[0]) == i) &&
                            (Integer.parseInt(sandiantemps[1]) == j) &&
                            (Integer.parseInt(sandiantemps[2]) == (i - 1)) &&
                            (Integer.parseInt(sandiantemps[3]) == (j + 1)) &&
                            (Integer.parseInt(sandiantemps[4]) == (i - 2)) &&
                            (Integer.parseInt(sandiantemps[5]) == (j + 2))) {
                        aa = true;
                    }
                }
                if (aa == true) {

                } else {
                    ifsangedianxianglian(i + 1, j - 1, i - 3, j + 3, Color);
                    cunchusandianArraylist.add(i + ":" + j + ":" + (i - 1) + ":" + j + 1 + ":" + (i - 2) + ":" + (j + 2));
                    return;
                }
            }
        }
    int[][] temp = {{x - 1, y - 1}, {x, y - 1}, {x + 1, y - 1}, {x - 1, y}, {x + 1, y}, {x - 1, y + 1}, {x, y + 1}, {x + 1, y + 1}};
    ArrayList templist = new ArrayList<>();
    for (int k = 0; k < temp.length; k++) {
        if (temp[k][0] >= 0 && temp[k][0] < 13 && temp[k][1] >= 0 && temp[k][1] < 13) {
            templist.add(temp[k]);
        }
        //判斷是否已經下過
        panduanshifouyijingxiaguo(templist);
        int num = (int) (Math.random() * templist.size());
        int a = templist.get(num)[0];
        int b = templist.get(num)[1];
        putChess(a, b, Color);
        return;
    }
}
3、藍牙連接實現代碼: 整個項目是客戶端與服務端一體的,所以藍牙部分大概可以分為三個模塊: 掃描模塊: 實首先打開一個廣播掃描周圍設備:
//接收廣播
/**
 * 接受廣播,並顯示尚未配對的可用的周圍所有藍牙設備
 */
private class BluetoothReceiver extends BroadcastReceiver {
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();
        //如果是正在掃描狀態
        if (BluetoothDevice.ACTION_FOUND.equals(action)) {
            //只要BluetoothReceiver接收到來自於系統的廣播,這個廣播是什麼呢,是我找到了一個遠程藍牙設備
            //Intent代表剛剛發現遠程藍牙設備適配器的對象,可以從收到的Intent對象取出一些信息
            BluetoothDevice bluetoothDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
            // 如果該設備已經被配對,則跳過
            //  if (bluetoothDevice.getBondState() != BluetoothDevice.BOND_BONDED) {
            if (!devices.contains(bluetoothDevice)) {
                //設備數組獲得新的設備信息並更新adapter
                deviceNameAndDresss.add(new Device(bluetoothDevice.getName(), bluetoothDevice.getAddress(),bluetoothDevice.getBondState()));
                //添加新的設備到設備Arraylist
                devices.add(bluetoothDevice);
                deviceshowAdapter.notifyDataSetChanged();
            }

        }
    }
}
也可以調用BlueToothAapter的startDiscovery()方法主動進行掃描:  
//掃描周圍的藍牙設備按鈕監聽器
private class SaoMiaoButtonListener implements View.OnClickListener {

    @Override
    public void onClick(View v) {
        ObjectAnimator animator = ObjectAnimator.ofFloat(v,"rotation",0,359);
        animator.setRepeatCount(12);
        animator.setDuration(1000);
        animator.start();


        isQuering = true;
        Toast.makeText(BlueToothFindOthersAty.this, "開始掃描", Toast.LENGTH_SHORT).show();
        //清空列表
        deviceNameAndDresss.clear();
        //獲得已配對的藍牙設備
        Set pairedDevices = bluetoothAdapter.getBondedDevices();
        if (pairedDevices.size() > 0) {
            for (BluetoothDevice device : pairedDevices) {
                if (!devices.contains(device)) {
                    deviceNameAndDresss.add(new Device(device.getName(), device.getAddress(),device.getBondState()));
                    devices.add(device);
                }
            }
        }
        deviceshowAdapter.setDevices(deviceNameAndDresss);
        deviceshowAdapter.notifyDataSetChanged();
        //開始掃描周圍的可見的藍牙設備
        bluetoothAdapter.startDiscovery();

    }
}
服務端等待連接模塊: 這裡服務端比較簡單,通過accpet方法監聽連接就可以了。
//開啟子線程等待連接
new Thread(new Runnable() {
    @Override
    public void run() {
        try {
            //開啟服務端
            //等待客戶端接入
            while (true) {
                bluetoothServerSocket = bluetoothAdapter.listenUsingInsecureRfcommWithServiceRecord(benjiname, Config.UUID);
                fuwuSocket = bluetoothServerSocket.accept();
                if (fuwuSocket.isConnected()) {


                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            Toast.makeText(BlueToothFindOthersAty.this, "接收挑戰請求,建立連接成功!", Toast.LENGTH_SHORT);
                            //執行socket方法

                             BlueToothGameAty blueToothGameAty = new BlueToothGameAty();
                            blueToothGameAty.blueToothGameAty.manageConnectedSocket(fuwuSocket, false);
                     //       blueToothGameAty.blueToothGameAty.chushihua(blueToothGameAty);
                        }
                    });




                    //跳轉到藍牙游戲activity
                    Intent i = new Intent(BlueToothFindOthersAty.this,BlueToothGameAty.class);
                    startActivity(i);
                    //初始化線程來傳輸數據
                    // manageConnectedSocket(fuwuSocket);
                    //得到連接之後關閉ServerSocket
                    // bluetoothServerSocket.close();
                    //打斷線程
                    //   Thread.interrupted();
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
            Log.d("whalea", "沒讀到的原因!:" + e.getMessage());
        }
    }
}).start();
客戶端主動連接模塊: 客戶端socket使用connet()方法主動連接。
/**
 * 建立連接的方法
 *
 * @param position  位置
 * @param isfaqiren
 */
private void buildConnect(int position, boolean isfaqiren) {
    //自己主動去連接
    BluetoothDevice device = bluetoothAdapter.getRemoteDevice(deviceNameAndDresss.get(position).getDeviceAddress());
    Boolean result = false;
    try {
        //先進行配對
        //如果沒有配對
        Log.d("whalea", "開始配對");
        if (device.getBondState() == BluetoothDevice.BOND_NONE) {
            Method createBondMethod = null;
            createBondMethod = BluetoothDevice.class
                    .getMethod("createBond");

            Log.d("whalea", "開始配對");
            result = (Boolean) createBondMethod.invoke(device);
        }
        //如果已經配對好了
        else if (device.getBondState() == BluetoothDevice.BOND_BONDED) {
            //獲得客戶端Socket
            kehuduanSocket = device.createRfcommSocketToServiceRecord(Config.UUID);
            final AlertDialog aDialog = new AlertDialog.Builder(BlueToothFindOthersAty.this).
                    setTitle("發起對戰").
                    setMessage("確認挑戰玩家:" + deviceNameAndDresss.get(position).getDeviceName() + "嗎?")
                    .setNegativeButton("確定", new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialog, int which) {
                            new Thread(new Runnable() {
                                @Override
                                public void run() {
                                    //先停止掃描,以防止之後的連接被阻塞
                                    bluetoothAdapter.cancelDiscovery();
                                    try {
                                        //開始連接,發送連接請求
                                        kehuduanSocket.connect();
                                        if (!bluetoothAdapter.isEnabled()) {
                                            bluetoothAdapter.enable();
                                        }
                                        if (kehuduanSocket.isConnected()) {
                                            runOnUiThread(new Runnable() {
                                                @Override
                                                public void run() {
                                                    Toast.makeText(BlueToothFindOthersAty.this, "連接成功!!", Toast.LENGTH_SHORT).show();
                                                    //執行socket方法
                                                     BlueToothGameAty blueToothGameAty = new BlueToothGameAty();

                                                    blueToothGameAty.blueToothGameAty.manageConnectedSocket(kehuduanSocket, true);
                                                 //   blueToothGameAty.blueToothGameAty.chushihua(blueToothGameAty);
                                                }
                                            });
                                            //跳轉到藍牙游戲activity
                                            Intent i = new Intent(BlueToothFindOthersAty.this,BlueToothGameAty.class);
                                            startActivity(i);
                                        }
                                    } catch (final IOException e) {
                                        runOnUiThread(new Runnable() {
                                            @Override
                                            public void run() {
                                                Toast.makeText(BlueToothFindOthersAty.this, "連接失敗!!" + e.getMessage(), Toast.LENGTH_SHORT).show();

                                            }
                                        });
                                             /*   try {
                                                    kehuduanSocket.close();
                                                } catch (IOException e1) {
                                                }
                                                return;*/
                                    }
                                    // manageConnectedSocket(kehuduanSocket);
                                    //之後關閉socket,清除內部資源
                                      /*      try {
                                                kehuduanSocket.close();
                                            } catch (IOException e) {
                                                e.printStackTrace();
                                            }*/
                                }
                            }).start();
                        }
                    })
                    .setPositiveButton("取消", null).show();
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
}
其他初始化的代碼都在項目裡面就不贅述了,可以自己下載來看。  
  本來有很多課程設計項目可供選擇的= =不過最後還是選了比較有挑戰性的這個。 之前一直沒接觸過藍牙開發,網上的demo或者例子又都很難理解(可能是沒有科學上網的原因)。 最終還是靠著官網的API才慢慢寫出了一開始的連接demo,然後一步步測試傳輸數據。 哎,你們吶,還是要多看API!        

 

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved