我們透過連接生成式AI的API,再規定提示詞以及走法規則,理論上就可以實現與AI的對弈,接下來的幾天,我們就來探討及實作如何利用AI來達成人機對弈的效果,並做出一些總結
首先需要到chat-gpt的官方網站申請API Token,從入口網站登入帳號,點擊右上角的創建新密鑰,輸入密鑰名稱後,直接創建
創建完成後,請妥善保存密鑰,這個視窗只會出現一次,關掉就無法再查看了
再來就是要創建該密鑰的付款資訊,前往付款頁面,選信用卡或其他方式付款,填入相關資訊
選擇需要的額度後,就可以用該密鑰送出請求了
我們回到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