Use ChatGPT to develop an interactive backgammon WeChat applet through WebSocket (2)

1 Introduction

1.1 Principle of Implementation

The general direction is to use ChatGPT as a back-end language model, and then integrate it with the front-end graphical user interface (GUI). Here are three key elements:

  • game logic

First of all, in terms of game logic, we need to 检测玩家点击的位置place the corresponding pieces on the board. After each player plays we need to check if the game is over and which side won. This can be 遍历棋盘上的棋子achieved with . We can check whether there are five identical chess pieces in 8 directions around each chess piece. If so, the game ends and the winning side is announced.

  • Integration with ChatGPT

In order for players to interact with ChatGPT, we can pass the state of the chessboard as input to the ChatGPT model after each player plays chess. ChatGPT will output a string indicating where ChatGPT thinks the next step should be. Then, we display this position on the chessboard with a different color as a chess piece for ChatGPT.

  • interface

The first thing is to create a game interface, divide a window into a 15x15 grid, and draw the board lines in it.

Finally, we need to create a user interface that allows the player to interact with the game. We can add a text box at the bottom of the window for entering ChatGPT's text hints and a button for ChatGPT to play chess. Players can click a position on the board and press a button to use it as input for ChatGPT. After receiving the chess prompt from ChatGPT, the program will automatically place a chess piece on the board.

1.2 How to contact the WeChat Mini Program

A basic process as follows:

  1. The user enters the WeChat mini-program and opens the backgammon game page.
  2. The game page displays the game interface, including the board, pieces and hint information.
  3. The user clicks on a position on the board to use it as the next move.
  4. The user clicks the chess button to trigger the chess event.
  5. The applet passes the user's position and the current board state as input to the ChatGPT model.
  6. The ChatGPT model calculates the best position for the next step, and returns the position to the applet.
  7. The applet displays the position of ChatGPT on the board.
  8. The applet checks to see if the game is over and declares the winner.
  9. The user can choose to play the game again or quit the game.

Draw a simple flow chart with characters, my grandma will read it:

+---------+       +--------+      +--------+
|         |       |        |      |        |
|进入小程序 | -->   |游戏界面 | -->  |  下棋   |
|         |       |        |      |        |
+---------+       +--------+      +--------+
                                       |
                                       |
                                       v
                                +-------------+
                                | ChatGPT 模型 |
                                +-------------+
                                       |
                                       |
                                       v
                                +------------+
                                | 在棋盘上下棋 |   
                                +------------+
                                      |
                                      |
                                      v
                               +--------------+
                               | 游戏结束或退出 |
                               +--------------+

2 backgammon items

2.1 Apply for OpenAI's API

Still the same, OpenAI's API URL: https://platform.openai.com/account/api-keys

2.2 Call API code

Use Python's request library:

import os
import requests
import json

API_KEY = os.environ.get("OPENAI_API_KEY")

def generate_text(prompt: str, max_tokens: int) -> str:
    headers = {
    
    
        "Content-Type": "application/json",
        "Authorization": f"Bearer {
      
      API_KEY}"
    }
    data = {
    
    
        "prompt": prompt,
        "max_tokens": max_tokens
    }
    response = requests.post(url="https://api.openai.com/v1/engines/davinci-codex/completions",
                             headers=headers,
                             data=json.dumps(data))
    response.raise_for_status()
    response_data = json.loads(response.text)
    generated_text = response_data["choices"][0]["text"]
    return generated_text.strip()

if __name__ == "__main__":
    prompt = "Hello, my name is"
    max_tokens = 5
    try:
        generated_text = generate_text(prompt, max_tokens)
        print(f"Generated text: {
      
      generated_text}")
    except requests.exceptions.RequestException as e:
        print(f"Error: {
      
      e}")

With the OpenAI library:

from flask import Flask, request
import openai
import os

app = Flask(__name__)

openai.api_key = os.environ.get("OPENAI_API_KEY")

def generate_response(prompt: str, max_tokens: int = 60) -> str:
    response = openai.Completion.create(
        engine="davinci",
        prompt=prompt,
        max_tokens=max_tokens
    )
    return response.choices[0].text.strip()

@app.route("/")
def home():
    return "Hello, World!"

@app.route("/chat", methods=["POST"])
def chat():
    try:
        data = request.json
        message = data["message"]
        if not message:
            return "Invalid message", 400
        response = generate_response(message)
        return response
    except (KeyError, ValueError):
        return "Invalid request data", 400
    except Exception as e:
        return str(e), 500

if __name__ == "__main__":
    port = int(os.environ.get("PORT", 5000))
    app.run(host="0.0.0.0", port=port, debug=True)

2.3 Interface code

 <template>
	<view class="chat-room">

		<gpt-card :show="showGPT"></gpt-card>
				
		
		<view class="box" >
			<view class="centent"><canvas @touchend="syncAction" canvas-id="canvas" class="canvas" style="width: 730rpx;height: 730rpx;"></canvas></view>
			<view>
				<view
					:class="value.class"
					:style="{ left: value.left, top: value.top, transform: value.transform, boxShadow: value.boxShadow }"
					v-for="(value, index) in game.h"
					:key="index"
				>
					{
    
    {
    
     value.text }}
				</view>
			</view>
			<view class="winner">
				<view class="state-chess Bchess"></view>
				<view class="chessName"></view>
			</view>
		</view>
		
		<user-card :show="showUser"></user-card>
	
	</view>
</template>

<script>
	import userCard from '@/components/infoCard/index.vue'
	import gptCard from '@/components/gptCard/index.vue'
	let goEasy = getApp().globalData.goEasy;
	let pubSub = goEasy.pubsub;
	export default {
    
    
		data() {
    
    
			return {
    
    
				showGPT: false,
				showUser: true,
				userInfo:{
    
    
					chessRole:1, // 1为白棋,2为黑棋
					roundFlag:true, // 表示是否为自己的回合
					enemy:'',
					name:''
				},
				chessMassage:{
    
    
					body:'',
					playerA:'',
					playerB:'',
					chessRole:1,
					mode:1
				},
				MoveMode:{
    
    
					a2b:1,
					b2a:2
				},
				game: {
    
    
				ctx: null,
				e: 0,
				chess_Board: [],
				chess_Name: ['黑棋', '白棋'],
				h: [],
				um: 0,
				lianz: [],
				winXY: [[1, 0], [0, 1], [1, 1], [1, -1]],
				chessOff: true
			},
			cName: '黑棋走',
			sChesee: 'Bchess',
				currentRoom: null,
				// 道具展示
				propDisplay: {
    
    
					showPropType: 0,
					play: false,
					timer: null
				},
				newMessageContent: "",
				// 道具类型
				Prop: {
    
    
					HEART: 0,//桃心
					ROCKET: 1//火箭
				},
				
				// 消息类型
				MessageType: {
    
    
					CHAT: 0,//文字聊天
					PROP: 1,//道具
					CHESS:2 // 下棋
				}
			}
		},
		components:{
    
    userCard, gptCard},
		onLoad(options) {
    
    
			//获取数据
			let roomToken = JSON.parse(options.roomToken);
			// 初始化room
			this.currentRoom = {
    
    
				roomId: roomToken.roomId,
				roomName: roomToken.roomName,
				onlineUsers: {
    
    
					count: 0,
					users: []
				},
				messages: [],
				currentUser: {
    
    
					id: roomToken.userId,
					nickname: roomToken.nickname,
					avatar: roomToken.avatar
				}
			};
			this.userInfo.name  = roomToken.nickname
			// 设置导航标题
			uni.setNavigationBarTitle({
    
    
				title: roomToken.roomName
			});

			// 连接goEasy
			this.connectGoEasy();

            // 监听用户上下线
            this.listenUsersOnlineOffline();


            // 加载最后10条消息历史
            this.loadHistory();

            // 监听新消息
            this.listenNewMessage();



        },
		onReady() {
    
    
			this.game.ctx = uni.createCanvasContext('canvas');
			this.drawLine();
		},
		onUnload() {
    
    
			// 断开连接
			goEasy.disconnect({
    
    
				onSuccess(){
    
    
					console.log("GoEasy disconnect successfully");
				},
				onFailed(error){
    
    
					console.log("GoEasy disconnect failed"+JSON.stringify(error));
				}
			});
		},
		methods: {
    
    
			// 连接goEasy
			connectGoEasy(){
    
    
				let self = this;
				let userData = {
    
    
					avatar: this.currentRoom.currentUser.avatar,
					nickname: this.currentRoom.currentUser.nickname
				}
				goEasy.connect({
    
    
					id : this.currentRoom.currentUser.id,
					data : userData,
					onSuccess: function(){
    
    
						console.log("GoEasy connect successfully.")

                        // 加载在线用户列表
                        self.loadOnlineUsers();
					},
					onFailed: function(error){
    
    
						console.log("Failed to connect GoEasy, code:"+error.code+ ",error:"+error.content);
					},
					onProgress: function(attempts){
    
    
						console.log("GoEasy is connecting", attempts);
					}
				});
			},
			// 监听用户上下线
			listenUsersOnlineOffline(){
    
    
				let self = this;
				let roomId = this.currentRoom.roomId;
				pubSub.subscribePresence({
    
    
					channel: roomId,
					onPresence: function (presenceEvents) {
    
    
						self.currentRoom.onlineUsers.count = presenceEvents.clientAmount;
						presenceEvents.events.forEach(function (event) {
    
    
							let userData = event.data;
							if (event.action === "join" || event.action === "online") {
    
    
								//进入房间
								let userId = event.id;
								let avatar = userData.avatar;
								let nickname = userData.nickname;
								let user = {
    
    
									id: userId,
									avatar: avatar,
									nickname: nickname
								};
								//添加新用户
								self.currentRoom.onlineUsers.users.push(user);
								//添加进入房间的消息
								let message = {
    
    
									content: " 进入房间",
									senderUserId: userId,
									senderNickname: nickname,
									type: self.MessageType.CHAT
								};
								self.currentRoom.messages.push(message);
							} else {
    
    
								//退出房间
								self.currentRoom.onlineUsers.users.forEach((user, index) => {
    
    
									if (event.id === user.id) {
    
    
										// 删除当前聊天室列表中离线的用户
										let offlineUser = self.currentRoom.onlineUsers.users.splice(index, 1);
										let message = {
    
    
											content: " 退出房间",
											senderUserId: offlineUser[0].id,
											senderNickname: offlineUser[0].nickname,
											type: self.MessageType.CHAT
										};
										self.currentRoom.messages.push(message);
									}
								});
							}
							self.scrollToBottom();
						});
					},
					onSuccess : function () {
    
    
						console.log("用户上下线监听成功")
					},
					onFailed : function (error) {
    
    
						console.log("监听用户上下线失败, code:"+error.code+ ",content:"+error.content);
                    }
				})
			},
			switchRound(){
    
    
				this.showGPT = !this.showGPT
				this.showUser = !this.showUser
			},
			// 监听新消息
			listenNewMessage(){
    
    
				// 监听当前聊天室的消息
				let self = this;
				let roomId = this.currentRoom.roomId;
				pubSub.subscribe({
    
    
					channel: roomId,
					onMessage : function (message) {
    
    
						let messageContent = "";
						let content = JSON.parse(message.content);
						//聊天消息
						if(content.type === self.MessageType.CHAT) {
    
    
							messageContent = content.content;
						}
						//道具消息
						if(content.type === self.MessageType.PROP) {
    
    
							if (content.content === self.Prop.ROCKET) {
    
    
								messageContent = "送出了一枚大火箭";
							}
							if (content.content === self.Prop.HEART) {
    
    
								messageContent = "送出了一个大大的比心";
							}
						}
						
						 console.log("监听消息成功==",content)
						if(content.type === self.MessageType.CHESS){
    
    
														
							self.canvasClick(content.body,content.chessRole)
							self.userInfo.roundFlag = true
							self.switchRound()
						}
						//添加消息
						let newMessage = {
    
    
							content: messageContent,
							senderUserId: content.senderUserId,
							senderNickname: content.senderNickname,
							type: self.MessageType.CHAT
						};
						self.currentRoom.messages.push(newMessage);
						if (content.type === self.MessageType.PROP) {
    
    
							self.propAnimation(parseInt(content.content))
						}
						self.scrollToBottom();
					},
					onSuccess : function () {
    
    
					  console.log("监听新消息成功")
					},
					onFailed : function(error) {
    
    
						console.log("订阅消息失败, code:"+error.code+ ",错误信息:"+error.content);
					}
				})
			},
			// 加载在线用户列表
			loadOnlineUsers(){
    
    
				let self = this;
				let roomId = this.currentRoom.roomId;
				pubSub.hereNow({
    
    
					channels : [roomId],
					includeUsers : true,
					distinct : true,
					onSuccess: function (result) {
    
    
						let users = [];
						let currentRoomOnlineUsers = result.content.channels[roomId];
						currentRoomOnlineUsers.users.forEach(function (onlineUser) {
    
    
							let userData = onlineUser.data;
							let user = {
    
    
								id: onlineUser.id,
								nickname: userData.nickname,
								avatar: userData.avatar
							};
							users.push(user);
						});
						self.currentRoom.onlineUsers = {
    
    
							users: users,
							count: currentRoomOnlineUsers.clientAmount
						};
						// 如果是第一个进房的就自动设为白棋
						// 如果是第二个进房的就是设为黑棋
						if(users.length==1){
    
    
							self.userInfo.chessRole = 1
							self.userInfo.name = users[0].nickname
						}
						if(users.length==2){
    
    
							self.userInfo.chessRole = 2
							self.userInfo.name = users[1].nickname
						}
						
					},
					onFailed: function (error) {
    
    
						//获取失败
                        console.log("获取在线用户失败, code:" + error.code + ",错误信息:" + error.content);
					}
				});
			},
			// 加载最后10条消息历史
			loadHistory(){
    
    
				let self = this;
				let roomId = this.currentRoom.roomId;
				pubSub.history({
    
    
					channel: roomId, //必需项
					limit: 10, //可选项,返回的消息条数
					onSuccess:function(response){
    
    
						let messages = [];
						response.content.messages.map(message => {
    
    
							let historyMessage = JSON.parse(message.content);
							//道具消息
							if (historyMessage.type === self.MessageType.PROP) {
    
    
								if (historyMessage.content === self.Prop.ROCKET) {
    
    
									historyMessage.content = "送出了一枚大火箭";
								}
								if (historyMessage.content === self.Prop.HEART) {
    
    
									historyMessage.content = "送出了一个大大的比心";
								}
							}
							messages.push(historyMessage);
						});
						self.currentRoom.messages = messages;
					},
					onFailed: function (error) {
    
    
                        console.log("获取历史消息失败, code:" + error.code + ",错误信息:" + error.content);
					}
				});
			},
			onInputMessage(event) {
    
    //双向绑定消息 兼容
				this.newMessageContent = event.target.value;
			},
			sendMessage(messageType, content) {
    
    
				//发送消息
				if (content === "" && messageType === this.MessageType.CHAT) {
    
    
					return;
				}
				var message = {
    
    
					senderNickname: this.currentRoom.currentUser.nickname,
					senderUserId: this.currentRoom.currentUser.id,
					type: messageType,
					content: content
				};
				
				if(messageType === this.MessageType.CHESS){
    
    
					this.chessMassage.body = content
					this.chessMassage.chessRole = this.userInfo.chessRole
					let userNum=this.currentRoom.onlineUsers.users.length
					
					message = {
    
    
						senderNickname: this.currentRoom.currentUser.nickname,
						senderUserId: this.currentRoom.currentUser.id,
						type: messageType,
						body:content,
						playerA:'',
						playerB:'',
						chessRole:this.userInfo.chessRole,
						mode:1,
						userNum:userNum
					}
				}
				console.log("发送==",message);
				pubSub.publish({
    
    
					channel : this.currentRoom.roomId,
					message : JSON.stringify(message),
					onSuccess : function () {
    
    
						console.log("发送成功");
					},
					onFailed : function (error) {
    
    
						console.log("消息发送失败,错误编码:" + error.code + " 错误信息:" + error.content);
					}
				});
				this.newMessageContent = "";
			},
			propAnimation(type) {
    
    //道具动画
				//动画的实现
				if (this.propDisplay.timer) {
    
    
					return;
				}
				this.propDisplay.showPropType = type;
				this.propDisplay.play = true;
				this.propDisplay.timer = setTimeout(() => {
    
    
					this.propDisplay.play = false;
					this.propDisplay.timer = null;
				}, 2000)
			},
			scrollToBottom () {
    
    
				this.$nextTick(function(){
    
    
					uni.pageScrollTo({
    
    
						scrollTop: 2000000,
						duration : 10
					})
				})
			},
			// ==== 五指棋控制逻辑  ===
			drawLine() {
    
    
				let s = uni.upx2px(730);
				let dis = Math.floor(s / 15);
				let w = dis * 14;
				for (let i = 1; i <= 14; i++) {
    
    
					this.game.ctx.moveTo(i * dis + 0.5, w);
					this.game.ctx.lineTo(i * dis + 0.5, dis);
					this.game.ctx.moveTo(dis, i * dis + 0.5);
					this.game.ctx.lineTo(w, i * dis + 0.5);
					this.game.ctx.setStrokeStyle('#a5aa6b');
					this.game.ctx.stroke();
				}
				this.game.ctx.draw();
				for (let i = 0; i <= 13; i++) {
    
    
					this.game.chess_Board[i] = [];
					this.game.lianz[i] = [];
					for (let j = 0; j <= 13; j++) {
    
    
						this.game.chess_Board[i][j] = 0;
						this.game.lianz[i][j] = 0;
					}
				}
			},
			syncAction(e){
    
    
				
				if(this.userInfo.roundFlag){
    
    
									
					
					this.sendMessage(this.MessageType.CHESS,e)
					this.canvasClick(e,this.userInfo.cheeRole)
					this.userInfo.roundFlag = false
				}else{
    
    
					uni.showModal({
    
    
						content: '还未到你的回合!'
					});
				}
				
			},
			canvasClick(e,chessRole) {
    
    
				console.log(JSON.stringify(e));
				let s = uni.upx2px(730);
				let dis = Math.floor(s / 15);
				let dx = parseInt(Math.floor(e.changedTouches[0].x + dis / 2) / dis);
				let dy = parseInt(Math.floor(e.changedTouches[0].y + dis / 2) / dis);
				let WBobj = {
    
    
					ox: dx * dis - dis / 2 + 10,
					oy: dy * dis - dis / 2 + 10,
					left: dx * dis - dis / 2 + 10 + 'px',
					top: dy * dis - dis / 2 + 10 + 'px',
					transform: '',
					boxShadow: '',
					text: '',
					mz: this.game.chess_Name[this.game.e % 2],
					class: this.game.e % 2 == 1 ? 'Wchess' : 'Bchess',
					list: this.game.um++
				};
				if (dx < 1 || (dx > dis - 1) | (dy < 1) || dy > dis - 1) return;
				if (this.game.chess_Board[dx - 1][dy - 1] == 0) {
    
    
					this.game.h.push(WBobj);
					this.game.chess_Board[dx - 1][dy - 1] = this.game.chess_Name[this.game.e % 2];
					this.game.lianz[dx - 1][dy - 1] = WBobj;
					this.win(dx - 1, dy - 1, this.game.chess_Name[this.game.e % 2], this.game.winXY[0], this.game.e % 2);
					this.win(dx - 1, dy - 1, this.game.chess_Name[this.game.e % 2], this.game.winXY[1], this.game.e % 2);
					this.win(dx - 1, dy - 1, this.game.chess_Name[this.game.e % 2], this.game.winXY[2], this.game.e % 2);
					this.win(dx - 1, dy - 1, this.game.chess_Name[this.game.e % 2], this.game.winXY[3], this.game.e % 2);
					this.cName = this.game.e % 2 == 0 ? this.game.chess_Name[1] + '走' : this.game.chess_Name[0] + '走';
					this.sChesee = chessRole==2? 'Bchess' : 'Wchess';
					this.game.e++;
				}
			},
			win(x, y, c, m, li) {
    
    
				let ms = 1;
				var continuity = [];
				for (let i = 1; i < 5; i++) {
    
    
					if (this.game.chess_Board[x + i * m[0]]) {
    
    
						if (this.game.chess_Board[x + i * m[0]][y + i * m[1]] === c) {
    
    
							continuity.push([x + i * m[0], y + i * m[1]]);
							ms++;
						} else {
    
    
							break;
						}
					}
				}
			
				for (let i = 1; i < 5; i++) {
    
    
					if (this.game.chess_Board[x - i * m[0]]) {
    
    
						if (this.game.chess_Board[x - i * m[0]][y - i * m[1]] === c) {
    
    
							continuity.push([x - i * m[0], y - i * m[1]]);
							ms++;
						} else {
    
    
							break;
						}
					}
				}
			
				if (ms >= 5) {
    
    
					setTimeout(function() {
    
    
						console.log(c + '赢了');
					}, 600);
					continuity.push([x, y]);
					this.game.chessOff = false;
					let s = 5;
					let ls = [270, 300, 330, 360, 390];
					let ls1 = [390, 420, 450, 480, 510];
					let _this = this;
					continuity.forEach(function(value, index) {
    
    
						let time = setInterval(function() {
    
    
							_this.game.lianz[value[0]][value[1]].transform = 'scale(0.9)';
							_this.game.lianz[value[0]][value[1]].boxShadow = '0px 0px 2px 2px #ffd507';
							s--;
							s <= 0 ? clearInterval(time) : clearInterval(time);
						}, ls[index]);
						let time2 = setInterval(function() {
    
    
							_this.game.lianz[value[0]][value[1]].transform = 'scale(1)';
							_this.game.lianz[value[0]][value[1]].boxShadow = '0px 0px 2px 2px #ffd507';
							s++;
							s >= 5 ? clearInterval(time2) : clearInterval(time2);
						}, ls1[index]);
					});
			
					for (var i = 0; i < this.game.chess_Board.length; i++) {
    
    
						for (var j = 0; j < this.game.chess_Board.length; j++) {
    
    
							if (this.game.chess_Board[i][j] === 0) {
    
    
								this.game.chess_Board[i][j] = 'null';
							}
						}
					}
			
					this.game.h.forEach(function(value, index) {
    
    
						value.text = value.list;
					});
			
					uni.showModal({
    
    
						content: c + '赢了'
					});
				}
			},
			regret() {
    
    
				if (this.game.chessOff) {
    
    
					if (this.game.h.length > 0) {
    
    
						let s = uni.upx2px(730);
						let dis = Math.floor(s / 15);
						let obj = this.game.h.pop();
						this.cName = this.game.e % 2 == 0 ? this.game.chess_Name[1] + '走' : this.game.chess_Name[0] + '走';
						this.sChesee = this.game.e % 2 == 1 ? 'Bchess' : 'Wchess';
						this.game.e -= 1;
						this.game.um -= 1;
						this.game.chess_Board[parseInt(obj.ox / dis)][parseInt(obj.oy / dis)] = 0;
					} else {
    
    
						return;
					}
				} else {
    
    
					return;
				}
			},
			anewClick() {
    
    
				this.game.h = [];
				this.game.um = 0;
				this.game.chessOff = true;
				for (let i = 0; i <= 13; i++) {
    
    
					this.game.chess_Board[i] = [];
					this.game.lianz[i] = [];
					for (let j = 0; j <= 13; j++) {
    
    
						this.game.chess_Board[i][j] = 0;
						this.game.lianz[i][j] = 0;
					}
				}
			}
		}
	}
</script>

<style>
	page {
    
    
		height: 100%;;
	}

	uni-page-body {
    
    
		height: 100%;;
	}

	.chat-room {
    
    
		display: flex;
		flex-direction: column;
		height: 100%;
	}

	.online-avatar-container {
    
    
		position: fixed;
		right: 0;
		width: 100%;
		height: 80rpx;
		display: flex;
		justify-content: flex-end;
		padding: 28rpx;
		box-shadow: 10rpx 30rpx 50rpx #fff;
		z-index: 40;
		background: #ffffff;
	}

	.online-avatar-item {
    
    
		width: 80rpx;
		height: 80rpx;
		border-radius: 40rpx;
		text-align: center;
		line-height: 80rpx;
		background: rgba(51, 51, 51, 0.3);
		color: #fff;
		font-size: 18rpx 28rpx;
	}

	.online-count {
    
    
		width: 80rpx;
		height: 80rpx;
		border-radius: 40rpx;
		text-align: center;
		line-height: 80rpx;
		background: rgba(51, 51, 51, 0.3);
		color: #fff;
		font-size: 28rpx;
	}

	.online-avatar-item image {
    
    
		width: 80rpx;
		height: 80rpx;
	}

	.chat-room-container {
    
    
		/* padding-top: 100rpx; */
	}

	.scroll-view {
    
    
		overflow-y: auto;
		padding: 20rpx 38rpx 130rpx 38rpx;
		box-sizing: border-box;
		-webkit-overflow-scrolling: touch;
	}

	.message-box {
    
    
		margin-top: 16rpx;
	}

	.message-item {
    
    
		box-sizing: border-box;
		height: 72rpx;
		background-color: rgba(196, 196, 196, 0.2);
		display: inline-block;
		font-size: 28rpx;
		border-radius: 100rpx;
		padding: 18rpx 30rpx;
		font-family: Microsoft YaHei UI;
	}

	.user-name {
    
    
		color: #D02129;
		font-family: Microsoft YaHei UI;
	}

	.user-message {
    
    
		color: #333;
		font-family: Microsoft YaHei UI;
	}

	.chat-room-input {
    
    
		position: fixed;
		bottom: 0;
		height: 92rpx;
		line-height: 92rpx;
		padding: 10rpx 28rpx 20rpx 28rpx;
		display: flex;
		background: #ffffff;
	}

	.uni-input {
    
    
		width: 528rpx;
		background-color: rgba(51, 51, 51, 0.1);
		height: 92rpx;
		border-radius: 100rpx;
		box-sizing: border-box;
		padding: 26rpx 40rpx;
		font-size: 28rpx;
	}

	.uni-btn {
    
    
		position: absolute;
		z-index: 1000;
		width: 72rpx;
		height: 72rpx;
		background: #D02129;
		right: 10rpx;
		top: 10rpx;
		border-radius: 72rpx;
		text-align: center;
		line-height: 72rpx;
		color: #fff;
		font-weight: bold;
		font-size: 32rpx;
	}

	.heart {
    
    
		width: 80rpx;
		height: 92rpx;
		padding: 0 15rpx;
	}

	.rocket {
    
    
		width: 40rpx;
		height: 92rpx;
	}

	.self {
    
    
		color: #D02129;
	}

	.show-animation {
    
    
		width: 80rpx;
		height: 320rpx;
		position: fixed;
		z-index: 44;
		left: 50%;
		bottom: 80rpx;
		margin: 0 -40rpx;
		justify-content: flex-end;
		animation: myanimation 2s linear;
	}

	.prop-heart {
    
    
		height: 80rpx;
		width: 80rpx;
	}

	.prop-rocket {
    
    
		height: 160rpx;
		width: 80rpx;
	}

	@keyframes myanimation {
    
    
		from {
    
    
			bottom: 80rpx;
		}
		to {
    
    
			bottom: 600rpx;
		}
	}






.box {
    
    
	position: relative;
	margin: 50rpx auto;
	width: 750rpx;
	height: 810rpx;
	background: #e6e7ec;
}

.centent {
    
    
	position: absolute;
	width: 730rpx;
	height: 730rpx;
	border: 1px solid #9e9e9e;
	overflow: hidden;
	border-radius: 8rpx;
	box-shadow: 0rpx 0rpx 5rpx 0rpx #9e9e9e;
	left: 10rpx;
	top: 20rpx;
}

.canvas {
    
    
	background: #f7e6b7;
}

.button,
.anew,
.state,
.winner {
    
    
	position: absolute;
	display: block;
	width: 100rpx;
	height: 55rpx;
	border-radius: 10rpx;
	outline: none;
	font-size: 22rpx;
	box-sizing: border-box;
	color: #00bcd4;
	background: #fff;
	border: none;
	box-shadow: 1rpx 1rpx 3rpx 1rpx #9e9e9e;
	top: 760rpx;
	left: 270rpx;
	user-select: none;
}
.anew {
    
    
	left: 150rpx;
}
.state {
    
    
	left: 400rpx;
	width: 140rpx;
}
.state .state-chess,
.winner .state-chess {
    
    
	position: absolute;
	width: 30rpx;
	height: 30rpx;
	top: 11rpx;
	left: 10rpx;
}

.state .chessName,
.winner .chessName {
    
    
	position: absolute;
	width: 80rpx;
	height: 30rpx;
	top: 12rpx;
	left: 45rpx;
	text-align: center;
	line-height: 30rpx;
	font-size: 15rpx;
}

.button:active,
.anew:active {
    
    
	transition-property: all;
	transition-duration: 1s;
	transition-timing-function: ease;
	transition-delay: 0s;
	transform: scale(0.8);
}

.Bchess {
    
    
	position: absolute;
	width: 40rpx;
	height: 40rpx;
	border-radius: 40rpx;
	background: radial-gradient(#9e9e9e -100%, #000000 100%);
	box-shadow: 1rpx 1rpx 2rpx 0rpx #000000;
	font-size: 10rpx;
	line-height: 50rpx;
	text-align: center;
	color: #fff;
}

.Wchess {
    
    
	position: absolute;
	width: 40rpx;
	height: 40rpx;
	border-radius: 40rpx;
	background: radial-gradient(#e4e4e4 10%, #b7aaaa);
	box-shadow: 1rpx 1rpx 2rpx 0rpx #0000006e;
	font-size: 10rpx;
	line-height: 50rpx;
	text-align: center;
	color: #000000;
}

.winner {
    
    
	width: 120rpx;
	left: 12rpx;
	display: none;
}
</style>


3 Synchronize backgammon to the front-end applet

3.1 WebSocket long connection

A simple WebSocket service is implemented with Flask and Flask-SocketIO , first install the library: , create a socketio instance:pip install flask-socketio

# app.py
from flask import Flask
from flask_socketio import SocketIO

socketio = SocketIO()

def create_app(config=None):
    app = Flask(__name__)
    app.config.from_mapping(
        SECRET_KEY='your-secret-key',
        # other Flask config parameters
    )
    if config is not None:
        app.config.from_mapping(config)

    socketio.init_app(app)
    register_blueprints(app)
    return app

def register_blueprints(app):
    from . import chat
    app.register_blueprint(chat.bp)

# chat.py
from flask import Blueprint, request
from flask_socketio import emit, join_room, leave_room

bp = Blueprint('chat', __name__)

@bp.route('/')
def index():
    return "Hello, World!"

@socketio.on('connect')
def on_connect():
    print('WebSocket连接已建立')

@socketio.on('disconnect')
def on_disconnect():
    print('WebSocket连接已断开')

@socketio.on('join')
def on_join(data):
    username = data['username']
    room = data['room']
    join_room(room)
    emit('message', f'{
      
      username} 加入了房间 {
      
      room}', room=room)

@socketio.on('leave')
def on_leave(data):
    username = data['username']
    room = data['room']
    leave_room(room)
    emit('message', f'{
      
      username} 离开了房间 {
      
      room}', room=room)

@socketio.on('message')
def on_message(data):
    username = data['username']
    message = data['message']
    room = data['room']
    emit('message', f'{
      
      username}: {
      
      message}', room=room)

3.2 Get real-time chess

from flask import Flask, request, jsonify
from flask_socketio import SocketIO, emit
import openai
import os

# 初始化Flask应用和SocketIO
app = Flask(__name__)
socketio = SocketIO(app)

# 从环境变量中读取OpenAI API密钥
openai.api_key = os.getenv("OPENAI_API_KEY")

# 处理WebSocket连接事件
@socketio.on('connect')
def handle_connect():
    print('Client connected')

# 处理WebSocket断开连接事件
@socketio.on('disconnect')
def handle_disconnect():
    print('Client disconnected')

# 处理发送请求事件
@socketio.on('send_request')
def handle_request(request_text):
    try:
        # 调用OpenAI API获取回复
        response = openai.Completion.create(
            engine="davinci",
            prompt=request_text,
            max_tokens=50,
            n=1,
            stop=None,
            temperature=0.7
        )

        # 从API响应中提取回复文本
        response_text = response.choices[0].text.strip()

        # 将回复发送给前端
        emit('response', {
    
    'text': response_text})

    except Exception as e:
        # 如果发生异常,将错误信息返回给前端
        emit('response', {
    
    'error': str(e)})


if __name__ == '__main__':
    socketio.run(app, host='0.0.0.0', port=5000)

4 discussions

It is easy to understand the working principle of realizing backgammon. The key difficulty is the interface code of the applet, and the synchronization of chess content to the applet. This step can be understood as visualization. The author is an OS system, and it seems to be a path or environment problem . The error report has not been resolved, the above code is optimized in combination with windows, and the finished product will be introduced in the next part!

Guess you like

Origin blog.csdn.net/weixin_48093827/article/details/130452730