iT邦幫忙

2024 iThome 鐵人賽

DAY 27
0

我們透過連接生成式AI的API,再規定提示詞以及走法規則,理論上就可以實現與AI的對弈,接下來的幾天,我們就來探討及實作如何利用AI來達成人機對弈的效果,並做出一些總結


申請API KEY及儲值

首先需要到chat-gpt的官方網站申請API Token,從入口網站登入帳號,點擊右上角的創建新密鑰,輸入密鑰名稱後,直接創建
未命名
未命名

創建完成後,請妥善保存密鑰,這個視窗只會出現一次,關掉就無法再查看了
未命名

再來就是要創建該密鑰的付款資訊,前往付款頁面,選信用卡或其他方式付款,填入相關資訊
未命名
image

選擇需要的額度後,就可以用該密鑰送出請求了

CreateJoinRoom

我們回到CreateJoinRoom,新增一個入口,並帶gamemode值"ai"到下個頁面

btn_ai.setOnClickListener(v -> {
            random_room_number = random.nextInt(10000);
            gameMode="ai";
            CreateRoom();
        });

接下來,我們在Player1ChessGame中的getSuccess新增判斷是否為ai對奕的情況

if(gameMode.equals("ai") && moveState.equals("black") && getCount==1){
            gptMove.setRoomKey(roomKey);
            gptMove.setTurn(String.valueOf(turn));
            gptMove.setCastling(castling);
            gptMove.setChessmanMoveData(chessmanMoveData);
            gptMove.setChessboardData(chessboardData);
            gptMove.setGPT();
        }

接著實作GPTMove

public class GPTMove implements DatabaseContract.view{
    private Activity activity;
    private DatabasePresenter dbp;
    private PlayerChessboardData playerChessboardData = new PlayerChessboardData("2");
    private List<GPTApiRequest.ChatMessage> messages = new ArrayList<>();
    private GPTApiService gptApiService = GPTApiClient.getGPTApiInstance().getGPTApiService();
    private String moveData="",castling="",moveTwoStep="",inPassing = "",roomKey="",turn="";
    private String[] coordinate_a = {"","a","b","c","d","e","f","g","h",""};
    private HashMap<String,Object> chessboardData = new HashMap<>();
    private HashMap<String,Object> chessmanMoveData = new HashMap<>();
    private HashMap<String,Object> pawnMove2 = new HashMap<>();
    private int start,stop;
    private Boolean isPromotion=false;
    public GPTMove(Activity activity){
        this.activity = activity;
        dbp = new DatabasePresenter(this);
        setPrompt();
    }
    public void setPrompt(){
        messages.add(new GPTApiRequest.ChatMessage("system", this.activity.getString(R.string.move_prompt)));
    }
    public void setRoomKey(String roomKey){
        this.roomKey = roomKey;
    }
    public void setCastling(String castling){
        this.castling = castling;
    }
    public void setTurn(String turn){
        this.turn = turn;
    }
    public void setChessmanMoveData(HashMap<String,Object> chessmanMoveData){
        this.chessmanMoveData = chessmanMoveData;
    }
    public void setChessboardData(HashMap<String,Object> chessboardData){
        this.chessboardData = chessboardData;
    }
    public void move(int start, int stop){
        new Thread(() -> {
            // 判斷遊戲是否結束
            if(!isGameFinish()){
                // 如果有王車易位,則修改指定格的棋子
                isCastling();
                // 處理兵的特殊情況
                disposePawn();
                // 產生棋譜資料
                moveData(String.valueOf(playerChessboardData.getStringBoardData(start))
                        ,String.valueOf(playerChessboardData.getStringBoardData(stop)));
                // 判斷是否可升變
                isPromotion();
                // 修改棋盤資料,將目標格棋子改為原起始格棋子,原起始格棋子改為空格
                chessboardData.put(String.valueOf(playerChessboardData.getStringBoardData(stop)),String.valueOf(chessboardData.get(playerChessboardData.getStringBoardData(start))));
                chessboardData.put(String.valueOf(playerChessboardData.getStringBoardData(start)),"  ");

                dbp.uploadData(roomKey,"PawnMove2",pawnMove2);
                // 上傳棋盤資料
                if(isPromotion == false){
                    chessmanMoveData.put(turn,moveData);
                    dbp.uploadData(roomKey,"ChessmanMoveData",chessmanMoveData);
                    dbp.uploadData(roomKey,"ChessboardData",chessboardData);
                    dbp.uploadData(roomKey,"MoveState","white");
                    // 回合數增加
                    dbp.uploadData(roomKey,"Turn",String.valueOf(Integer.parseInt(turn)+1));
                }
            }

            activity.runOnUiThread(() -> {
                // 清除上回合所有移動資料
                clearData();
            });
        }).start();
    }
    public void setGPT(){
        GPTApiRequest request = new GPTApiRequest();
//        messages = new ArrayList<>();
//        messages.add(new GPTApiRequest.ChatMessage("system", this.activity.getString(R.string.move_prompt)));
        messages.add(new GPTApiRequest.ChatMessage("user", chessboardData.toString()));
        Log.d("20240806", "setGPT: "+chessboardData.toString());
        request.setMessages(messages);
        Log.d("20240805", "setGPT: "+request.getMessages());

        gptApiService.getChatGPTRespond(request)
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Observer<Response<GPTApiRespond>>() {
                    @Override
                    public void onSubscribe(@NonNull Disposable d) {
                    }
                    @Override
                    public void onNext(@NonNull Response<GPTApiRespond> response) {
                        if (response.isSuccessful()){
                            String content = response.body().choices.get(0).getMessage().getContent();
                            Log.d("20240805", "onNext: " + content);
                            // 未選擇起始格
                            // 選擇起始格,未選擇目標格
                            // 起始格與目標格都已選擇
                            start = playerChessboardData.getIntBoardData2(String.valueOf(content.charAt(0))+String.valueOf(content.charAt(1)));
                            stop = playerChessboardData.getIntBoardData2(String.valueOf(content.charAt(2))+String.valueOf(content.charAt(3)));
                            move(start,stop);
                        }
                    }
                    @Override
                    public void onError(@NonNull Throwable e) {
                        Log.e("20240805", "onError: " + e.getMessage());
                        chatGptErrorResponse(e.getMessage());
                        Toast.makeText(activity, "請檢查網路連線", Toast.LENGTH_SHORT).show();
                    }
                    @Override
                    public void onComplete() {
                    }
                });
    }
    public void chatGptErrorResponse(String errorMes){}
    public void addSuccess(){
        Log.d("TAG", "資料上傳成功");
    }
    public void addError(){
        Log.d("TAG", "資料上傳失敗");
    }
    public void getSuccess(String getId,
                           String getRoomNumber,
                           Boolean getPlayer1OnlineState,
                           Boolean getPlayer2OnlineState,
                           String getMoveState,
                           HashMap<String,Object> getChessboardData,
                           HashMap<String,Object> getChessmanMoveData,
                           String getTurn,
                           HashMap<String,Object> getPawnMove1,
                           HashMap<String,Object> getPawnMove2,
                           HashMap<String,Object> getCastlingMove1,
                           HashMap<String,Object> getCastlingMove2,
                           String getDate,
                           String getPlayer1message,
                           String getPlayer2message,
                           String getWinPlayer){
        Log.d("TAG", "資料獲取成功");
    }
    public void getError(){
        Log.d("TAG", "資料獲取失敗");
    }
    public void uploadSuccess(){
        Log.d("TAG", "資料修改成功");
    }
    public void uploadError(){
        Log.d("TAG", "資料修改失敗");
    }
    public void deleteSuccess(){
        Log.d("TAG", "資料刪除成功");
    }
    public void deleteError(){
        Log.d("TAG", "資料刪除失敗");
    }
    public void clearData(){
        start = 99;
        stop = 99;
        castling = "";
        moveTwoStep = "";
        inPassing = "";
        isPromotion = false;
    }
    public boolean isGameFinish(){
        Boolean isGameFinish=false;
        // 如果吃的棋是國王則遊戲結束
        if(chessboardData.get(playerChessboardData.getStringBoardData(stop)).equals("wK")){
            isGameFinish = true;
            dbp.uploadData(roomKey,"WinPlayer","black");

            // 產生棋譜資料
            moveData(String.valueOf(playerChessboardData.getStringBoardData(start))
                    ,String.valueOf(playerChessboardData.getStringBoardData(stop)));
            moveData = moveData + " #";
            // 修改棋盤資料,將目標格棋子改為原起始格棋子,原起始格棋子改為空格
            chessboardData.put(String.valueOf(playerChessboardData.getStringBoardData(stop)),String.valueOf(chessboardData.get(playerChessboardData.getStringBoardData(start))));
            chessboardData.put(String.valueOf(playerChessboardData.getStringBoardData(start)),"  ");
            chessmanMoveData.put(String.valueOf(turn),moveData);
            dbp.uploadData(roomKey,"ChessmanMoveData",chessmanMoveData);
            dbp.uploadData(roomKey,"ChessboardData",chessboardData);
        }
        return isGameFinish;
    }
    public void moveData(String start,String stop){
        // moveData = 棋種 + 起始座標
        moveData = chessboardData.get(start).toString().charAt(1) + start;
        // 判斷是移動到空格還是吃棋
        if(chessboardData.get(stop).equals("  ")){
            moveData = moveData + " > ";
        } else if (String.valueOf(chessboardData.get(stop).toString().charAt(0)).equals("w")) {
            moveData = moveData + " X ";
        }
        // moveData = 棋種 + 起始座標 X,> 目標座標
        moveData = moveData + stop;
    }
    public void isCastling(){
        if(castling.equals("leftCastling")){
            chessboardData.put("f8","bR");
            chessboardData.put("h8","  ");
        }else if(castling.equals("rightCastling")){
            chessboardData.put("d8","bR");
            chessboardData.put("a8","  ");
        }
    }
    public void disposePawn(){
        // 將自己的兵標記清除
        for(int i=1;i<=8;i++){
            pawnMove2.put(coordinate_a[i]+"4",false);
        }
        // 如果有兵起始走2格,將該兵做標記
        if(!(moveTwoStep.equals(""))){
            pawnMove2.put(moveTwoStep,true);
        }
    }
    public void isPromotion(){
        // 判斷是否有兵移動到最底橫列
        if(chessboardData.get(String.valueOf(playerChessboardData.getStringBoardData(start))).equals("bP")
                && stop/8==0){
            isPromotion = true;
            activity.runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    moveData = moveData +" (Q)";
                    new Thread(() ->{
                        chessmanMoveData.put(String.valueOf(turn),moveData);
                        chessboardData.put(playerChessboardData.getStringBoardData(stop),"bQ");
                        dbp.uploadData(roomKey,"ChessboardData",chessboardData);
                        dbp.uploadData(roomKey,"ChessmanMoveData",chessmanMoveData);
                        dbp.uploadData(roomKey,"MoveState","white");
                        // 回合數增加
                        turn += 1;
                        dbp.uploadData(roomKey,"Turn",String.valueOf(turn));
                    }).start();
                }
            });
        }
    }
}

可以發現其實與先前的實作相差無幾,差別在於說沒有實作吃過路兵,因為本範例會先以簡單能達成的目標測試gpt的程度


下一篇來實作API連接的Client及Service


上一篇
【DAY 26】activity - setting - musicCreate
下一篇
【DAY 28】chatgpt - API Client及Service
系列文
基於Firebase整合生成式AI研究開發雙人國際象棋系統(Based on Firebase and AI to research chess system)30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言