Gogs vor 2 Monaten
Ursprung
Commit
21329a3515

+ 6 - 2
bin/client_msg/common.proto

@@ -7,7 +7,10 @@ message ResLogin {
   string userId = 1;
   string nikeName = 2;
 }
-
+message SiteShowRequest {
+  int32 InitiatorSitPos = 1;  // 发起比牌请求的玩家
+  int32 OpponentSitPos = 2;   // 和谁比
+}
 message ReqUserInfo {
   string UserId = 1;
   int32 Points = 2;
@@ -143,6 +146,8 @@ message ReqDealCard {
 message ReqPlayerAction {
   int32 sitPos = 1;
   PlayerOpt playerOpt = 2;
+  ResBet resBet = 3;
+
 }
 
 // 接到玩家操作
@@ -242,7 +247,6 @@ message ReqRound {
     PlayerOpt playerOpt = 3;
     // 用户id
     string userId = 4;
-    ResBet resBet = 5;
 
 }
 

+ 0 - 2
bin/gamedata/.gitignore

@@ -1,2 +0,0 @@
-*
-!.gitignore

+ 17 - 0
bin/gamedata/robot_head_image.json

@@ -0,0 +1,17 @@
+{
+    "head_image": [
+        "img_head_1",
+        "img_head_2",
+        "img_head_3",
+        "img_head_4",
+        "img_head_5",
+        "img_head_6",
+        "img_head_7",
+        "img_head_8",
+        "img_head_9",
+        "img_head_10",
+        "img_head_11",
+        "img_head_12",
+        "img_head_13"
+    ]
+}

+ 17 - 0
bin/gamedata/robot_name.json

@@ -0,0 +1,17 @@
+{
+    "name": [
+        "阿三",
+        "阿四",
+        "阿五",
+        "阿六",
+        "阿七",
+        "阿八",
+        "阿九",
+        "阿十",
+        "阿十一",
+        "阿十二",
+        "阿十三",
+        "阿十四",
+        "阿十五"
+    ]
+}

+ 48 - 0
bin/gamedata/teen_patti_conf.json

@@ -0,0 +1,48 @@
+{
+    "game_id": "teen_patti",
+    "name": "teen_patti",
+    "status": 1,
+    "think_time": 10,
+    "room_list": [
+        {
+            "type":"cash",
+            "boot": "1",
+            "minBuyin": "10",
+            "chaalLimmit": "128",
+            "potLimit": "1024",
+            "totalPlayers":"1000",
+            "roomLevel": "low",
+            "confId": "1"
+        },
+        {
+            "type":"cash",
+            "boot": "10",
+            "minBuyin": "1000",
+            "chaalLimmit": "12800",
+            "potLimit": "10240",
+            "totalPlayers":"1000",
+            "roomLevel": "mid",
+            "confId": "2"
+        },
+        {
+            "type":"cash",
+            "boot": "100",
+            "minBuyin": "10000",
+            "chaalLimmit": "128000",
+            "potLimit": "102400",
+            "totalPlayers":"1000",
+            "roomLevel": "high",
+            "confId": "3"
+        },
+        {
+            "type":"practice",
+            "boot": "100",
+            "minBuyin": "10000",
+            "chaalLimmit": "128000",
+            "potLimit": "102400",
+            "totalPlayers":"1000",
+            "roomLevel": "practice",
+            "confId": "4"
+        }
+    ]
+}

+ 12 - 0
src/server/common/user_common.go

@@ -0,0 +1,12 @@
+package common
+
+type UserData struct {
+	Id     string
+	Status int
+	GameId string
+	// Teen_Patti_Room *room.Room
+	Head_Image string
+	Nickname   string
+	Room_id    string
+	Points     int32
+}

+ 58 - 59
src/server/db/mongodb/mongodbmgr.go

@@ -2,7 +2,6 @@ package mongodbmgr
 
 import (
 	"server/conf"
-	"time"
 
 	"github.com/name5566/leaf/db/mongodb"
 	"github.com/name5566/leaf/log"
@@ -16,7 +15,7 @@ var dialContext = new(mongodb.DialContext)
 
 func init() {
 	Connect()
-	test()
+	// test()
 }
 func Connect() {
 	c, err := mongodb.Dial(conf.MongoDBAddr, 10)
@@ -85,60 +84,60 @@ func (c *Collection) FindMany(query bson.M, skip, limit int, result interface{})
 	return s.DB(c.DB).C(c.Collection).Find(query).Skip(skip).Limit(limit).All(result)
 }
 
-func test() {
-	// 1. 用户集合操作
-	type User struct {
-		UserID   int64  `bson:"user_id"`
-		Name     string `bson:"name"`
-		Password string `bson:"password"`
-	}
-
-	userColl := NewCollection(conf.MongoDBName, "users")
-
-	// 插入用户
-	user := &User{UserID: 1001, Name: "player1"}
-	err := userColl.Insert(user)
-	if err != nil {
-		log.Error(err.Error())
-	}
-
-	// 查询用户
-	findUser := new(User)
-	err = userColl.FindOne(bson.M{"user_id": 1001}, findUser)
-
-	if err != nil {
-		log.Error(err.Error())
-	}
-	// 2. 游戏记录集合操作
-	type GameRecord struct {
-		GameID    int64   `bson:"game_id"`
-		Players   []int64 `bson:"players"`
-		CreatedAt int64   `bson:"created_at"`
-	}
-
-	gameColl := NewCollection("teen_patti", "game_records")
-
-	// 插入游戏记录
-	record := &GameRecord{
-		GameID:    1,
-		Players:   []int64{1001, 1002},
-		CreatedAt: time.Now().Unix(),
-	}
-	err = gameColl.Insert(record)
-	if err != nil {
-		log.Error(err.Error())
-	}
-
-	// 查询游戏记录列表
-	var records []GameRecord
-	err = gameColl.FindMany(
-		bson.M{"players": 1001},
-		0,
-		10,
-		&records,
-	)
-	if err != nil {
-		log.Error(err.Error())
-	}
-	log.Error("records:", records)
-}
+// func test() {
+// 	// 1. 用户集合操作
+// 	type User struct {
+// 		UserID   int64  `bson:"user_id"`
+// 		Name     string `bson:"name"`
+// 		Password string `bson:"password"`
+// 	}
+
+// 	userColl := NewCollection(conf.MongoDBName, "users")
+
+// 	// 插入用户
+// 	user := &User{UserID: 1001, Name: "player1"}
+// 	err := userColl.Insert(user)
+// 	if err != nil {
+// 		log.Error(err.Error())
+// 	}
+
+// 	// 查询用户
+// 	findUser := new(User)
+// 	err = userColl.FindOne(bson.M{"user_id": 1001}, findUser)
+
+// 	if err != nil {
+// 		log.Error(err.Error())
+// 	}
+// 	// 2. 游戏记录集合操作
+// 	type GameRecord struct {
+// 		GameID    int64   `bson:"game_id"`
+// 		Players   []int64 `bson:"players"`
+// 		CreatedAt int64   `bson:"created_at"`
+// 	}
+
+// 	gameColl := NewCollection("teen_patti", "game_records")
+
+// 	// 插入游戏记录
+// 	record := &GameRecord{
+// 		GameID:    1,
+// 		Players:   []int64{1001, 1002},
+// 		CreatedAt: time.Now().Unix(),
+// 	}
+// 	err = gameColl.Insert(record)
+// 	if err != nil {
+// 		log.Error(err.Error())
+// 	}
+
+// 	// 查询游戏记录列表
+// 	var records []GameRecord
+// 	err = gameColl.FindMany(
+// 		bson.M{"players": 1001},
+// 		0,
+// 		10,
+// 		&records,
+// 	)
+// 	if err != nil {
+// 		log.Error(err.Error())
+// 	}
+// 	log.Error("records:", records)
+// }

+ 48 - 0
src/server/db/mongodb/userMongodb.go

@@ -0,0 +1,48 @@
+package mongodbmgr
+
+import (
+	"log"
+	"server/common"
+	"server/conf"
+
+	"gopkg.in/mgo.v2/bson"
+)
+
+// 定义用户集合操作
+var userCollection = NewCollection(conf.MongoDBName, "users")
+
+// 新增用户到 MongoDB
+func AddUser(userData *common.UserData) error {
+	// 创建插入的用户数据
+	err := userCollection.Insert(userData)
+	if err != nil {
+		log.Printf("Error inserting user data into MongoDB: %v", err)
+		return err
+	}
+	log.Printf("User data added to MongoDB for userId: %s", userData.Id)
+	return nil
+}
+
+// 更新用户信息到 MongoDB
+func UpdateUserInfo(userData *common.UserData) error {
+	// 使用用户 ID 来更新用户数据
+	err := userCollection.Update(bson.M{"id": userData.Id}, bson.M{"$set": userData})
+	if err != nil {
+		log.Printf("Error updating user data in MongoDB: %v", err)
+		return err
+	}
+	log.Printf("User data updated in MongoDB for userId: %s", userData.Id)
+	return nil
+}
+
+// 获取用户信息从 MongoDB
+func GetUserInfoFromMongoDB(userId string) (*common.UserData, error) {
+	var userData common.UserData
+	err := userCollection.FindOne(bson.M{"id": userId}, &userData) // 查找用户ID
+	if err != nil {
+		log.Printf("Error fetching user data from MongoDB: %v", err)
+		// 如果没有找到用户数据,可以返回 nil 或者处理错误
+		return nil, err
+	}
+	return &userData, nil
+}

+ 5 - 0
src/server/db/redis/gateRedis.go

@@ -0,0 +1,5 @@
+package redismgr
+
+func setUser() {
+
+}

+ 6 - 0
src/server/db/redis/redismgr.go

@@ -31,6 +31,12 @@ func Connect() {
 			)
 		},
 	}
+	log.Release("redis Connect success")
+}
+
+// GetPool 获取连接池
+func GetPool() *redis.Pool {
+	return pool
 }
 
 // Set 设置键值对

+ 157 - 0
src/server/db/redis/robotRedis.go

@@ -0,0 +1,157 @@
+package redismgr
+
+import (
+	"encoding/json"
+	"server/common"
+
+	"github.com/gomodule/redigo/redis"
+	"github.com/name5566/leaf/log"
+)
+
+// 存储机器人的信息到 Redis
+func SaveRobotInfoToRedis(robotID string, robotData *common.UserData) error {
+	// 获取 Redis 连接
+	conn := GetPool().Get()
+	defer conn.Close()
+
+	// 将机器人信息序列化成 JSON 字符串
+	robotDataJson, err := json.Marshal(robotData)
+	if err != nil {
+		log.Debug("Error marshalling robot data: %v", err)
+		return err
+	}
+
+	// 存储机器人信息,使用 HSET 命令
+	_, err = conn.Do("HSET", "robot:"+robotID, "data", string(robotDataJson))
+	if err != nil {
+		log.Debug("Error saving robot data to Redis: %v", err)
+		return err
+	}
+
+	// 设置过期时间(例如,设置3天过期)
+	_, err = conn.Do("EXPIRE", "robot:"+robotID, 86400*3) // 86400*3 = 3天
+	if err != nil {
+		log.Debug("Error setting expiry for robot data in Redis: %v", err)
+		return err
+	}
+
+	log.Debug("Robot %s data saved to Redis", robotID)
+	return nil
+}
+
+// 更新机器人信息(例如更新积分)
+func UpdateRobotPointsInRedis(robotID string, newPoints int32) error {
+	// 获取 Redis 连接
+	conn := GetPool().Get()
+	defer conn.Close()
+
+	// 获取现有的机器人信息
+	robotData, err := GetRobotInfoFromRedis(robotID)
+	if err != nil || robotData == nil {
+		log.Debug("Error fetching robot data for update: %v", err)
+		return err
+	}
+
+	// 更新积分
+	robotData.Points = newPoints
+
+	// 存储更新后的机器人信息
+	err = SaveRobotInfoToRedis(robotID, robotData)
+	if err != nil {
+		log.Debug("Error saving updated robot data to Redis: %v", err)
+		return err
+	}
+
+	log.Debug("Updated robot %s points to: %d", robotID, newPoints)
+	return nil
+}
+
+// 获取机器人的信息从 Redis
+func GetRobotInfoFromRedis(robotID string) (*common.UserData, error) {
+	// 获取 Redis 连接
+	conn := GetPool().Get()
+	defer conn.Close()
+
+	// 获取机器人信息
+	robotDataJson, err := redis.String(conn.Do("HGET", "robot:"+robotID, "data"))
+	if err == redis.ErrNil {
+		// 如果没有找到机器人信息
+		log.Debug("Robot data not found in Redis for robotID: %s", robotID)
+		return nil, nil
+	}
+	if err != nil {
+		log.Debug("Error fetching robot data from Redis: %v", err)
+		return nil, err
+	}
+
+	// 将 JSON 字符串反序列化为 UserData 对象
+	var robotData common.UserData
+	err = json.Unmarshal([]byte(robotDataJson), &robotData)
+	if err != nil {
+		log.Debug("Error unmarshalling robot data: %v", err)
+		return nil, err
+	}
+
+	log.Debug("Fetched robot data for robotID: %s", robotID)
+	return &robotData, nil
+}
+
+// 将机器人添加到空闲队列
+func AddRobotToFreeRobots(confID, robotID string) error {
+	conn := GetPool().Get()
+	defer conn.Close()
+
+	// 将机器人ID推入到空闲队列的左侧
+	_, err := conn.Do("LPUSH", "freeRobots:"+confID, robotID)
+	if err != nil {
+		log.Debug("Error adding robot to freeRobots queue: %v", err)
+		return err
+	}
+
+	log.Debug("Robot %s added to freeRobots queue for confID: %s", robotID, confID)
+	return nil
+}
+
+// 获取一个空闲状态的机器人ID
+func GetFreeRobot(confID string) (string, error) {
+	conn := GetPool().Get()
+	defer conn.Close()
+
+	// 从空闲队列的右侧弹出一个机器人ID
+	robotID, err := redis.String(conn.Do("RPOP", "freeRobots:"+confID))
+	if err == redis.ErrNil {
+		// 如果队列为空,表示没有空闲机器人
+		log.Debug("No free robots available in queue for confID: %s", confID)
+		return "0", nil
+	}
+	if err != nil {
+		log.Debug("Error fetching free robot from queue: %v", err)
+		return "", err
+	}
+
+	log.Debug("Fetched free robot %s from queue for confID: %s", robotID, confID)
+	return robotID, nil
+}
+
+func ResetRobotRoomID(robotID string) {
+	conn := GetPool().Get()
+	defer conn.Close()
+
+	_, err := conn.Do("HSET", "robot:"+robotID, "Room_id", "0")
+	if err != nil {
+		log.Debug("Error resetting Room_id for robot %s: %v", robotID, err)
+	} else {
+		log.Debug("Successfully reset Room_id for robot %s", robotID)
+	}
+}
+func AddRobotToFreeQueue(confID string, robotID string) {
+	conn := GetPool().Get()
+	defer conn.Close()
+
+	_, err := conn.Do("LPUSH", "freeRobots:"+confID, robotID)
+	if err != nil {
+		log.Debug("Error adding robot %s back to free queue: %v", robotID, err)
+	} else {
+		log.Debug("Robot %s added back to free queue", robotID)
+	}
+}

+ 122 - 0
src/server/db/redis/teenPattiRoomRedis.go

@@ -0,0 +1,122 @@
+package redismgr
+
+import (
+	"encoding/json"
+	"server/game/room"
+
+	"github.com/gomodule/redigo/redis"
+	"github.com/name5566/leaf/log"
+)
+
+// 保存房间的基础信息和玩家信息到 Redis(去掉 GameRound 和 RoundSitPos)
+func SaveRoomToRedis(roomInfo *room.Room) error {
+	// 获取 Redis 连接
+	conn := GetPool().Get()
+	defer conn.Close()
+
+	// 将房间的基础信息(不包含 GameRound 和 RoundSitPos)序列化为 JSON
+	roomJson, err := json.Marshal(roomInfo)
+	if err != nil {
+		log.Debug("Error marshalling room data: %v", err)
+		return err
+	}
+
+	// 使用 HSET 存储房间的 JSON 字符串
+	_, err = conn.Do("HSET", "teem_patti:"+roomInfo.Id, "RoomData", string(roomJson))
+	if err != nil {
+		log.Debug("Error saving room data to Redis: %v", err)
+		return err
+	}
+
+	// 设置房间的过期时间为 3 天
+	_, err = conn.Do("EXPIRE", "teem_patti:"+roomInfo.Id, 86400*3) // 86400*3 = 3 天
+	if err != nil {
+		log.Debug("Error setting expiry for room data in Redis: %v", err)
+		return err
+	}
+
+	log.Debug("Room data saved to Redis for roomId: %s", roomInfo.Id)
+	return nil
+}
+
+func GetRoomFromRedis(roomId string) (*room.Room, error) {
+	// 获取 Redis 连接
+	conn := GetPool().Get()
+	defer conn.Close()
+
+	// 获取房间的 JSON 字符串
+	roomData, err := redis.String(conn.Do("HGET", "teem_patti:"+roomId, "RoomData"))
+	if err != nil {
+		log.Debug("Error fetching room data from Redis: %v", err)
+		return nil, err
+	}
+
+	// 反序列化 JSON 字符串为 Room 结构体
+	var room room.Room
+	err = json.Unmarshal([]byte(roomData), &room)
+	if err != nil {
+		log.Debug("Error unmarshalling room data: %v", err)
+		return nil, err
+	}
+
+	// 返回房间数据
+	return &room, nil
+}
+func IsRoomExistInRedis(roomId string) (bool, error) {
+	// 获取 Redis 连接
+	conn := GetPool().Get()
+	defer conn.Close()
+
+	// 检查房间 ID 是否存在于 Redis 集合中
+	exists, err := redis.Bool(conn.Do("SISMEMBER", "teen_patti_rooms", roomId))
+	if err != nil {
+		log.Debug("Error checking if roomId exists in teen_patti_rooms: %v", err)
+		return false, err
+	}
+
+	return exists, nil
+}
+
+func SaveRoomIDToRedis(roomId string) error {
+	// 获取 Redis 连接
+	conn := GetPool().Get()
+	defer conn.Close()
+
+	// 检查房间 ID 是否已经存在于集合中
+	exists, err := redis.Bool(conn.Do("SISMEMBER", "teen_patti_rooms", roomId))
+	if err != nil {
+		log.Debug("Error checking if roomId exists in teen_patti_rooms: %v", err)
+		return err
+	}
+
+	if exists {
+		log.Debug("Room ID %s already exists in teen_patti_rooms", roomId)
+		return nil // 如果房间 ID 已存在,直接返回
+	}
+
+	// 将房间 ID 存储到 Redis 集合中
+	_, err = conn.Do("SADD", "teen_patti_rooms", roomId)
+	if err != nil {
+		log.Debug("Error adding roomId to teen_patti_rooms: %v", err)
+		return err
+	}
+
+	log.Debug("Room ID %s added to teen_patti_rooms", roomId)
+	return nil
+}
+
+func RemoveRoomIDFromRedis(roomId string) error {
+	// 获取 Redis 连接
+	conn := GetPool().Get()
+	defer conn.Close()
+
+	// 从 Redis 集合中删除房间 ID
+	_, err := conn.Do("SREM", "teen_patti_rooms", roomId)
+	if err != nil {
+		log.Debug("Error removing roomId from teen_patti_rooms: %v", err)
+		return err
+	}
+
+	log.Debug("Room ID %s removed from active_rooms", roomId)
+	return nil
+}

+ 70 - 0
src/server/db/redis/userRedis.go

@@ -0,0 +1,70 @@
+package redismgr
+
+import (
+	"encoding/json"
+	"server/common"
+
+	"github.com/gomodule/redigo/redis"
+	"github.com/name5566/leaf/log"
+)
+
+// 保存用户信息到 Redis 哈希表
+func SaveUserInfoToRedis(userData *common.UserData) error {
+	// 将用户数据转换为 JSON 字符串
+	userDataJson, err := json.Marshal(userData)
+	if err != nil {
+		log.Debug("Error marshalling user data: %v", err)
+		return err
+	}
+
+	// 获取 Redis 连接
+	conn := GetPool().Get()
+	defer conn.Close()
+
+	// 使用 HSET 存储用户数据
+	_, err = conn.Do("HSET", "user:"+userData.Id, "data", string(userDataJson))
+	if err != nil {
+		log.Debug("Error saving user data to Redis: %v", err)
+		return err
+	}
+
+	// 设置过期时间为3天
+	_, err = conn.Do("EXPIRE", "user:"+userData.Id, 86400*3) // 86400*3 = 3天
+	if err != nil {
+		log.Debug("Error setting expiry for user data in Redis: %v", err)
+		return err
+	}
+	// 进入redis队列存mongodb .
+	// UpdateUserInfo(userData)
+	log.Debug("User data saved to Redis for userId: %s", userData.Id)
+	return nil
+}
+
+// 获取用户信息从 Redis 哈希表
+func GetUserInfoFromRedis(userId string) (*common.UserData, error) {
+	// 获取 Redis 连接
+	conn := GetPool().Get()
+	defer conn.Close()
+
+	// 从 Redis 获取用户数据
+	userDataJson, err := redis.String(conn.Do("HGET", "user:"+userId, "data"))
+	if err == redis.ErrNil {
+		// 如果 Redis 中没有数据
+		log.Debug("User data not found in Redis for userId: %s", userId)
+		return nil, nil
+	} else if err != nil {
+		log.Debug("Error fetching user data from Redis: %v", err)
+		return nil, err
+	}
+
+	// 将获取到的 JSON 字符串反序列化为 UserData 对象
+	var userData common.UserData
+	err = json.Unmarshal([]byte(userDataJson), &userData)
+	if err != nil {
+		log.Debug("Error unmarshalling user data from Redis: %v", err)
+		return nil, err
+	}
+
+	log.Debug("User data fetched from Redis for userId: %s", userId)
+	return &userData, nil
+}

+ 2 - 0
src/server/events/events.go

@@ -6,7 +6,9 @@ type EventType int
 
 const (
 	EventTeenPattiGameInfo EventType = iota
+	EventGetTeenPattiRoomId
 	EventJoinTeenPattiRoom
+	EventLeaveTeenPattiRoom
 	EventTeenPattiPlayerOptAction
 )
 

+ 17 - 6
src/server/game/internal/chanrpc.go

@@ -1,6 +1,7 @@
 package internal
 
 import (
+	"server/common"
 	"server/msg"
 	"server/user"
 	"time"
@@ -27,6 +28,8 @@ func rpcNewAgent(args []interface{}) {
 }
 
 func rpcCloseAgent(args []interface{}) {
+	log.Debug("rpcCloseAgent")
+
 	agent := args[0].(gate.Agent)
 	// 清理未认证连接
 	_, exists := pendingConns[agent]
@@ -36,6 +39,11 @@ func rpcCloseAgent(args []interface{}) {
 	// 清理已认证连接
 	for userID, a := range authedUsers {
 		if a == agent {
+			a.SetUserData(&common.UserData{
+				Id:       userID,
+				Nickname: userID,
+				Status:   0,
+			})
 			delete(authedUsers, userID)
 			break
 		}
@@ -63,16 +71,19 @@ func handleAuth(args []interface{}) {
 			oldAgent.Close()
 			delete(authedUsers, m.UserId)
 		}
-
-		a.SetUserData(&user.UserData{
-			Id:              m.UserId,
-			Nickname:        m.NikeName,
-			Teen_Patti_Room: nil,
+		// 获取用户信息 ,如果新用户则创建一个
+		userData := user.GetUserInfoById(m.UserId)
+		a.SetUserData(&common.UserData{
+			Id:       m.UserId,
+			Nickname: m.UserId,
+			Status:   1,
 		})
+
 		// 发送登录成功响应
 		a.WriteMsg(&msg.ReqLogin{
-			NikeName:   m.NikeName,
+			NikeName:   m.UserId,
 			UserId:     m.UserId,
+			Points:     int32(userData.Points),
 			GameStatus: "online",
 		})
 	}

+ 28 - 6
src/server/game/room/room.go

@@ -14,6 +14,7 @@ type GameRound struct {
 	TotalBet int32
 	//回合列表
 	Rounds []*msg.ReqRound
+	ResBet *msg.ResBet
 }
 
 // RoomStatus 房间状态
@@ -26,6 +27,7 @@ const (
 
 type Room struct {
 	Id      string
+	ConfId  string
 	Players []*Player
 	Status  int
 	// 当前轮次
@@ -166,11 +168,15 @@ func (room *Room) GetPlayersBySitPos(startSitPos int32) []*Player {
 
 // 根据座位号获取玩家
 func (r *Room) GetPlayerBySitPos(sitPos int32) *Player {
+	// 查找对应座位号的玩家
 	for _, player := range r.Players {
 		if player.SitPos == sitPos {
+			log.Debug("Player found: SitPos: %d, UserId: %s", player.SitPos, player.Id)
 			return player
 		}
 	}
+	// 如果没有找到玩家,记录日志并返回 nil
+	log.Debug("Player with SitPos %d not found in room.", sitPos)
 	return nil
 }
 
@@ -180,7 +186,7 @@ func (r *Room) AddPlayerOpt(round *msg.ReqRound) {
 	r.GameRound.Rounds = append(r.GameRound.Rounds, round)
 }
 
-// 设置当前回合为下个没有弃牌的玩家(时针)
+// 设置当前回合为下个没有弃牌的玩家(时针)
 func (r *Room) SetNextRound() {
 	curSitPos := int32(r.RoundSitPos)
 
@@ -190,7 +196,8 @@ func (r *Room) SetNextRound() {
 	sort.Slice(r.Players, func(i, j int) bool {
 		return r.Players[i].SitPos < r.Players[j].SitPos
 	})
-
+	count := len(r.Players)
+	log.Debug("总人数count", count)
 	// 获取当前座位号的索引
 	curSitPosIndex := -1
 	for i, player := range r.Players {
@@ -200,17 +207,17 @@ func (r *Room) SetNextRound() {
 		}
 	}
 
-	// 逆时针:当前索引减1
-	// 如果减1后小于0,则回到最后一个位置
+	// 顺时针:当前索引加1
+	// 如果加1后超出最大索引,则回到第一个位置
 	nextIndex := curSitPosIndex
 	for i := 0; i < len(r.Players); i++ {
-		nextIndex = (curSitPosIndex - 1 - i + len(r.Players)) % len(r.Players)
+		nextIndex = (curSitPosIndex + 1 + i) % len(r.Players)
 		if !r.Players[nextIndex].IsPacked {
 			break
 		}
 	}
 	r.RoundSitPos = int(r.Players[nextIndex].SitPos)
-	log.Debug("SetNextRound:下一个操作的玩家座位:%d", r.RoundSitPos)
+	log.Debug("SetNextRound: 下一个操作的玩家座位:%d", r.RoundSitPos)
 }
 
 // 获取没有弃牌的玩家是否只有一个,并返回没有弃牌的座位号
@@ -233,7 +240,22 @@ func (r *Room) PlayerWin(sitPos int32) {
 	r.Status = RoomStatusEnd
 }
 
+func (r *Room) UpdateResBet(bet int32, chaalLimmit int32) {
+	// 获取当前状态,是否修改
+	if bet*2 < chaalLimmit {
+		// 动态修改下注金额
+		r.GameRound.ResBet.MinBlindBet = bet
+		r.GameRound.ResBet.MaxBlindBet = bet * 2
+		r.GameRound.ResBet.MinCheelBet = bet * 2
+		r.GameRound.ResBet.MaxCheelBet = bet * 2
+	} else if bet*4 < chaalLimmit {
+		r.GameRound.ResBet.MaxCheelBet = bet * 4
+	}
+
+}
+
 // 增加押注
 func (r *Room) AddBet(bet int32) {
 	r.GameRound.TotalBet += bet
+
 }

+ 23 - 0
src/server/game/teen/betting.go

@@ -0,0 +1,23 @@
+package teen
+
+import (
+	"strconv"
+
+	"github.com/name5566/leaf/log"
+)
+
+func getAnte(ConfId string) int32 {
+	// 遍历 GameConfig.RoomList,查找对应的 ConfId
+	for _, betinfo := range GameConfig.RoomList {
+		if betinfo.ConfId == ConfId { // 判断房间 ID 是否匹配
+			// 返回该房间的 boot 值作为底注
+			ante, err := strconv.Atoi(betinfo.Boot)
+			if err != nil {
+				log.Fatal("Error converting boot to int:", err)
+			}
+			return int32(ante)
+		}
+	}
+	// 如果没有找到匹配的房间,返回一个默认值或错误提示
+	return 10 // 默认底注为 10,可以根据需要修改
+}

+ 323 - 12
src/server/game/teen/buildRoom.go

@@ -1,19 +1,116 @@
 package teen
 
 import (
+	"fmt"
+	"math/rand"
+	"server/common"
+	redismgr "server/db/redis"
+	"server/events"
+	"server/game"
 	"server/game/room"
 	"server/msg"
+	"server/user"
+	"strconv"
+	"time"
 
+	"github.com/name5566/leaf/gate"
 	"github.com/name5566/leaf/log"
 )
 
+func MatchTeenPattiRoom(event events.Event) *room.Room {
+	// 从事件中解析消息数据
+
+	userData := event.Agent.UserData().(*common.UserData)
+	RoomId := userData.Room_id // 默认房间ID是0 , 离开房间的时候需要重置为”0“
+
+	m := event.Data.(*msg.ResJoinRoom)
+	// 检查房间是否存在
+	r := getRoomInfo(RoomId)
+	// 房间不存在的情况
+	if r == nil {
+		log.Debug("Room %s does not exist, creating a new room or finding available room.", RoomId)
+		// 在这里可以加入创建新房间或选择符合条件的房间的逻辑
+		// 例如:创建一个新房间
+		teenPattiRoom := createRoom(m.ConfId)
+
+		// 返回创建的房间指针
+		return teenPattiRoom
+		// 返回创建的房间
+	}
+	// 房间存在的情况
+	log.Debug("Room %s exists. Proceeding with user join.", RoomId)
+
+	// 返回获取到的房间
+	return r
+
+}
+func handleJoinTeenPattiRoom(event events.Event) {
+	m := event.Data.(*msg.ResJoinRoom)
+	// 匹配房间, 没有就建一个
+	r := MatchTeenPattiRoom(event)
+	// 判断是否符合加入条件
+	isTrue := canJoinRoom(m.ConfId, event.Agent)
+	// 如果条件不符合,返回房间创建失败信息,并终止后续代码执行
+	if !isTrue {
+		log.Debug("Room creation failed, returning RoomId: 0")
+		// 发送失败的房间信息返回给客户端
+		event.Agent.WriteMsg(&msg.JoinRoomFail{
+			GameId: m.GameId,
+			ConfId: m.ConfId,
+			UserId: m.UserId,
+		})
+		return
+	}
+	userData := event.Agent.UserData().(*common.UserData)
+	log.Debug("Room created with ID: %v", r.Id)
+
+	// 玩家加入房间
+	joinRoomAsPlayer(userData, event.Agent, r)
+
+	// 判断房间人数,如果小于2个,加入机器人
+	PlayerCount := len(r.Players)
+	if PlayerCount < 2 {
+		// random := rand.Intn(3) + 1
+		random := 1
+		for i := 0; i < random; i++ {
+			joinRoomAsRobot(r)
+		}
+		// 设置下一个庄家
+		setNextDealerSitPos(r, 1)
+	}
+
+	// 向每个玩家发送当前回合的信息
+	for _, player := range r.Players {
+		if player.Agent != nil {
+			player.Agent.WriteMsg(&msg.ReqJoinRoom{
+				UserId:   player.Id,
+				RoomId:   r.Id,
+				GameId:   "teen_patti",
+				RoomInfo: buildRoom(r),
+				SitPos:   player.SitPos,
+			})
+			log.Debug("Sent current round info to player %v in room %v", player.Id, r.Id)
+		}
+	}
+
+	// 如果房间状态为等待状态(RoomStatusWaiting),则2秒后开始发牌
+	if r.Status == 0 {
+		game.Module.Skeleton.AfterFunc(time.Second*2, func() {
+			go startDealCard(r)
+		})
+	}
+	log.Debug("EventJoinTeenPattiRoom")
+}
 func buildRoom(teenPattiRoom *room.Room) *msg.ReqRoom {
+	log.Debug("teenPattiRoom11111111111111111: %+v", teenPattiRoom)
+	log.Debug("After creating room, ResBet: %+v", teenPattiRoom.GameRound.ResBet)
 	return &msg.ReqRoom{
 		Id:         teenPattiRoom.Id,
 		Status:     int32(teenPattiRoom.Status),
 		Round:      int32(teenPattiRoom.Round),
 		ReqPlayers: convertToMsgPlayerList(teenPattiRoom.Players),
 		GameRound:  convertToMsgGameRound(teenPattiRoom.GameRound),
+		BetInfo:    teenPattiRoom.GameRound.ResBet,
 	}
 }
 
@@ -41,13 +138,6 @@ func convertToMsgPlayerList(players []*room.Player) []*msg.ReqPlayer {
 }
 
 func convertToMsgCardList(cards []*msg.ReqCard) []*msg.ReqCard {
-	// msgCards := make([]*msg.ReqCard, len(*cards))
-	// for i, card := range *cards {
-	// 	msgCards[i] = &msg.ReqCard{
-	// 		Color: int32(card.Color),
-	// 		Point: int32(card.Point),
-	// 	}
-	// }
 	return cards
 }
 
@@ -58,10 +148,6 @@ func convertToMsgGameRound(gameRound *room.GameRound) *msg.ReqGameRound {
 	}
 }
 func convertToMsgRoundList(rounds []*msg.ReqRound) []*msg.ReqRound {
-	// msgRounds := make([]*msg.ReqRound, len(rounds))
-	// for i := range rounds {
-	// 	msgRounds[i] = rounds[i]
-	// }
 	return rounds
 }
 
@@ -75,7 +161,7 @@ func convertToMsgRoomList(rooms []TeenPattiRoom) *msg.TeenPattiRoomList {
 			PotLimit:     room.PotLimit,
 			TotalPlayers: room.TotalPlayers,
 			RoomLevel:    room.RoomLevel,
-			RoomId:       room.RoomId,
+			ConfId:       room.ConfId,
 			Type:         room.Type,
 		}
 	}
@@ -83,3 +169,228 @@ func convertToMsgRoomList(rooms []TeenPattiRoom) *msg.TeenPattiRoomList {
 		TeenPattiRoom: msgRooms,
 	}
 }
+
+// 判断用户是否符合房间条件
+func canJoinRoom(ConfId string, agent gate.Agent) bool {
+	// 查找匹配的房间
+	userData := agent.UserData().(*common.UserData)
+	userData = user.GetUserInfoById(userData.Id)
+	for _, room := range GameConfig.RoomList {
+		if room.ConfId == ConfId {
+			// 获取房间的最低买入金额
+			minBuyin := room.MinBuyin
+			minBuyinInt, err := strconv.Atoi(minBuyin)
+			if err != nil {
+				log.Error("Invalid minBuyin value: %v", minBuyin)
+			}
+			// 判断用户的余额是否满足最低买入金额
+			if userData.Points < int32(minBuyinInt) {
+				log.Debug("User %s does not have enough balance. Required: %d, Available: %d\n", userData.Id, minBuyin, userData.Points)
+				agent.WriteMsg(&msg.ReqInsufficientPoints{
+					UserId:          userData.Id,
+					RequiredAmount:  int32(minBuyinInt),
+					AvailableAmount: userData.Points,
+					GameId:          "teen_patti",
+				})
+			}
+			// 如果符合条件,返回 true
+			return true
+		}
+	}
+
+	// 如果没有找到匹配的房间
+	log.Debug("Room not found with ID:", ConfId)
+	return false
+}
+func generateRoomId() string {
+	return fmt.Sprintf("room_%d", time.Now().UnixNano())
+}
+func loadTeemConf(ConfId string) *msg.ResBet {
+	for _, betinfo := range GameConfig.RoomList {
+		if betinfo.ConfId == ConfId {
+			// 找到对应的房间配置
+			Boot, err := strconv.Atoi(betinfo.Boot)
+			if err != nil {
+				log.Fatal("Error converting Boot:", err)
+			}
+			// 构造并返回 ResBet
+			return &msg.ResBet{
+				MinBlindBet: int32(Boot),
+				MaxBlindBet: int32(Boot * 2),
+				MinCheelBet: int32(Boot),
+				MaxCheelBet: int32(Boot * 2),
+				RoomMinBet:  int32(Boot),
+				RoomMaxBet:  int32(Boot * 128),
+			}
+		}
+	}
+	return &msg.ResBet{
+		MinBlindBet: 10,
+		MaxBlindBet: 20,
+		MinCheelBet: 10,
+		MaxCheelBet: 20,
+		RoomMinBet:  1,
+		RoomMaxBet:  128,
+	}
+}
+func createRoom(ConfId string) *room.Room {
+
+	roomId := generateRoomId() // 生成房间 ID
+	confInfo := loadTeemConf(ConfId)
+	room := &room.Room{
+		Id:      roomId,
+		Status:  room.RoomStatusWaiting,
+		Round:   1,
+		Players: make([]*room.Player, 0),
+		ConfId:  ConfId,
+		GameRound: &room.GameRound{
+			Rounds:   make([]*msg.ReqRound, 0),
+			TotalBet: 0,
+			ResBet:   confInfo,
+		},
+		CardDeck: make([]*msg.ReqCard, 0),
+	}
+
+	mu.Lock()
+	log.Debug("创建roomid", roomId)
+	roomStorage[roomId] = room
+	mu.Unlock()
+	return room
+}
+
+func dissolveRoom(teenPattiRoom *room.Room) {
+	if teenPattiRoom == nil {
+		return
+	}
+
+	log.Debug("Dissolving room %s", teenPattiRoom.Id)
+
+	// 移除所有机器人
+	for _, player := range teenPattiRoom.Players {
+		if player.IsRobot {
+			// 机器人退出房间,重置 Room_id
+			redismgr.ResetRobotRoomID(player.Id)
+			// 将机器人放回 Redis 空闲队列
+			redismgr.AddRobotToFreeQueue(teenPattiRoom.ConfId, player.Id)
+			log.Debug("Removed robot %s from room %s", player.Id, teenPattiRoom.Id)
+		}
+	}
+
+	// 释放房间资源
+	mu.Lock()
+	delete(roomStorage, teenPattiRoom.Id)
+	mu.Unlock()
+
+	log.Debug("Room %s has been dissolved", teenPattiRoom.Id)
+}
+
+func getRoomInfo(roomId string) *room.Room {
+	// 查找房间是否存在于内存中的 roomStorage
+	room, exists := roomStorage[roomId]
+	if !exists {
+		return nil
+	}
+	return room
+}
+
+// 处理机器人加入房间
+func joinRoomAsRobot(teenPattiRoom *room.Room) {
+	randomSitPos := getRandomSitPos(teenPattiRoom) // 随机选择一个座位
+
+	// 获取或创建新的机器人
+	robotUserData, err := getOrCreateTeenPattiRobot(teenPattiRoom)
+	if err != nil {
+		log.Debug("Error getting or creating robot: %v", err)
+		return
+	}
+
+	// 将机器人加入房间
+	teenPattiRoom.Players = append(teenPattiRoom.Players, &room.Player{
+		Id:        robotUserData.Id, // 使用时间戳生成机器人ID
+		Agent:     nil,              // 机器人没有 agent
+		IsRobot:   true,
+		UserData:  robotUserData,
+		SitPos:    randomSitPos,
+		IsPacked:  false,
+		IsSeen:    false,
+		IsShow:    false,
+		IsDealer:  false,
+		CanShow:   false,
+		HandCards: []*msg.ReqCard{},
+	})
+
+}
+
+// 离开房间
+func LeaveTeenPattiRoom(teenPattiRoom *room.Room, userId string, leaveReason string) {
+	for i, player := range teenPattiRoom.Players {
+		if player.Id == userId {
+			log.Debug("Player %s left room %s", userId, teenPattiRoom.Id)
+			player.Agent.SetUserData(&common.UserData{
+				GameId: "",
+			})
+
+			// 发送离开消息
+			if player.Agent != nil {
+				player.Agent.WriteMsg(&msg.ReqLeaveRoom{
+					GameId:      "teen_patti",
+					RoomId:      teenPattiRoom.Id,
+					UserId:      player.Id,
+					LeaveReason: leaveReason,
+				})
+			}
+			// 清除用户的房间 ID
+			if userData, ok := player.Agent.UserData().(*common.UserData); ok && userData != nil {
+				userData.Room_id = ""
+			}
+			// 从房间玩家列表中移除该玩家
+			teenPattiRoom.Players = append(teenPattiRoom.Players[:i], teenPattiRoom.Players[i+1:]...)
+			return // 退出循环,确保只删除一个玩家
+		}
+	}
+
+	log.Debug("Player %s not found in room %s", userId, teenPattiRoom.Id)
+}
+
+// 处理玩家加入房间
+func joinRoomAsPlayer(userData *common.UserData, agent gate.Agent, teenPattiRoom *room.Room) {
+	// 将玩家加入房间
+	// randomSitPos := getRandomSitPos(teenPattiRoom) // 随机选择一个座位
+
+	teenPattiRoom.Players = append(teenPattiRoom.Players, &room.Player{
+		Id:       userData.Id, // 使用玩家的 UserId
+		Agent:    agent,
+		IsRobot:  false,
+		UserData: userData,
+		SitPos:   4, // 你可以根据座位分配逻辑来设置
+		// SitPos:    randomSitPos,
+		IsPacked:  false,
+		IsSeen:    false,
+		IsShow:    false,
+		IsDealer:  false,
+		CanShow:   false,
+		HandCards: []*msg.ReqCard{},
+	})
+	userData.Room_id = teenPattiRoom.Id
+	user.SetUserInfo(userData)
+	log.Debug("Player joined room")
+
+	// 发送房间信息给所有玩家
+	// sendCurrentRoundInfo(teenPattiRoom)
+}
+
+func getRandomSitPos(teenPattiRoom *room.Room) int32 {
+	// 获取空闲的座位
+	availableSitPos := getAvailableSitPos(teenPattiRoom)
+	// 如果没有空座位,不能再加入机器人
+	if len(availableSitPos) == 0 {
+		log.Debug("No available seats for new robot in room %v", teenPattiRoom.Id)
+		return 999
+	}
+	// 初始化随机数生成器
+	random := rand.New(rand.NewSource(time.Now().UnixNano()))
+	// 随机选择一个座位
+	randomIndex := random.Intn(len(availableSitPos))
+	selectedSitPos := availableSitPos[randomIndex]
+	return selectedSitPos
+}

+ 20 - 37
src/server/game/teen/event.go

@@ -1,10 +1,10 @@
 package teen
 
 import (
+	"server/common"
 	"server/events"
 	"server/game/room"
 	"server/msg"
-	"server/user"
 
 	"github.com/name5566/leaf/log"
 )
@@ -17,45 +17,28 @@ func handleEvents() {
 				GameId:            "teen_patti",
 				TeenPattiRoomList: convertToMsgRoomList(GameConfig.RoomList),
 			})
-
-		case events.EventJoinTeenPattiRoom:
-			log.Debug("EventJoinTeenPattiRoom")
-			m := event.Data.(*msg.ResJoinRoom)
-			userData := event.Agent.UserData().(*user.UserData)
-			if userData.Teen_Patti_Room == nil {
-				userData.Teen_Patti_Room = &room.Room{
-					Id:      "teen_patti",
-					Players: make([]*room.Player, 0),
-					Status:  room.RoomStatusWaiting,
-				}
-			}
-			userData.Teen_Patti_Room.Players = append(userData.Teen_Patti_Room.Players, &room.Player{
-				Id:        m.UserId,
-				Agent:     event.Agent,
-				IsRobot:   false,
-				UserData:  userData,
-				SitPos:    SelfSitPos,
-				IsPacked:  false,
-				IsSeen:    false,
-				IsShow:    false,
-				IsDealer:  false,
-				CanShow:   false,
-				HandCards: []*msg.ReqCard{},
-			})
-			userData.Teen_Patti_Room.GameRound = &room.GameRound{
-				Rounds:   make([]*msg.ReqRound, 0),
-				TotalBet: 0,
+		case events.EventLeaveTeenPattiRoom:
+			m := event.Data.(*msg.ReqLeaveRoom)
+			room, exists := roomStorage[m.RoomId]
+			if !exists {
+				log.Debug("Room %s not found", m.RoomId)
+				return
 			}
-			go startGame(m.UserId, m.RoomId, event.Agent, userData.Teen_Patti_Room)
+			LeaveTeenPattiRoom(room, m.UserId, "1")
+		case events.EventJoinTeenPattiRoom:
+			handleJoinTeenPattiRoom(event)
+
 		case events.EventTeenPattiPlayerOptAction:
 			// m := event.Data.(*msg.ResPlayerOptAction)
 			log.Debug("EventTeenPattiPlayerOptAction")
 			m := event.Data.(*msg.ResPlayerOptAction)
-			userData := event.Agent.UserData().(*user.UserData)
-			if userData.Teen_Patti_Room != nil {
-				recvPlayerOptAction(userData.Teen_Patti_Room, m.SitPos, m.PlayerOpt)
-			} else {
-				log.Error("userData.Teen_Patti_Room is nil")
+			// 查询房间信息
+			TeenPattiRoom := getRoomInfo(m.RoomId)
+			if TeenPattiRoom != nil {
+				//说明正常获取
+				recvPlayerOptAction(TeenPattiRoom, m.SitPos, m.PlayerOpt)
+			} else { //说明有错误
+				log.Error("Teen_Patti_Room is nil")
 			}
 		}
 	}
@@ -63,12 +46,12 @@ func handleEvents() {
 
 // 获取玩家的昵称
 func GetNickname(p *room.Player) string {
-	userData := p.UserData.(*user.UserData)
+	userData := p.UserData.(*common.UserData)
 	return userData.Nickname
 }
 
 // 获取玩家的头像
 func GetAvatar(p *room.Player) string {
-	userData := p.UserData.(*user.UserData)
+	userData := p.UserData.(*common.UserData)
 	return userData.Head_Image
 }

+ 429 - 10
src/server/game/teen/opt.go

@@ -5,6 +5,8 @@ import (
 	"server/game"
 	"server/game/room"
 	"server/msg"
+	"server/user"
+	"strconv"
 	"time"
 
 	"github.com/name5566/leaf/log"
@@ -28,38 +30,150 @@ func seen(room *room.Room, sitPos int32) {
 		PlayerOpt:   &msg.PlayerOpt{OptType: msg.PlayerOptType_OPT_SEEN, SeenCards: convertToMsgCardList(seenCards)},
 		UserId:      room.GetPlayerBySitPos(sitPos).GetUserId(),
 	})
+	log.Debug("玩家看破sitPos", sitPos)
+	log.Debug("玩家看破", seenCards)
+
 }
 
 // 弃牌
 func packed(room *room.Room, sitPos int32) {
 	player := room.GetPlayerBySitPos(sitPos)
 	player.IsPacked = true
+	SendRoundMsgToAll(room, &msg.ReqRound{
+		Round:       int32(room.Round),
+		RoundSitPos: int32(sitPos),
+		PlayerOpt:   &msg.PlayerOpt{OptType: msg.PlayerOptType_OPT_PACKED},
+		UserId:      room.GetPlayerBySitPos(sitPos).GetUserId(),
+	})
 	if isOnlyOne, sp := room.IsOnlyOneNotPacked(); isOnlyOne {
+
 		// 如果所有玩家都弃牌,判定当前座位玩家获胜
 		room.PlayerWin(sp)
+
+		winPlayer := room.GetPlayerBySitPos(sp)
+		// 结算
+		if winPlayer.IsRobot {
+			// 机器人 也要加减分.暂时先放
+		} else {
+			user.AddUserPoint(room.GameRound.TotalBet, winPlayer.Id)
+			UserInfo := user.GetUserInfoById(winPlayer.Id)
+			// 同步用户信息
+			SendUserInfoMsg(room, &msg.ReqUserInfo{
+				UserId: winPlayer.Id,
+				Points: int32(UserInfo.Points),
+			})
+		}
+
 		sendResult(room, sp)
+
+		time.Sleep(5 * time.Second)
+		log.Debug("弃牌的位置startNextRound: %d")
+		startNextRound(room)
+	} else {
+		log.Debug("弃牌的位置: %d", room.RoundSitPos)
+		room.SetNextRound()
+		checkRoomStatus(room)
 	}
+}
+
+// 跟注
+func chaal(room *room.Room, sitPos int32, betAmount int32) {
+
+	// 判断金额是否正常
+	if betAmount > room.GameRound.ResBet.MaxCheelBet || betAmount < room.GameRound.ResBet.MinCheelBet {
+		log.Debug("Bet amount %d is out of valid range. Min: %d, Max: %d", betAmount, room.GameRound.ResBet.MinCheelBet, room.GameRound.ResBet.MaxCheelBet)
+		betAmount = room.GameRound.ResBet.MaxCheelBet
+	}
+	UserId := room.GetPlayerBySitPos(sitPos).GetUserId()
+	if room.GetPlayerBySitPos(sitPos).IsRobot {
+		// 机器人
+	} else {
+		// 判断积分是否足够
+
+		UserInfo := user.GetUserInfoById(UserId)
+		if UserInfo.Points < betAmount {
+			log.Debug("chaal User %s has insufficient points. Current points: %d", UserId, UserInfo.Points)
+			// 告诉他充值
+			room.GetPlayerBySitPos(sitPos).Agent.WriteMsg(&msg.ReqInsufficientPoints{
+				UserId:          UserInfo.Id,
+				RequiredAmount:  int32(betAmount),
+				AvailableAmount: UserInfo.Points,
+				GameId:          "teen_patti",
+			})
+		}
+		// 扣除用户金币
+		user.DecreaseUserPoint(betAmount, UserId)
+		// 同步用户金币
+		SendUserInfoMsg(room, &msg.ReqUserInfo{
+			UserId: UserId,
+			Points: int32(UserInfo.Points),
+		})
+	}
+	// 用户的金额是否大于下注金额
 	room.SetNextRound()
-	log.Debug("弃牌的位置: %d", room.RoundSitPos)
+	room.AddBet(betAmount)
+	//
+	// 动态修改下注金额
+	room.UpdateResBet(betAmount, getRoomChaalLimmit(room.ConfId))
+
 	SendRoundMsgToAll(room, &msg.ReqRound{
 		Round:       int32(room.Round),
 		RoundSitPos: int32(sitPos),
-		PlayerOpt:   &msg.PlayerOpt{OptType: msg.PlayerOptType_OPT_PACKED},
-		UserId:      room.GetPlayerBySitPos(sitPos).GetUserId(),
+		PlayerOpt:   &msg.PlayerOpt{OptType: msg.PlayerOptType_OPT_CHAAL},
+		UserId:      UserId,
 	})
+
 	checkRoomStatus(room)
+	game.Module.Skeleton.AfterFunc(time.Microsecond*500, func() {
+		sendRoomBet(room)
+	})
 }
 
-// 跟注
-func chaal(room *room.Room, sitPos int32, betAmount int32) {
+// blind跟注
+func blind(room *room.Room, sitPos int32, betAmount int32) {
+	// 判断金额是否正常
+	if betAmount > room.GameRound.ResBet.MaxBlindBet || betAmount < room.GameRound.ResBet.MinBlindBet {
+		log.Debug("Bet amount %d is out of valid range. MinBlindBet: %d, MaxBlindBet: %d", betAmount, room.GameRound.ResBet.MinBlindBet, room.GameRound.ResBet.MaxBlindBet)
+
+		// 金额不对 直接弃牌
+		betAmount = room.GameRound.ResBet.MaxBlindBet
+	}
+	UserId := room.GetPlayerBySitPos(sitPos).GetUserId()
+	UserInfo := user.GetUserInfoById(UserId)
 	room.SetNextRound()
 	room.AddBet(betAmount)
+
+	// 动态修改下注金额
+	room.UpdateResBet(betAmount, getRoomChaalLimmit(room.ConfId))
+
 	SendRoundMsgToAll(room, &msg.ReqRound{
 		Round:       int32(room.Round),
 		RoundSitPos: int32(sitPos),
-		PlayerOpt:   &msg.PlayerOpt{OptType: msg.PlayerOptType_OPT_CHAAL},
-		UserId:      room.GetPlayerBySitPos(sitPos).GetUserId(),
+		PlayerOpt:   &msg.PlayerOpt{OptType: msg.PlayerOptType_OPT_BLIND},
+		UserId:      UserId,
 	})
+	if room.GetPlayerBySitPos(sitPos).IsRobot {
+		// 机器人
+	} else {
+		if UserInfo.Points < betAmount {
+			log.Debug("blind User %s has insufficient points. Current points: %d", UserId, UserInfo.Points)
+			// 告诉他充值
+			room.GetPlayerBySitPos(sitPos).Agent.WriteMsg(&msg.ReqInsufficientPoints{
+				UserId:          UserInfo.Id,
+				RequiredAmount:  int32(betAmount),
+				AvailableAmount: UserInfo.Points,
+				GameId:          "teen_patti",
+			})
+		}
+		// 扣除用户金币
+		user.DecreaseUserPoint(betAmount, UserId)
+		// 同步用户金币
+		SendUserInfoMsg(room, &msg.ReqUserInfo{
+			UserId: UserId,
+			Points: int32(UserInfo.Points),
+		})
+	}
+
 	checkRoomStatus(room)
 	game.Module.Skeleton.AfterFunc(time.Microsecond*500, func() {
 		sendRoomBet(room)
@@ -69,24 +183,296 @@ func chaal(room *room.Room, sitPos int32, betAmount int32) {
 // 底注
 func ante(room *room.Room, sitPos int32, betAmount int32) {
 	room.AddBet(betAmount)
+	UserId := room.GetPlayerBySitPos(sitPos).GetUserId()
 	SendRoundMsgToAll(room, &msg.ReqRound{
 		Round:       int32(room.Round),
 		RoundSitPos: int32(sitPos),
 		PlayerOpt:   &msg.PlayerOpt{OptType: msg.PlayerOptType_OPT_ANTE},
-		UserId:      room.GetPlayerBySitPos(sitPos).GetUserId(),
+		UserId:      UserId,
 	})
+	if room.GetPlayerBySitPos(sitPos).IsRobot {
+		// 机器人
+	} else {
+		// 扣除用户金币
+		user.DecreaseUserPoint(betAmount, UserId)
+		// 同步用户金币
+		UserInfo := user.GetUserInfoById(UserId)
+		SendUserInfoMsg(room, &msg.ReqUserInfo{
+			UserId: UserId,
+			Points: int32(UserInfo.Points),
+		})
+	}
 	game.Module.Skeleton.AfterFunc(time.Microsecond*500, func() {
 		sendRoomBet(room)
 	})
 }
 
+func checkSiteShow(r *room.Room, sitPos int32) bool {
+	// 条件1: 当前玩家是否弃牌
+	currentPlayer := r.GetPlayerBySitPos(sitPos)
+	log.Debug("现在的座位号1", sitPos)
+	if currentPlayer == nil {
+		log.Debug("currentPlayer Player with SitPos %d not found.", sitPos)
+		return false
+	}
+
+	if currentPlayer.IsPacked {
+		log.Debug("Player %s has packed, cannot show cards.", currentPlayer.Id)
+		return false
+	}
+
+	// 条件2: 剩余有效玩家大于等于 3 人
+	activePlayers := 0
+	for _, player := range r.Players {
+		if player != nil && !player.IsPacked { // 只计算没有弃牌的玩家
+			activePlayers++
+		}
+	}
+
+	if activePlayers < 3 {
+		log.Debug("Not enough active players (less than 3). Cannot show cards.")
+		return false
+	}
+	// 条件3. 上一位玩家是否看牌
+	// 获取上一个玩家
+	previousSitPos := getRoomSitPos(r, sitPos, -1)
+	log.Debug("上一位玩家的座位号checkSiteShow", previousSitPos)
+
+	previousPlayer := r.GetPlayerBySitPos(previousSitPos)
+	if previousPlayer == nil {
+		log.Debug("previousPlayer1 Player with SitPos %d not found.", previousSitPos)
+		return false
+	}
+
+	// 判断当前玩家和目标玩家是否都已看牌
+	if !previousPlayer.IsSeen || !currentPlayer.IsSeen {
+		log.Debug("Either player %s or previous player %s has not seen their cards.", currentPlayer.Id, previousPlayer.Id)
+		return false
+	}
+
+	// 如果满足以上条件,则返回 true
+	log.Debug("Player ture %s can show cards.", currentPlayer.Id)
+	return true
+}
+func site_show(r *room.Room, sitPos int32, betAmount int32) {
+	// 检查是否符合条件
+	if !checkSiteShow(r, sitPos) {
+		// 	return
+	}
+	// notifyPlayerAction(r, int32(sitPos), msg.PlayerOptType_OPT_CHAAL)
+
+	ante(r, sitPos, betAmount)
+	// 扣除用户金币
+	UserId := r.GetPlayerBySitPos(sitPos).GetUserId()
+	if r.GetPlayerBySitPos(sitPos).IsRobot {
+		updateRobotPoints(UserId, betAmount, false)
+		// 机器人
+	} else {
+		// 扣除用户金币
+		user.DecreaseUserPoint(betAmount, UserId)
+		// 同步用户金币
+		UserInfo := user.GetUserInfoById(UserId)
+		SendUserInfoMsg(r, &msg.ReqUserInfo{
+			UserId: UserId,
+			Points: int32(UserInfo.Points),
+		})
+	}
+
+	UserInfo := user.GetUserInfoById(UserId)
+	// // 同步用户信息
+	SendUserInfoMsg(r, &msg.ReqUserInfo{
+		UserId: UserId,
+		Points: int32(UserInfo.Points),
+		RoomId: r.Id,
+	})
+	// 获取上一个玩家
+	previousSitPos := getRoomSitPos(r, sitPos, -1)
+
+	log.Debug("发起比牌的玩家座位号", sitPos)
+	log.Debug("接受比牌选项玩家的座位号", previousSitPos)
+
+	SendRoundMsgToAll(r, &msg.ReqRound{
+		Round:       int32(r.Round),
+		RoundSitPos: int32(sitPos),
+		PlayerOpt:   &msg.PlayerOpt{OptType: msg.PlayerOptType_OPT_SITESHOW},
+		UserId:      r.GetPlayerBySitPos(sitPos).Id,
+	})
+
+	notifyPlayerAction(r, int32(previousSitPos), msg.PlayerOptType_OPT_SELECTSITESHOW)
+
+}
+
+// 不同意,直接通知下一位玩家操作
+func RefuseSiteShow(r *room.Room, sitPos int32) {
+
+	// // 不同意
+	SendRoundMsgToAll(r, &msg.ReqRound{
+		Round:       int32(r.Round),
+		RoundSitPos: int32(sitPos),
+		PlayerOpt:   &msg.PlayerOpt{OptType: msg.PlayerOptType_OPT_REFUSESITESHOW},
+		UserId:      r.GetPlayerBySitPos(sitPos).GetUserId(),
+	})
+	r.SetNextRound()
+	checkRoomStatus(r)
+}
+
+// 如果同意,处理比牌请求
+func AgreeSiteShow(r *room.Room, sitPos int32) {
+	// // 检查是否符合条件
+	// if !checkSiteShow(r, sitPos) {
+	// 	log.Debug("Player %d cannot show cards, conditions not met.", sitPos)
+	// 	return
+	// }
+	// 获取比牌双方: 当前玩家和下家
+	currentPlayer := r.GetPlayerBySitPos(sitPos)
+	if currentPlayer == nil {
+		log.Debug("Player with SitPos %d not found.", sitPos)
+		return
+	}
+
+	// 获取下家座位
+	previousSitPos := getRoomSitPos(r, sitPos, 1)
+	previousPlayer := r.GetPlayerBySitPos(previousSitPos)
+	if previousPlayer == nil {
+		log.Debug("Next player with SitPos %d not found.", previousPlayer)
+		return
+	}
+
+	// 比较玩家的手牌
+	players := []*room.Player{currentPlayer, previousPlayer}
+	winPlayer := compareCards(players) // 假设 compareCards 函数已经比较手牌并返回胜者
+
+	if winPlayer != nil {
+		// 胜者继续牌局
+		// 输的玩家弃牌
+		var loserPlayer *room.Player
+		if winPlayer == currentPlayer {
+			loserPlayer = previousPlayer // 当前玩家赢,上一位玩家是输家
+		} else {
+			loserPlayer = currentPlayer // 上一位玩家赢,当前玩家是输家
+		}
+		// 输的玩家弃牌
+		loserPlayer.IsPacked = true
+		log.Debug("Player %s has packed, they lose the showdown.", loserPlayer.Id)
+		// 发送比牌结果
+		SendRoundMsgToAll(r, &msg.ReqRound{
+			Round:       int32(r.Round),
+			RoundSitPos: int32(currentPlayer.SitPos),
+			PlayerOpt:   &msg.PlayerOpt{OptType: msg.PlayerOptType_OPT_AGREESITESHOW},
+			UserId:      currentPlayer.Id,
+		})
+		// 赢家和输家互相可见手牌
+		for _, player := range []*room.Player{currentPlayer, previousPlayer} {
+			if player.Agent == nil {
+				continue
+			}
+			var seenCards []*msg.ReqCard
+			var opponent *room.Player
+
+			if player.Id == currentPlayer.Id {
+				opponent = previousPlayer
+				seenCards = convertToMsgCardList(previousPlayer.HandCards) // 赢家看到输家的手牌
+			} else {
+				opponent = currentPlayer
+				seenCards = convertToMsgCardList(currentPlayer.HandCards) // 输家看到赢家的手牌
+			}
+			log.Debug("AgreeSiteShow seenCards", seenCards)
+			// 发送消息给当前玩家,告诉 TA 他的对手是谁,并展示对手的手牌
+			player.Agent.WriteMsg(&msg.SiteShowResult{
+				Round:       int32(r.Round),
+				RoundSitPos: int32(opponent.SitPos), // **座位号改为对手的**
+				SeenCards:   seenCards,              // 只展示对手的手牌
+				UserId:      opponent.Id,            // **UserId 也是对手的**
+			})
+		}
+		packed(r, loserPlayer.SitPos)
+
+	} else {
+		log.Error("到这就错误了SiteShowResult")
+	}
+
+	// 处理结果
+	// r.SetNextRound()
+	// checkRoomStatus(r)
+}
+
+// 检查是否可以亮牌
+func checkShow(r *room.Room, sitPos int32) bool {
+	// 当前玩家是否弃牌
+	currentPlayer := r.GetPlayerBySitPos(sitPos)
+	if currentPlayer == nil {
+		log.Debug("Player with SitPos %d not found.", sitPos)
+		return false
+	}
+
+	// 如果玩家已弃牌,不能亮牌
+	if currentPlayer.IsPacked {
+		log.Debug("Player %s has packed, cannot show cards.", currentPlayer.Id)
+		return false
+	}
+
+	// 判断剩余有效玩家人数
+	activePlayers := 0
+	for _, player := range r.Players {
+		if player != nil && !player.IsPacked { // 只计算没有弃牌的玩家
+			activePlayers++
+		}
+	}
+
+	// 如果剩余有效玩家是两名,则允许亮牌
+	if activePlayers == 2 {
+		log.Debug("There are two players left, showing cards is allowed.")
+		return true
+	}
+
+	log.Debug("There are not two players left, cannot show cards.")
+	return false
+}
+
 // 亮牌
-func show(r *room.Room) {
+func show(r *room.Room, sitPos int32) {
+	// 判断金额是否正常
+
+	betAmount := r.GameRound.ResBet.MinCheelBet
+	ante(r, sitPos, betAmount)
+	// 检查是否可以亮牌
+	if !checkShow(r, sitPos) {
+		log.Debug("Player %d cannot show cards, conditions not met.", sitPos)
+		return
+	}
+	// 扣除用户金币
+	UserId := r.GetPlayerBySitPos(sitPos).GetUserId()
+	UserInfo := user.GetUserInfoById(UserId)
+	if r.GetPlayerBySitPos(sitPos).IsRobot {
+		updateRobotPoints(UserId, betAmount, false)
+		// 机器人
+	} else {
+		if UserInfo.Points < betAmount && UserInfo.Points > 0 {
+			betAmount = UserInfo.Points
+		}
+		// 扣除用户金币
+		user.DecreaseUserPoint(betAmount, UserId)
+		// 同步用户金币
+		SendUserInfoMsg(r, &msg.ReqUserInfo{
+			UserId: UserId,
+			Points: int32(UserInfo.Points),
+		})
+	}
+
+	// 同步用户信息
+	SendUserInfoMsg(r, &msg.ReqUserInfo{
+		UserId: UserId,
+		Points: int32(UserInfo.Points),
+	})
+	// 亮牌的玩家
 	players := r.Players
 	temp := make([]*room.Player, 0)
+
+	// 只处理没有弃牌的玩家
 	for _, player := range players {
 		if !player.IsPacked {
 			temp = append(temp, player)
+			// 向所有玩家发送亮牌的消息
 			SendRoundMsgToAll(r, &msg.ReqRound{
 				Round:       int32(r.Round),
 				RoundSitPos: int32(player.SitPos),
@@ -96,17 +482,37 @@ func show(r *room.Room) {
 		}
 	}
 
+	// 比较玩家的手牌,决定胜利者
 	winPlayer := compareCards(temp)
 	if winPlayer != nil {
 		r.PlayerWin(winPlayer.SitPos)
 		sendResult(r, winPlayer.SitPos)
+		// 加分
+
+		log.Debug("SHOWUserId", UserId)
+		// 胜的玩家是否是机器人
+		if winPlayer.IsRobot {
+			// 机器人暂时不处理
+		} else {
+			user.AddUserPoint(r.GameRound.TotalBet, winPlayer.Id)
+
+			UserInfo := user.GetUserInfoById(winPlayer.Id)
+			// 同步用户信息
+			SendUserInfoMsg(r, &msg.ReqUserInfo{
+				UserId: winPlayer.Id,
+				Points: int32(UserInfo.Points),
+			})
+		}
 	} else {
 		log.Error("所有玩家都弃牌了")
 	}
 
+	// 等待一定时间后开始下一回合
+	time.Sleep(5 * time.Second)
+	startNextRound(r)
 }
 
-// 取消操作超时
+// 超时取消操作
 func cancelOptTimeout(player *room.Player, sitPos int32) {
 	eventID := fmt.Sprintf("player_%s_opt_%s", player.Id, sitPos)
 	timerMgr.RemoveEvent(eventID)
@@ -123,3 +529,16 @@ func checkRoomStatus(r *room.Room) {
 	}
 
 }
+func getRoomChaalLimmit(ConfId string) int32 {
+	for _, betInfo := range GameConfig.RoomList {
+		if betInfo.ConfId == ConfId {
+			// 获取房间的最低买入金额
+			chaalLimmit, err := strconv.Atoi(betInfo.ChaalLimmit)
+			if err != nil {
+				log.Error("Invalid chaalLimmit value: %v", chaalLimmit)
+			}
+			return int32(chaalLimmit)
+		}
+	}
+	return 0
+}

+ 150 - 49
src/server/game/teen/robot.go

@@ -2,12 +2,14 @@ package teen
 
 import (
 	"encoding/json"
+	"fmt"
 	"math/rand"
 	"os"
-	"server/game"
+	"server/common"
+	redismgr "server/db/redis"
 	"server/game/room"
 	"server/msg"
-	"sync"
+	"slices"
 	"time"
 
 	"github.com/name5566/leaf/log"
@@ -17,6 +19,26 @@ type RobotNames struct {
 	Names []string `json:"name"`
 }
 
+// 用于生成 10 位随机 ID 的函数
+func generateRandomID() string {
+	random := rand.New(rand.NewSource(time.Now().UnixNano()))
+	randomID := random.Intn(10000000000)  // 生成一个 10 位的随机整数
+	return fmt.Sprintf("%010d", randomID) // 返回格式化的 10 位 ID,不足的补零
+}
+
+// 创建人机的UserData
+func createRobotUserData() *common.UserData {
+	return &common.UserData{
+		Id:     generateRandomID(),
+		Status: 1,
+		// Teen_Patti_Room: nil,
+		Head_Image: randomIndianHeadImage(),
+		Nickname:   randomIndianName(),
+		Room_id:    "0",
+		Points:     int32(1000),
+	}
+}
+
 // 随即印度人名字
 func randomIndianName() string {
 	data, err := os.ReadFile("/home/zjh/teen_patti/bin/gamedata/robot_name.json")
@@ -50,66 +72,145 @@ func randomIndianHeadImage() string {
 	return images.HeadImages[rand.Intn(len(images.HeadImages))]
 }
 
-// 人机操作
-func robotOpt(r *room.Room, sitPos int32, optType msg.PlayerOptType) {
-	if optType == msg.PlayerOptType_OPT_SELECT {
-		go robotSelect(r, sitPos)
-	}
-	// switch optType {
-	// case msg.PlayerOptType_OPT_SELECT:
-	// 	robotSelect(r, sitPos)
-	// case msg.PlayerOptType_OPT_CHAAL:
-	// 	robotChaal(r, sitPos)
-	// case msg.PlayerOptType_OPT_SHOW:
-	// 	robotShow(r, sitPos)
-	// case msg.PlayerOptType_OPT_PACKED:
-	// 	robotPacked(r, sitPos)
-	// }
-}
+// 获取空闲机器人或创建新机器人
+func getOrCreateTeenPattiRobot(teenPattiRoom *room.Room) (*common.UserData, error) {
+	robotUserData, err := getTeenPattiRobot(teenPattiRoom)
+	if err != nil {
+		log.Debug("Error getting robot info: %v", err)
+		return nil, err
+	}
 
-func robotSelect(r *room.Room, sitPos int32) {
-	log.Debug("robotSelect, sitPos: %d", sitPos)
-	wg := sync.WaitGroup{}
-	wg.Add(1)
-	go func() {
-		time.Sleep(time.Second * 1)
-		randTime := rand.Intn(2) + 1
-		if randTime == 1 {
-			isSeen := r.GetPlayerBySitPos(sitPos).IsSeen
-			log.Debug("isSeen: %v", isSeen)
-			if !isSeen {
-				robotSeen(r, sitPos)
-			}
+	if robotUserData == nil {
+		// 如果没有空闲机器人,创建新的机器人
+		log.Debug("No free robot available. Creating a new robot.")
+		robotUserData = createRobotUserData()
+		robotUserData.Room_id = teenPattiRoom.Id
+		err := redismgr.SaveRobotInfoToRedis(robotUserData.Id, robotUserData)
+		if err != nil {
+			log.Debug("Error saving new robot to Redis: %v", err)
+			return nil, err
 		}
-		wg.Done()
-	}()
+		log.Debug("Successfully created and saved robot info: %+v", robotUserData)
+	}
 
-	wg.Wait()
-	log.Debug("robotChaal, sitPos: %d", sitPos)
-	robotChaal(r, sitPos)
+	return robotUserData, nil
 }
 
-func robotPack(r *room.Room, sitPos int32) {
+// 为房间加入新机器人
+func addNewRobot(teenPattiRoom *room.Room, availableSitPos []int32) {
+	// 初始化随机数生成器
+	random := rand.New(rand.NewSource(time.Now().UnixNano()))
 
-}
+	// 随机选择一个座位
+	randomIndex := random.Intn(len(availableSitPos))
+	selectedSitPos := availableSitPos[randomIndex]
+	log.Debug("Selected seat position: %v", selectedSitPos)
+
+	// 获取或创建新的机器人
+	robotUserData, err := getOrCreateTeenPattiRobot(teenPattiRoom)
+	if err != nil {
+		log.Debug("Error getting or creating robot: %v", err)
+		return
+	}
 
-func robotChaal(r *room.Room, sitPos int32) {
-	//获取1-5的随机数
-	randTime := rand.Intn(5) + 1
-	chaalAmount := 200
-	game.Module.Skeleton.AfterFunc(time.Second*time.Duration(randTime), func() {
-		recvPlayerOptAction(r, sitPos, &msg.PlayerOpt{OptType: msg.PlayerOptType_OPT_CHAAL, BetAmount: int32(chaalAmount)})
-	})
+	// 创建新的 Player 对象并加入房间
+	newRobot := &room.Player{
+		Id:        robotUserData.Id,      // 使用生成的机器人ID
+		Agent:     nil,                   // 代理为空
+		IsRobot:   true,                  // 标记为机器人
+		UserData:  robotUserData,         // 设置机器人用户数据
+		SitPos:    int32(selectedSitPos), // 分配随机座位
+		HandCards: []*msg.ReqCard{},      // 初始手牌为空
+		IsPacked:  false,
+		IsSeen:    false,
+		IsShow:    false,
+		IsDealer:  false,
+		CanShow:   false,
+	}
+	// 将新机器人加入房间
+	teenPattiRoom.Players = append(teenPattiRoom.Players, newRobot)
+	log.Debug("Added new robot to room %v at position %v", teenPattiRoom.Id, selectedSitPos)
 }
 
-func robotShow(r *room.Room, sitPos int32) {
+// 随机去除一个机器人,排除座位号为 4
+func removeRandomRobot(teenPattiRoom *room.Room) {
+	if len(teenPattiRoom.Players) > 1 { // 至少有 2 个玩家,才能删除一个机器人
+		var randomIndex int
+		var removedRobotID string
+
+		// 随机选择一个机器人位置,排除座位号为 4 并且确保选择的是机器人
+		for {
+			randomIndex = rand.Intn(len(teenPattiRoom.Players)) // 随机选择一个玩家位置
+			// 确保选中的是机器人
+			if teenPattiRoom.Players[randomIndex].IsRobot {
+				removedRobotID = teenPattiRoom.Players[randomIndex].Id
+				break
+			}
+		}
+
+		// 删除该位置的机器人
+		if randomIndex < len(teenPattiRoom.Players) {
+			teenPattiRoom.Players = slices.Delete(teenPattiRoom.Players, randomIndex, randomIndex+1)
+			log.Debug("Robot removed from room %v at position %d", teenPattiRoom.Id, randomIndex)
+
+			// 机器人退出房间,重置其 Room_id
+			redismgr.ResetRobotRoomID(removedRobotID)
 
+			// 将机器人重新放回 Redis 空闲队列
+			redismgr.AddRobotToFreeQueue(teenPattiRoom.ConfId, removedRobotID)
+		}
+	}
 }
 
-func robotPacked(r *room.Room, sitPos int32) {
+// 获取空闲机器人的信息
+func getTeenPattiRobot(r *room.Room) (*common.UserData, error) {
+	// 获取空闲机器人的信息
+	robotID, err := redismgr.GetFreeRobot(r.ConfId) // 获取空闲机器人的ID
+	if err != nil {
+		log.Debug("Error getting free robots: %v", err)
+		return nil, err
+	}
+
+	// 如果没有空闲机器人,返回nil
+	if robotID == "0" {
+		log.Debug("No free robots available.")
+		return nil, nil
+	}
 
+	// 获取机器人的详细信息
+	robotInfo, err := redismgr.GetRobotInfoFromRedis(robotID)
+	if err != nil {
+		log.Debug("Error fetching robot info for robotID %s: %v", robotID, err)
+		return nil, err
+	}
+
+	if robotInfo == nil {
+		log.Debug("Robot info for robotID %s is nil", robotID)
+		return nil, fmt.Errorf("robot info is nil for robotID %s", robotID)
+	}
+
+	// 输出机器人的详细信息
+	log.Debug("Robot Info: %+v", robotInfo)
+	// 返回机器人信息
+	return robotInfo, nil
 }
+func updateRobotPoints(robotID string, points int32, isAdd bool) {
+	// 获取机器人当前信息
+	robotInfo, err := redismgr.GetRobotInfoFromRedis(robotID)
+	if err != nil {
+		log.Debug("Robot %s not found in Redis", robotID)
+		return
+	}
+
+	currentPoints := robotInfo.Points
+	// 计算新积分
+	newPoints := int32(currentPoints)
+	if isAdd {
+		newPoints += points
+	} else {
+		newPoints -= points
+	}
 
-func robotSeen(r *room.Room, sitPos int32) {
-	recvPlayerOptAction(r, sitPos, &msg.PlayerOpt{OptType: msg.PlayerOptType_OPT_SEEN})
+	// 更新 Redis
+	redismgr.UpdateRobotPointsInRedis(robotID, newPoints)
 }

+ 310 - 0
src/server/game/teen/robotAction.go

@@ -0,0 +1,310 @@
+package teen
+
+import (
+	"math/rand"
+	"server/game"
+	"server/game/room"
+	"server/msg"
+	"sync"
+	"time"
+
+	"github.com/name5566/leaf/log"
+)
+
+// 人机操作
+func robotOpt(r *room.Room, sitPos int32, optType msg.PlayerOptType) {
+	if optType == msg.PlayerOptType_OPT_SELECT {
+		go robotSelect(r, sitPos)
+	}
+	if optType == msg.PlayerOptType_OPT_SELECTSITESHOW {
+		go robotAcceptSiteShow(r, sitPos)
+	}
+
+}
+
+// 机器人选择操作
+func robotSelect(r *room.Room, sitPos int32) {
+	log.Debug("robotSelect, sitPos: %d", sitPos)
+
+	wg := sync.WaitGroup{}
+	wg.Add(1)
+	go func() {
+		time.Sleep(time.Second * 1)
+		randTime := rand.Intn(2) + 1 // 随机决定操作
+
+		player := r.GetPlayerBySitPos(sitPos)
+
+		// 判断是否看牌
+		if !player.IsSeen {
+			log.Debug("!player.IsSeen: %d", !player.IsSeen)
+
+			// 没有看牌状态
+			// 三分之一概率选择闷牌跟注,四分之三选择看牌
+			if randTime == 4 {
+				log.Debug("robotBlindChaal: %d", !player.IsSeen)
+
+				robotBlindChaal(r, sitPos) // 闷牌跟注
+			} else {
+				log.Debug("robotSeen: %d", !player.IsSeen)
+
+				robotSeen(r, sitPos) // 看牌
+				// 执行看牌后的操作
+				robotPostSeenActions(r, sitPos)
+			}
+		} else {
+			// 已看牌状态
+			robotPostSeenActions(r, sitPos)
+		}
+
+		wg.Done()
+	}()
+	wg.Wait()
+}
+
+// 执行机器人看牌后的操作
+func robotPostSeenActions(r *room.Room, sitPos int32) {
+	log.Debug("robotPostSeenActions, sitPos: %d", sitPos)
+
+	// 获取当前玩家
+	player := r.GetPlayerBySitPos(sitPos)
+	if player == nil {
+		log.Debug("Player %d not found for post-seen actions.", sitPos)
+		return
+	}
+
+	// 判断可执行操作
+	possibleActions := []string{}
+	if isValidForChaal(r, sitPos) {
+		possibleActions = append(possibleActions, "OPT_CHAAL")
+	}
+	if isValidForPacked(r, sitPos) {
+		possibleActions = append(possibleActions, "OPT_PACKED")
+	}
+	if isValidForSiteShow(r, sitPos) {
+		possibleActions = append(possibleActions, "OPT_INIRIATESITESHOW")
+	}
+	if isValidForShow(r, sitPos) {
+		possibleActions = append(possibleActions, "OPT_SHOW")
+	}
+	// 测试比牌
+	// possibleActions = append(possibleActions, "OPT_SITESHOW")
+	previousSitPos := getRoomSitPos(r, sitPos, -1)
+	var previousPlayer *room.Player
+	var i = 0
+	for _, player := range r.Players {
+		if player.SitPos == previousSitPos {
+			previousPlayer = player
+		}
+		if !player.IsPacked {
+			i += 1
+		}
+	}
+	// 随机选择一个可执行操作
+	if len(possibleActions) > 0 {
+		action := possibleActions[rand.Intn(len(possibleActions))]
+		//如果符合比牌的逻辑那么直接
+		// 测试
+		if !previousPlayer.IsRobot && !previousPlayer.IsPacked && i > 2 && previousPlayer.IsSeen {
+			action = "OPT_INIRIATESITESHOW"
+		}
+		if previousPlayer.IsRobot && !previousPlayer.IsPacked && i > 2 && previousPlayer.IsSeen {
+			action = "OPT_INIRIATESITESHOW"
+		}
+		performRobotAction(r, sitPos, action)
+	} else {
+		// 如果没有可执行操作,选择弃牌
+		log.Debug("No valid actions available for robot, choosing PACKED.")
+		robotPacked(r, sitPos)
+	}
+}
+
+// 机器人执行具体操作
+func performRobotAction(r *room.Room, sitPos int32, action string) {
+	switch action {
+	case "OPT_CHAAL":
+		log.Debug("robotChaal, sitPos: %d", sitPos)
+		robotChaal(r, sitPos)
+	case "OPT_PACKED":
+		// log.Debug("robotPacked, sitPos: %d", sitPos)
+		robotPacked(r, sitPos)
+	case "OPT_INIRIATESITESHOW":
+		log.Debug("robotInitiateiteShow, sitPos: %d", sitPos)
+		robotInitiateiteShow(r, sitPos)
+
+	case "OPT_SHOW":
+		log.Debug("robotShow, sitPos: %d", sitPos)
+		robotShow(r, sitPos)
+	default:
+		log.Debug("Invalid action selected for robot.")
+	}
+}
+
+// 判断是否可以跟注
+func isValidForChaal(r *room.Room, sitPos int32) bool {
+	// 判断是否符合跟注条件
+	return true
+}
+
+// 判断是否可以加倍跟注
+func isValidForDoubleChaal(r *room.Room, sitPos int32) bool {
+	// 判断是否符合加倍跟注条件
+	return true
+}
+
+// 判断是否可以弃牌
+func isValidForPacked(r *room.Room, sitPos int32) bool {
+	// 判断是否符合弃牌条件
+	return true
+}
+
+// 判断是否可以发起比牌
+func isValidForSiteShow(r *room.Room, sitPos int32) bool {
+	// 判断是否符合比牌条件
+
+	return checkSiteShow(r, sitPos)
+}
+
+// 判断是否可以亮牌
+func isValidForShow(r *room.Room, sitPos int32) bool {
+	// 判断是否符合亮牌条件
+	return checkShow(r, sitPos)
+}
+
+// 机器人看牌
+func robotSeen(r *room.Room, sitPos int32) {
+	log.Debug("robotSeen, sitPos: %d", sitPos)
+
+	player := r.GetPlayerBySitPos(sitPos)
+	if player == nil {
+		log.Debug("Player %d not found.", sitPos)
+		return
+	}
+
+	// 设置玩家为看牌状态
+	player.IsSeen = true
+	recvPlayerOptAction(r, sitPos, &msg.PlayerOpt{OptType: msg.PlayerOptType_OPT_SEEN})
+	log.Debug("Player %d has seen their cards.", sitPos)
+
+}
+
+// 闷牌跟注
+func robotBlindChaal(r *room.Room, sitPos int32) {
+	log.Debug("robotBlindChaal, sitPos: %d", sitPos)
+	// 实现闷牌跟注的操作
+	log.Debug("robotChaal, sitPos: %d", sitPos)
+
+	// 获取1-5秒的随机时间
+	randTime := rand.Intn(5) + 1
+
+	// 获取房间中的最低和最高下注金额
+	minBet := int(r.GameRound.ResBet.MinBlindBet)
+	maxBet := int(r.GameRound.ResBet.MaxBlindBet)
+
+	// 随机选择最低或最高下注金额
+	randBet := minBet
+	if rand.Intn(2) == 1 {
+		randBet = maxBet // 50% 概率选择最高下注金额
+	}
+
+	game.Module.Skeleton.AfterFunc(time.Second*time.Duration(randTime), func() {
+		recvPlayerOptAction(r, sitPos, &msg.PlayerOpt{
+			OptType:   msg.PlayerOptType_OPT_BLIND,
+			BetAmount: int32(randBet), // 传递随机选择的下注金额
+		})
+	})
+}
+
+// 跟注
+func robotChaal(r *room.Room, sitPos int32) {
+	log.Debug("robotChaal, sitPos: %d", sitPos)
+	// 获取1-5秒的随机时间
+	randTime := rand.Intn(5) + 1
+
+	// 获取房间中的最低和最高下注金额
+	minBet := int(r.GameRound.ResBet.MinCheelBet)
+	maxBet := int(r.GameRound.ResBet.MaxCheelBet)
+
+	// 随机选择最低或最高下注金额
+	randBet := minBet
+	if rand.Intn(2) == 1 {
+		randBet = maxBet // 50% 概率选择最高下注金额
+	}
+
+	game.Module.Skeleton.AfterFunc(time.Second*time.Duration(randTime), func() {
+		recvPlayerOptAction(r, sitPos, &msg.PlayerOpt{
+			OptType:   msg.PlayerOptType_OPT_CHAAL,
+			BetAmount: int32(randBet), // 传递随机选择的下注金额
+		})
+	})
+	// 实现跟注的操作
+}
+
+// 弃牌
+func robotPacked(r *room.Room, sitPos int32) {
+	log.Debug("robotPacked, sitPos: %d", sitPos)
+	//获取1-5的随机数
+	randTime := rand.Intn(5) + 1
+	game.Module.Skeleton.AfterFunc(time.Second*time.Duration(randTime), func() {
+		recvPlayerOptAction(r, sitPos, &msg.PlayerOpt{OptType: msg.PlayerOptType_OPT_PACKED})
+	})
+	// 实现弃牌的操作
+}
+
+// 亮牌
+func robotShow(r *room.Room, sitPos int32) {
+	log.Debug("robotShow, sitPos: %d", sitPos)
+	//获取1-5的随机数
+	randTime := rand.Intn(5) + 1
+	game.Module.Skeleton.AfterFunc(time.Second*time.Duration(randTime), func() {
+		recvPlayerOptAction(r, sitPos, &msg.PlayerOpt{OptType: msg.PlayerOptType_OPT_SHOW})
+	})
+	// 实现亮牌的操作
+}
+
+// 机器人发起SiteShow
+func robotInitiateiteShow(r *room.Room, sitPos int32) {
+	log.Debug("robotSiteShow, sitPos: %d", sitPos)
+	//获取1-5的随机数
+	randTime := rand.Intn(5) + 1
+	game.Module.Skeleton.AfterFunc(time.Second*time.Duration(randTime), func() {
+		recvPlayerOptAction(r, sitPos, &msg.PlayerOpt{OptType: msg.PlayerOptType_OPT_SITESHOW})
+	})
+	// 实现亮牌的操作
+}
+
+// 机器人选择是否同意比牌
+func robotAcceptSiteShow(r *room.Room, sitPos int32) {
+
+	// 假设机器人有50%的几率同意比牌,50%的几率拒绝
+	agreeToShow := rand.Intn(2) == 0 // 0 表示同意比牌,1 表示拒绝比牌
+
+	if agreeToShow {
+		// 机器人同意比牌
+		log.Debug("Robot at SitPos %d agrees to show cards", sitPos)
+		// 这里可以调用比牌的函数
+		robotAgreeSiteShow(r, sitPos)
+		// 比如:startShowCards(r, sitPos)  // 这个函数是示例,实际需要根据你的需求调用比牌逻辑
+	} else {
+		// 机器人拒绝比牌
+		log.Debug("Robot at SitPos %d refuses to show cards", sitPos)
+		// 这里可以调用拒绝比牌的函数
+		robotRefuseSiteShow(r, sitPos)
+		// 比如:refuseShowCards(r, sitPos)  // 这个函数是示例,实际需要根据你的需求调用拒绝比牌的逻辑
+	}
+}
+
+func robotAgreeSiteShow(r *room.Room, sitPos int32) {
+	//获取1-5的随机数
+	randTime := rand.Intn(5) + 1
+	game.Module.Skeleton.AfterFunc(time.Second*time.Duration(randTime), func() {
+		recvPlayerOptAction(r, sitPos, &msg.PlayerOpt{OptType: msg.PlayerOptType_OPT_AGREESITESHOW})
+	})
+}
+func robotRefuseSiteShow(r *room.Room, sitPos int32) {
+	//获取1-5的随机数
+	randTime := rand.Intn(5) + 1
+	game.Module.Skeleton.AfterFunc(time.Second*time.Duration(randTime), func() {
+		recvPlayerOptAction(r, sitPos, &msg.PlayerOpt{OptType: msg.PlayerOptType_OPT_REFUSESITESHOW})
+	})
+
+}

+ 64 - 13
src/server/game/teen/round.go

@@ -17,26 +17,34 @@ var timerMgr = NewTimerManager()
 func startDealCard(r *room.Room) {
 	initCardDeck(r)
 	shuffleCardDeck(r.CardDeck)
+	log.Debug("r.Status: %d", r.Status)
 	if r.Status == room.RoomStatusWaiting {
 		r.Status = room.RoomStatusDealing
 		dealer := r.GetDealerPlayer()
 		r.RoundSitPos = int(dealer.SitPos)
 
 		players := r.GetPlayersBySitPos(dealer.SitPos)
-
 		// 让所有玩家进行下底
 		for _, player := range players {
+			log.Debug("发牌时下底的用户: %d", player.SitPos)
+
 			delay := time.Duration(300 * time.Millisecond)
 			game.Module.Skeleton.AfterFunc(delay, func() {
-				ante(r, player.SitPos, 100)
+
+				// ante(r, player.SitPos, 100)
+				ante(r, player.SitPos, getAnte(r.ConfId))
+
 			})
 		}
 
 		time.Sleep(time.Second * 2)
 
 		//每个玩家从第一张开始发,发完再给每个玩家发第二张牌,发完再给每个玩家发第三张牌,要有序有间隔的体现
+		log.Debug("发牌的房间id: %d", r.Id)
+
 		for index := 0; index < 3; index++ {
 			for i, player := range players {
+				log.Debug("发牌用户: %d", player.SitPos)
 				player.HandCards = getThreeCards(r)
 				player.WaitCard = false
 				// 计算延时:轮次*总延时 + 玩家索引*单次延时
@@ -89,12 +97,30 @@ func notifyPlayerAction(room *room.Room, sitPos int32, optType msg.PlayerOptType
 	players := room.GetPlayersBySitPos(sitPos)
 	for _, p := range players {
 		if p.Agent != nil {
-			p.Agent.WriteMsg(&msg.ReqPlayerAction{SitPos: int32(sitPos), PlayerOpt: &msg.PlayerOpt{OptType: optType}})
+			p.Agent.WriteMsg(&msg.ReqPlayerAction{
+				SitPos:    int32(sitPos),
+				PlayerOpt: &msg.PlayerOpt{OptType: optType},
+				ResBet:    room.GameRound.ResBet,
+			})
 		}
 		if sitPos == p.SitPos && !p.IsRobot {
-			addPlayerOptTimeout(room, player, time.Second*time.Duration(GameConfig.ThinkTime))
+			if optType == msg.PlayerOptType_OPT_SELECTSITESHOW {
+				log.Debug("通知玩家开始操作2: %d", sitPos)
+
+				addPlayerOptSiteShowTimeout(room, player, time.Second*time.Duration(GameConfig.ThinkTime))
+				log.Debug("通知玩家开始操作3: %d", sitPos)
+
+			} else {
+				log.Debug("通知玩家开始操作4: %d", sitPos)
+
+				addPlayerOptTimeout(room, player, time.Second*time.Duration(GameConfig.ThinkTime))
+				log.Debug("通知玩家开始操作5: %d", sitPos)
+
+			}
 		}
 	}
+	log.Debug("通知玩家开始操作6: %d", sitPos)
+
 	// game.Module.Skeleton.AfterFunc(time.Second*time.Duration(GameConfig.ThinkTime), func() {
 
 	// })
@@ -105,7 +131,19 @@ func addPlayerOptTimeout(room *room.Room, player *room.Player, timeout time.Dura
 	eventID := fmt.Sprintf("player_%s_opt_%s", player.Id, player.SitPos)
 	timerMgr.AddEvent(eventID, timeout, func() {
 		// 超时处理逻辑,弃牌
-		packed(room, player.SitPos)
+		// packed(room, player.SitPos)
+		recvPlayerOptAction(room, player.SitPos, &msg.PlayerOpt{OptType: msg.PlayerOptType_OPT_PACKED})
+		// show(room, player.SitPos)
+	})
+}
+
+// 添加玩家操作超时事件
+func addPlayerOptSiteShowTimeout(room *room.Room, player *room.Player, timeout time.Duration) {
+	eventID := fmt.Sprintf("player_%s_opt_%s", player.Id, player.SitPos)
+	timerMgr.AddEvent(eventID, timeout, func() {
+		// 超时处理逻辑,弃牌
+		// packed(room, player.SitPos)
+		recvPlayerOptAction(room, player.SitPos, &msg.PlayerOpt{OptType: msg.PlayerOptType_OPT_REFUSESITESHOW})
 	})
 }
 
@@ -114,7 +152,7 @@ func recvPlayerOptAction(room *room.Room, sitPos int32, opt *msg.PlayerOpt) {
 	player := room.GetPlayerBySitPos(sitPos)
 	optType := opt.OptType
 	sendRoomDynamicMsg(room, fmt.Sprintf("玩家%s选择了%s", GetNickname(player), optType.String()))
-	log.Debug("recvPlayerOptAction, optType:%v", optType)
+	// log.Debug("recvPlayerOptAction, optType:%v", optType)
 	player.LastOpt = &msg.PlayerOpt{OptType: optType}
 	if optType == msg.PlayerOptType_OPT_SEEN { // 看牌
 		seen(room, sitPos)
@@ -122,14 +160,31 @@ func recvPlayerOptAction(room *room.Room, sitPos int32, opt *msg.PlayerOpt) {
 		cancelOptTimeout(player, sitPos)
 		if optType == msg.PlayerOptType_OPT_PACKED { // 弃牌
 			packed(room, sitPos)
+
+		} else if optType == msg.PlayerOptType_OPT_BLIND { // 跟注
+			blind(room, sitPos, opt.BetAmount)
 		} else if optType == msg.PlayerOptType_OPT_CHAAL { // 跟注
+
 			chaal(room, sitPos, opt.BetAmount)
+		} else if optType == msg.PlayerOptType_OPT_SITESHOW { // 发起与上家比牌
+			site_show(room, sitPos, opt.BetAmount)
+		} else if optType == msg.PlayerOptType_OPT_REFUSESITESHOW { // 拒绝与下家比牌
+			go RefuseSiteShow(room, sitPos)
+		} else if optType == msg.PlayerOptType_OPT_AGREESITESHOW { // 同意与下家比牌
+			go AgreeSiteShow(room, sitPos)
 		} else if optType == msg.PlayerOptType_OPT_SHOW { // 亮牌
-			show(room)
+			go show(room, sitPos)
 		}
 	}
 	updateAllPlayerInfo(room)
 }
+func SendUserInfoMsg(room *room.Room, mg *msg.ReqUserInfo) {
+	for _, player := range room.Players {
+		if player.Agent != nil && player.Id == mg.UserId {
+			player.Agent.WriteMsg(mg)
+		}
+	}
+}
 
 // 发送消息给所有玩家
 func SendRoundMsgToAll(room *room.Room, mg *msg.ReqRound) {
@@ -166,10 +221,11 @@ func sendRoomBet(room *room.Room) {
 
 // 发送结果
 func sendResult(r *room.Room, sitPos int32) {
+
 	log.Debug("发送结果: %d", r.GameRound.TotalBet)
 	for _, player := range r.Players {
 		if player.Agent != nil {
-			player.Agent.WriteMsg(&msg.ReqResult{RoundSitPos: sitPos, Result: r.GameRound.TotalBet})
+			player.Agent.WriteMsg(&msg.ReqResult{RoundSitPos: &sitPos, Result: &r.GameRound.TotalBet})
 		}
 	}
 }
@@ -183,8 +239,3 @@ func sendRoomDynamicMsg(r *room.Room, room_msg string) {
 		}
 	}
 }
-
-// 开始下一局游戏
-func startNextRound(r *room.Room) {
-	log.Debug("开始下一局游戏")
-}

+ 99 - 19
src/server/game/teen/sit.go

@@ -1,32 +1,112 @@
 package teen
 
 import (
-	"math/rand"
 	"server/game/room"
+	"sort"
+
+	"github.com/name5566/leaf/log"
 )
 
 // 随机选一个一个玩家当庄家md
-func randomDealer(room *room.Room) {
-	// room.SetDealerPlayer(room.Players[rand.Intn(len(room.Players))])
-	room.SetDealerPlayer(room.GetPlayerBySitPos(SelfSitPos))
+// func randomDealer(room *room.Room) {
+// 	// room.SetDealerPlayer(room.Players[rand.Intn(len(room.Players))])
+// 	room.SetDealerPlayer(room.GetPlayerBySitPos(SelfSitPos))
+// }
+
+// // 一共4个座位,指定需要的座位数量,且不重复的随即返回
+// func randomSitPos(_ *room.Room, num int) []int {
+// 	availablePos := make([]int, 4)
+// 	for i := range availablePos {
+// 		availablePos[i] = i
+// 	}
+
+//		// 随机选择不重复的座位号
+//		sitPos := make([]int, 0, num)
+//		for i := 0; i < num && len(availablePos) > 0; i++ {
+//			// 随机选择一个索引
+//			index := rand.Intn(len(availablePos))
+//			// 将选中的座位号添加到结果中
+//			sitPos = append(sitPos, availablePos[index])
+//			// 从可用座位中移除已选择的座位
+//			availablePos = append(availablePos[:index], availablePos[index+1:]...)
+//		}
+//		return sitPos
+//	}
+//
+// 按座位获取房间的上一位玩家或者下一位玩家
+func getRoomSitPos(r *room.Room, sitPos int32, num int) int32 {
+	// 未弃牌的玩家按座位排序
+	validSitPositions := getSortedValidSitPositions(r)
+	// 当前玩家的索引
+	currentPlayerIndex := -1
+	for i, pos := range validSitPositions {
+		if pos == sitPos {
+			currentPlayerIndex = i
+			break
+		}
+	}
+	previousSitPos := validSitPositions[(currentPlayerIndex+num+len(validSitPositions))%len(validSitPositions)]
+	return previousSitPos
 }
 
-// 一共4个座位,指定需要的座位数量,且不重复的随即返回
-func randomSitPos(_ *room.Room, num int) []int {
-	availablePos := make([]int, 4)
-	for i := range availablePos {
-		availablePos[i] = i
+// 获取当前房间有效玩家的座位,并按座位号排序
+func getSortedValidSitPositions(teenPattiRoom *room.Room) []int32 {
+	validSitPositions := make([]int32, 0)
+
+	// 遍历房间玩家,筛选未弃牌玩家的座位号
+	for _, player := range teenPattiRoom.Players {
+		if !player.IsPacked { // 排除弃牌的玩家
+			validSitPositions = append(validSitPositions, player.SitPos)
+		}
 	}
 
-	// 随机选择不重复的座位号
-	sitPos := make([]int, 0, num)
-	for i := 0; i < num && len(availablePos) > 0; i++ {
-		// 随机选择一个索引
-		index := rand.Intn(len(availablePos))
-		// 将选中的座位号添加到结果中
-		sitPos = append(sitPos, availablePos[index])
-		// 从可用座位中移除已选择的座位
-		availablePos = append(availablePos[:index], availablePos[index+1:]...)
+	// 按照座位号升序排序
+	sort.Slice(validSitPositions, func(i, j int) bool {
+		return validSitPositions[i] < validSitPositions[j]
+	})
+
+	return validSitPositions
+}
+func setNextDealerSitPos(teenPattiRoom *room.Room, currentDealerSitPos int32) {
+	// 获取当前牌局所有有效的玩家坐位(按座位号排序)
+	validSitPositions := getSortedValidSitPositions(teenPattiRoom)
+
+	// 打印当前房间有效的玩家座位信息
+	log.Debug("currentDealerSitPos: %v", currentDealerSitPos)
+
+	log.Debug("Valid sit positions: %v", validSitPositions)
+
+	// 确保至少有 2 名有效玩家,否则无法更换庄家
+	if len(validSitPositions) < 2 {
+		log.Debug("Not enough players to change dealer.")
+		return
+	}
+
+	// 查找当前庄家的索引
+	currentDealerIndex := -1
+	for i, sitPos := range validSitPositions {
+		if sitPos == currentDealerSitPos {
+			currentDealerIndex = i
+			break
+		}
+	}
+
+	// 如果当前庄家不存在(找不到),默认选取座位号最小的玩家
+	if currentDealerIndex == -1 {
+		log.Debug("Current dealer's seat position not found, using first player as dealer.")
+		currentDealerIndex = 0 // 选择第一个有效玩家作为庄家
+	}
+
+	// 获取下一个庄家(按照顺时针方向)
+	nextDealerSitPos := validSitPositions[(currentDealerIndex+1)%len(validSitPositions)]
+
+	// 设置下一个庄家
+	// nextDealer := teenPattiRoom.GetPlayerBySitPos(nextDealerSitPos)
+	nextDealer := teenPattiRoom.GetPlayerBySitPos(4)
+	if nextDealer != nil {
+		nextDealer.IsDealer = true
+		log.Debug("Setting player %v (seat: %d) as dealer", nextDealer.Id, nextDealerSitPos)
+	} else {
+		log.Debug("Next dealer not found.")
 	}
-	return sitPos
 }

+ 209 - 0
src/server/game/teen/startNextRound.go

@@ -0,0 +1,209 @@
+package teen
+
+import (
+	"math/rand"
+	"server/common"
+	"server/game"
+	"server/game/room"
+	"server/msg"
+	"time"
+
+	"github.com/name5566/leaf/log"
+)
+
+// 向每个玩家发送当前回合的房间信息和玩家信息
+func sendCurrentRoundInfo(teenPattiRoom *room.Room) {
+	for _, player := range teenPattiRoom.Players {
+		if player.Agent != nil {
+			nextRoundInfo := &msg.NextRound{
+				Info: &msg.ReqJoinRoom{
+					UserId:   player.Id, // 设置当前玩家的 ID
+					GameId:   "teen_patti",
+					RoomId:   teenPattiRoom.Id,
+					SitPos:   player.SitPos,
+					RoomInfo: buildRoom(teenPattiRoom), // 获取房间信息
+				},
+			}
+
+			// 发送 NextRound 消息
+			player.Agent.WriteMsg(nextRoundInfo)
+			log.Debug("Sent current round info to player %v in room %v", player.Id, teenPattiRoom.Id)
+		}
+	}
+}
+
+// 打印 teenPattiRoom.Players 的详细信息
+func printPlayersInfo(teenPattiRoom *room.Room) {
+	// 打印房间中的每个玩家
+	for i, player := range teenPattiRoom.Players {
+		if player != nil {
+			log.Debug("Player %d: ID=%v, SitPos=%v, IsRobot=%v, Nickname=%v",
+				i, player.Id, player.SitPos, player.IsRobot)
+		} else {
+			log.Debug("Player %d: Empty", i)
+		}
+	}
+}
+
+// 获取房间中所有空闲的座位号
+func getAvailableSitPos(teenPattiRoom *room.Room) []int32 {
+	// 假设一个房间最多有 5 个座位
+	maxSeats := int32(5)
+	availableSitPos := []int32{}
+
+	// 创建一个已占用座位的集合
+	occupiedSeats := make(map[int32]bool)
+
+	// 遍历当前房间的所有玩家
+	for _, player := range teenPattiRoom.Players {
+		if player != nil {
+			occupiedSeats[player.SitPos] = true // 标记已占用的座位
+		}
+	}
+
+	// 检查哪些座位没有被占用
+	for i := int32(0); i < maxSeats; i++ { // 使用 int32 类型
+		if !occupiedSeats[i] {
+			availableSitPos = append(availableSitPos, i) // 如果座位没有被占用,加入空闲座位列表
+		}
+	}
+	// 返回空闲座位的列表
+	return availableSitPos
+}
+
+func removeOfflinePlayers(teenPattiRoom *room.Room) {
+	// 遍历玩家列表,找出离线玩家
+	for _, player := range teenPattiRoom.Players {
+		if !player.IsRobot && player.Agent != nil {
+			userData, ok := player.Agent.UserData().(*common.UserData)
+			if !ok || userData == nil {
+				continue // 避免崩溃
+			}
+
+			// 判断是否离线
+			if userData.Status == 0 {
+				// 直接调用 LeaveTeenPattiRoom 移除玩家
+				LeaveTeenPattiRoom(teenPattiRoom, player.Id, "0")
+			}
+		}
+	}
+}
+
+// 过滤出非机器人的玩家
+func filterNonRobotPlayers(teenPattiRoom *room.Room) []*room.Player {
+	var players []*room.Player
+	for _, player := range teenPattiRoom.Players {
+		if !player.IsRobot {
+			players = append(players, player)
+		}
+	}
+	return players
+}
+
+// 初始化游戏
+func startNextRound(teenPattiRoom *room.Room) {
+	teenPattiRoom.Status = room.RoomStatusWaiting
+	var currentDealerSitPos int32 = -1
+	removeOfflinePlayers(teenPattiRoom)
+	// 计算房间内的非机器人玩家数量
+	nonRobotPlayers := filterNonRobotPlayers(teenPattiRoom)
+	if len(nonRobotPlayers) == 0 {
+		// 没有真人玩家了,解散房间
+		log.Debug("Room %v dissolved due to no players left.", teenPattiRoom.Id)
+		// 执行解散房间的逻辑,比如通知玩家、释放资源等
+		dissolveRoom(teenPattiRoom)
+		return
+	}
+	// 加减机器人
+	adjustRobotCount(teenPattiRoom)
+	// 清理上一回合的玩家状态
+	for _, player := range teenPattiRoom.Players {
+		// 如果该玩家是庄家,记录下座位号
+		if player.IsDealer {
+			currentDealerSitPos = player.SitPos
+		}
+		player.IsPacked = false
+		player.IsSeen = false
+		player.IsShow = false
+		player.IsDealer = false // 清空上一回合的庄家状态
+		player.CanShow = false
+		player.HandCards = []*msg.ReqCard{}
+	}
+	// 增加回合计数
+	teenPattiRoom.Round++
+
+	// 更新回合信息
+	// teenPattiRoom.GameRound.Rounds = append(teenPattiRoom.GameRound.Rounds, &msg.ReqRound{
+	// 	Round: int32(teenPattiRoom.Round),
+	// })
+	// 初始化回合数据
+	// 旧的需要入库吗?
+	teenPattiRoom.GameRound.Rounds = []*msg.ReqRound{}
+
+	teenPattiRoom.GameRound.TotalBet = 0 // 重置总下注
+
+	// 重置下注金额区间
+	confInfo := loadTeemConf(teenPattiRoom.ConfId)
+
+	teenPattiRoom.GameRound.ResBet = confInfo
+
+	// 更新座位状态,假设 SelfSitPos 和 RoundSitPos 已正确设置
+	teenPattiRoom.RoundSitPos = 0 // 开始新回合,座位重置
+
+	// 设置新的庄家
+	setNextDealerSitPos(teenPattiRoom, currentDealerSitPos)
+	// 开始下一回合
+	log.Debug("Starting round %d for room %s", teenPattiRoom.Round, teenPattiRoom.Id)
+
+	// 向每个玩家发送当前回合的玩家信息
+	sendCurrentRoundInfo(teenPattiRoom)
+	// room.SaveRoomToRedis(teenPattiRoom)
+	// 2秒后开始发牌
+	game.Module.Skeleton.AfterFunc(time.Second*2, func() {
+		log.Debug("---------------------------------------------------------------------------------------")
+		log.Debug("startDealCard(teenPattiRoom) %v in room %v")
+		printPlayersInfo(teenPattiRoom)
+		go startDealCard(teenPattiRoom) // 继续发牌
+	})
+}
+
+// 调整房间中的机器人数量
+func adjustRobotCount(teenPattiRoom *room.Room) {
+
+	// 初始化随机数生成器
+	random := rand.New(rand.NewSource(time.Now().UnixNano()))
+
+	// 随机加减人数,确保人数在2到5之间
+	currentPlayerCount := len(teenPattiRoom.Players)
+	newPlayerCount := currentPlayerCount
+
+	// 生成随机数来决定是否加人或减人
+	if random.Intn(2) == 0 && newPlayerCount < 5 { // 50%几率加一个玩家,且最多不超过5人
+		newPlayerCount++
+	} else if random.Intn(3) == 0 && newPlayerCount > 2 { // 如果有更多于2个玩家,且随机选择去除玩家
+		newPlayerCount--
+	}
+	// 打印 currentPlayerCount 和 newPlayerCount
+	log.Debug("currentPlayerCount: %v", currentPlayerCount)
+	log.Debug("newPlayerCount: %v", newPlayerCount)
+	// 如果人数发生变化,则调整玩家数量
+	if newPlayerCount != currentPlayerCount {
+		if newPlayerCount > currentPlayerCount {
+			// 获取空闲的座位
+			availableSitPos := getAvailableSitPos(teenPattiRoom)
+			// 如果没有空座位,不能再加入机器人
+			if len(availableSitPos) == 0 {
+				log.Debug("No available seats for new robot in room %v", teenPattiRoom.Id)
+				return
+			}
+			// 加入新机器人
+			addNewRobot(teenPattiRoom, availableSitPos)
+			log.Debug("addNewRobot: %v")
+		} else {
+			// 随机去除一个机器人
+			removeRandomRobot(teenPattiRoom)
+			log.Debug("removeRandomRobot: %v")
+
+		}
+	}
+}

+ 289 - 106
src/server/game/teen/teen.go

@@ -2,20 +2,20 @@ package teen
 
 import (
 	"encoding/json"
-	"fmt"
 	"math/rand"
 	"os"
-	"server/game"
 	"server/game/room"
 	"server/msg"
-	"server/user"
 	"sort"
-	"time"
+	"sync"
 
-	"github.com/name5566/leaf/gate"
 	"github.com/name5566/leaf/log"
 )
 
+// 全局房间存储
+var roomStorage = map[string]*room.Room{}
+var mu sync.Mutex // 用于保护房间存储的并发访问
+
 type TeenPattiRoom struct {
 	Boot         string `json:"boot"`
 	MinBuyin     string `json:"minBuyin"`
@@ -23,7 +23,7 @@ type TeenPattiRoom struct {
 	PotLimit     string `json:"potLimit"`
 	TotalPlayers string `json:"totalPlayers"`
 	RoomLevel    string `json:"roomLevel"`
-	RoomId       string `json:"roomId"`
+	ConfId       string `json:"ConfId"`
 	Type         string `json:"type"`
 }
 
@@ -47,10 +47,12 @@ var SelfSitPos int32 = 4
 type CardType int
 
 const (
+	CardTypeThreeOfAKind  CardType = 0 //三条
 	CardTypeFlushStraight CardType = 1 // 同花顺
-	CardTypeFlush         CardType = 2 // 同花
-	CardTypePair          CardType = 3 // 对子
-	CardTypeSingle        CardType = 4 // 散牌
+	CardTypeStraight      CardType = 2 // 同花顺
+	CardTypeFlush         CardType = 3 // 同花
+	CardTypePair          CardType = 4 // 对子
+	CardTypeSingle        CardType = 5 // 散牌
 )
 
 func InitGame() {
@@ -69,59 +71,6 @@ func init() {
 	go handleEvents()
 }
 
-func createRoom(_ string, _ string, agent gate.Agent) {
-	//随即创建 1-3 个人机陪这个userId
-	userData := agent.UserData().(*user.UserData)
-	random := rand.Intn(3) + 1
-	//先移除掉除自己之外的机器人
-	userData.Teen_Patti_Room.RemoveAllRobot()
-	sitPosList := randomSitPos(userData.Teen_Patti_Room, random)
-	for i := 0; i < random; i++ {
-		robotUserData := createRobotUserData()
-		userData.Teen_Patti_Room.Players = append(userData.Teen_Patti_Room.Players, &room.Player{
-			Id:        fmt.Sprintf("%d", i),
-			Agent:     nil,
-			IsRobot:   true,
-			UserData:  robotUserData,
-			SitPos:    int32(sitPosList[i]),
-			HandCards: []*msg.ReqCard{},
-			IsPacked:  false,
-			IsSeen:    false,
-			IsShow:    false,
-			IsDealer:  false,
-			CanShow:   false,
-		})
-	}
-
-	randomDealer(userData.Teen_Patti_Room)
-}
-
-// 创建人机的UserData
-func createRobotUserData() *user.UserData {
-	return &user.UserData{
-		Id:              "robot",
-		Status:          1,
-		Teen_Patti_Room: nil,
-		Head_Image:      randomIndianHeadImage(),
-		Nickname:        randomIndianName(),
-	}
-}
-
-func startGame(userId string, roomId string, agent gate.Agent, teenPattiRoom *room.Room) {
-	// agent := Teen_Patti_Room.GetPlayer(userId).Agent
-	createRoom(userId, roomId, agent)
-	agent.WriteMsg(&msg.ReqJoinRoom{
-		UserId:   userId,
-		RoomId:   roomId,
-		GameId:   "teen_patti",
-		RoomInfo: buildRoom(teenPattiRoom),
-		SitPos:   SelfSitPos,
-	})
-	game.Module.Skeleton.AfterFunc(time.Second*2, func() {
-		go startDealCard(teenPattiRoom)
-	})
-}
-
 /* 完整的52张牌映射:
 黑桃(0):
     i=0:  ♠A  (Color=0, Point=1)
@@ -166,15 +115,34 @@ func initCardDeck(c_room *room.Room) {
 
 	// 初始化卡牌
 	for i := 0; i < 52; i++ {
+		point := int32(i%13 + 1) // 计算点数
+		if point == 1 {          // A(Ace)从 1 改成 14
+			point = 14
+		}
 		cards[i] = &msg.ReqCard{
-			Color: int32(i / 13),
-			Point: int32(i%13 + 1),
+			Color: int32(i / 13), // 计算花色
+			Point: point,         // 计算点数
 		}
 	}
 	// 赋值给房间
 	c_room.CardDeck = cards
 }
 
+// func initCardDeck(c_room *room.Room) {
+// 	// 创建长度为52的切片
+// 	cards := make([]*msg.ReqCard, 52)
+
+// 	// 初始化卡牌
+// 	for i := 0; i < 52; i++ {
+// 		cards[i] = &msg.ReqCard{
+// 			Color: int32(i / 13),
+// 			Point: int32(i%13 + 1),
+// 		}
+// 	}
+// 	// 赋值给房间
+// 	c_room.CardDeck = cards
+// }
+
 // 洗牌
 func shuffleCardDeck(deck []*msg.ReqCard) {
 	rand.Shuffle(len(deck), func(i, j int) {
@@ -182,14 +150,23 @@ func shuffleCardDeck(deck []*msg.ReqCard) {
 	})
 }
 
-// 计算一个玩家的手牌的类型,按照同花顺>同花>对子>散牌的顺序
+// 计算一个玩家的手牌的类型,按照三条>同花顺>顺子>同花>对子>散牌的顺序
 func calculateCardType(cards []*msg.ReqCard) CardType {
 	// 先判断是否是同花顺
+	isThreeOfAKind := isThreeOfAKind(cards)
 	isFlush := isFlush(cards)
+	// 判断是否是三条
+	if isThreeOfAKind {
+		return CardTypeThreeOfAKind
+	}
 	isStraight := isStraight(cards)
 	if isFlush && isStraight {
 		return CardTypeFlushStraight
 	}
+	// 判断是否是顺子
+	if isStraight {
+		return CardTypeStraight
+	}
 	// 判断是否是同花
 	if isFlush {
 		return CardTypeFlush
@@ -213,6 +190,14 @@ func isFlush(cards []*msg.ReqCard) bool {
 	return cards[0].Color == cards[1].Color && cards[1].Color == cards[2].Color
 }
 
+// 判断是否是三条(三张相同点数)
+func isThreeOfAKind(cards []*msg.ReqCard) bool {
+	if len(cards) != 3 {
+		return false
+	}
+	return cards[0].Point == cards[1].Point && cards[1].Point == cards[2].Point
+}
+
 // 判断是否是顺子(三张牌点数连续)
 func isStraight(cards []*msg.ReqCard) bool {
 	if len(cards) != 3 {
@@ -255,15 +240,6 @@ func isPair(cards []*msg.ReqCard) bool {
 		cards[0].Point == cards[2].Point
 }
 
-// 计算一个玩家的手牌的总值
-func calculateCardTotalValue(cards []*msg.ReqCard) int {
-	total := 0
-	for _, card := range cards {
-		total += int(card.Point)
-	}
-	return total
-}
-
 // 返回玩家列表里最大的玩家
 func compareCards(players []*room.Player) *room.Player {
 	if len(players) == 0 {
@@ -296,7 +272,15 @@ func compareCards(players []*room.Player) *room.Player {
 
 		cardType1 := calculateCardType(cards1)
 		cardType2 := calculateCardType(cards2)
-
+		// 处理A=14 ,并且把cards从大到小排序
+		sortCardsByPoint(cards1)
+		for _, card := range cards1 {
+			log.Debug("cards1---:Point: %d, Color: %d\n", card.Point, card.Color)
+		}
+		sortCardsByPoint(cards2)
+		for _, card := range cards2 {
+			log.Debug("cards2---: Point: %d, Color: %d\n", card.Point, card.Color)
+		}
 		// 如果牌型不同
 		if cardType1 != cardType2 {
 			// CardType 数字越小牌型越大
@@ -305,47 +289,246 @@ func compareCards(players []*room.Player) *room.Player {
 			}
 			continue
 		}
-
-		// 如果牌型相同,比较牌值
-		if result := compareCardValue(cards1, cards2); result < 0 {
-			maxPlayer = player
+		// 如果牌型相同,先确认是什么类型的牌
+		switch cardType1 {
+		//三条
+		case 0:
+			res := compareThreeOfAKind(cards1, cards2)
+			if res == 0 {
+				maxPlayer = player
+			}
+		//同花顺
+		case 1:
+			res := compareStraightFlush(cards1, cards2)
+			if res == 0 {
+				maxPlayer = player
+			}
+		//顺子
+		case 2:
+			log.Debug("对子终于来了")
+			res := compareStraight(cards1, cards2)
+			log.Debug("结果", res)
+			if res == 0 {
+				maxPlayer = player
+			}
+		//同花
+		case 3:
+			res := compareFlush(cards1, cards2)
+			if res == 0 {
+				maxPlayer = player
+			}
+		//对子
+		case 4:
+			res := comparePair(cards1, cards2)
+			if res == 0 {
+				maxPlayer = player
+			}
+		//高牌
+		case 5:
+			res := compareSingle(cards1, cards2)
+			if res == 0 {
+				maxPlayer = player
+			}
 		}
+
 	}
 
 	return maxPlayer
 }
 
-// 两个玩家手牌类型相同,比手牌总值
-func compareCardValue(cards1 []*msg.ReqCard, cards2 []*msg.ReqCard) int {
-	// 计算手牌总值
-	cardValue1 := calculateCardTotalValue(cards1)
-	cardValue2 := calculateCardTotalValue(cards2)
-	if cardValue1 == cardValue2 {
-		return compareCardColor(cards1, cards2)
+// valueToInt 映射点数,处理A为最大牌(14)
+func valueToInt(value int32) int32 {
+	// A (1) 作为最大牌,转换为 14
+	if value == 1 {
+		return 14
 	}
-	return int(cardValue1 - cardValue2)
+	return value
 }
 
-// 两个玩家手牌类型和大小都相同,比黑桃红桃方块梅花
-// 返回值:
-// 正数:cards1 赢
-// 负数:cards2 赢
-// 0:平局(理论上不会发生,因为不可能有完全相同的牌)
-func compareCardColor(cards1 []*msg.ReqCard, cards2 []*msg.ReqCard) int {
-	// 先把牌按点数排序
-	sort.Slice(cards1, func(i, j int) bool {
-		return cards1[i].Point < cards1[j].Point
-	})
-	sort.Slice(cards2, func(i, j int) bool {
-		return cards2[i].Point < cards2[j].Point
+// 排序:先按点数从大到小排序
+func sortCardsByPoint(cards []*msg.ReqCard) {
+	// 先把A转换为14,然后按点数从大到小排序
+	sort.Slice(cards, func(i, j int) bool {
+		// 转换点数(如果是A,将其视为14)
+		pointI := valueToInt(cards[i].Point)
+		pointJ := valueToInt(cards[j].Point)
+		// 如果点数相同,按花色排序(从小到大)
+		if pointI == pointJ {
+			return cards[i].Color < cards[j].Color
+		}
+		// 按点数从大到小排序
+		return pointI > pointJ
 	})
-
-	// 从大牌开始比较花色
-	for i := 2; i >= 0; i-- {
-		if cards1[i].Color != cards2[i].Color {
-			// 花色数字越小,牌越大(黑桃0 > 红桃1 > 方块2 > 梅花3)
-			return int(cards2[i].Color - cards1[i].Color)
+	// 排序完成后,更新所有 A 的点数为 14
+	for _, card := range cards {
+		if card.Point == 1 {
+			card.Point = 14
 		}
 	}
-	return 0
+}
+
+// 比较三条的大小
+func compareThreeOfAKind(cards1, cards2 []*msg.ReqCard) int {
+	// 比较三条的点数(这里需要特别处理 A)
+	val1 := cards1[0].Point
+	val2 := cards2[0].Point
+
+	// 比较点数
+	if val1 > val2 {
+		return 1 // cards1更大
+	} else {
+		return 0 // cards2更大
+	}
+}
+
+// 比较同花顺的大小
+func compareStraightFlush(cards1, cards2 []*msg.ReqCard) int {
+
+	// 比较最小的牌(最后一张牌,即最大点数)
+	max1 := cards1[2].Point // 排序后第三张牌是最大牌
+	max2 := cards2[2].Point
+	if max1 > max2 {
+		return 1 // cards1更大
+	} else if max1 < max2 {
+		return 0 // cards2更大
+	}
+	// 如果最小牌相同,则比较花色
+	if cards1[2].Color > cards2[2].Color {
+		return 1 // cards1更大
+	} else {
+		return 0 // cards2更大
+	}
+}
+
+// 顺子
+func compareStraight(cards1, cards2 []*msg.ReqCard) int {
+	// 比较最大的牌
+	max1 := cards1[0].Point // 排序后第三张牌是最大牌
+	max2 := cards2[0].Point
+	if max1 > max2 {
+		return 1 // cards1更大
+	} else if max1 < max2 {
+		return 0 // cards2更大
+	}
+	// 如果最大牌相同,则比较花色
+	if cards1[0].Color > cards2[0].Color {
+		return 1 // cards1更大
+	} else {
+		return 0 // cards2更大
+	}
+}
+
+// 同花
+func compareFlush(cards1, cards2 []*msg.ReqCard) int {
+	// 比较最大的牌
+	max1 := cards1[0].Point // 排序后第三张牌是最大牌
+	max2 := cards2[0].Point
+	if max1 > max2 {
+		return 1 // cards1更大
+	} else if max1 < max2 {
+		return 0 // cards2更大
+	}
+	// 比较第二大的牌
+	max1 = cards1[1].Point // 排序后第三张牌是最大牌
+	max2 = cards2[1].Point
+	if max1 > max2 {
+		return 1 // cards1更大
+	} else if max1 < max2 {
+		return 0 // cards2更大
+	}
+	// 比较第三大的牌
+	max1 = cards1[2].Point // 排序后第三张牌是最大牌
+	max2 = cards2[2].Point
+	if max1 > max2 {
+		return 1 // cards1更大
+	} else if max1 < max2 {
+		return 0 // cards2更大
+	}
+
+	// 如果相同,则比较最大牌的花色
+	if cards1[0].Color > cards2[0].Color {
+		return 1 // cards1更大
+	} else {
+		return 0 // cards2更大
+	}
+}
+
+// 对子
+func comparePair(cards1, cards2 []*msg.ReqCard) int {
+	var pairCardColor1, pairCardColor2 int32 // 声明花色变量
+	// 第二张牌一定是对子的其中一张
+	// 先判断对子大小, 如果相同,就判断剩余两张的点数加起来
+	// 直接比较第二张牌(即对子)
+	pair1 := cards1[1].Point
+	pair2 := cards2[1].Point
+
+	// 比较对子大小
+	if pair1 > pair2 {
+		return 1 // cards1的对子更大
+	} else if pair1 < pair2 {
+		return 0 // cards2的对子更大
+	}
+
+	// 对子相同,比较另外两张牌的点数之和
+	remaining1 := cards1[0].Point + cards1[2].Point
+	remaining2 := cards2[0].Point + cards2[2].Point
+
+	if remaining1 > remaining2 {
+		return 1 // cards1的剩余点数更大
+	} else if remaining1 < remaining2 {
+		return 0 // cards2的剩余点数更大
+	}
+	// 如果剩余点数相同,比较对子最大牌的花色
+	if cards1[0].Point == cards1[1].Point {
+		pairCardColor1 = cards1[0].Color
+	} else {
+		pairCardColor1 = cards1[1].Color
+	}
+
+	if cards2[0].Point == cards2[1].Point {
+		pairCardColor2 = cards2[0].Color
+	} else {
+		pairCardColor2 = cards2[1].Color
+	}
+
+	// 比较最大对子牌的花色
+	if pairCardColor1 > pairCardColor2 {
+		return 1 // cards1的对子花色较小
+	} else {
+		return 0 // cards2的对子花色较小
+	}
+}
+
+// 高张
+func compareSingle(cards1, cards2 []*msg.ReqCard) int {
+	// 比较最大的牌
+	max1 := cards1[0].Point // 排序后第三张牌是最大牌
+	max2 := cards2[0].Point
+	if max1 > max2 {
+		return 1 // cards1更大
+	} else if max1 < max2 {
+		return 0 // cards2更大
+	}
+	// 第二张的牌
+	max1 = cards1[1].Point // 排序后第三张牌是最大牌
+	max2 = cards2[1].Point
+	if max1 > max2 {
+		return 1 // cards1更大
+	} else if max1 < max2 {
+		return 0 // cards2更大
+	}
+	// 第三大的牌
+	max1 = cards1[2].Point // 排序后第三张牌是最大牌
+	max2 = cards2[2].Point
+	if max1 > max2 {
+		return 1 // cards1更大
+	} else if max1 < max2 {
+		return 0 // cards2更大
+	}
+	// 如果都想等,就比较最大的花色
+	if cards1[0].Color > cards2[0].Color {
+		return 1 // cards1更大
+	} else {
+		return 0 // cards2
+	}
 }

+ 106 - 0
src/server/game/teen/timer.go

@@ -1,3 +1,109 @@
+// package teen
+
+// import (
+// 	"sync"
+// 	"time"
+// )
+
+// // TimerEvent 定时事件结构
+// type TimerEvent struct {
+// 	ID        string      // 事件ID
+// 	Timestamp int64       // 触发时间
+// 	Callback  func()      // 回调函数
+// 	Timer     *time.Timer // 定时器
+// }
+
+// // TimerManager 定时管理器
+// type TimerManager struct {
+// 	events map[string]*TimerEvent
+// 	mutex  sync.RWMutex
+// }
+
+// // NewTimerManager 创建定时管理器
+// func NewTimerManager() *TimerManager {
+// 	return &TimerManager{
+// 		events: make(map[string]*TimerEvent),
+// 	}
+// }
+
+// // AddEvent 添加定时事件
+// func (tm *TimerManager) AddEvent(id string, delay time.Duration, callback func()) {
+// 	tm.mutex.Lock()
+// 	defer tm.mutex.Unlock()
+
+// 	// 先移除已有事件
+// 	if oldEvent, exists := tm.events[id]; exists {
+// 		oldEvent.Timer.Stop()
+// 		delete(tm.events, id)
+// 	}
+
+// 	// 创建新定时器
+// 	timer := time.NewTimer(delay)
+// 	event := &TimerEvent{
+// 		ID:        id,
+// 		Timestamp: time.Now().Add(delay).Unix(),
+// 		Callback:  callback,
+// 		Timer:     timer,
+// 	}
+
+// 	tm.events[id] = event
+
+// 	// 启动协程等待触发
+// 	go func() {
+// 		<-timer.C
+// 		tm.TriggerEvent(id)
+// 	}()
+// }
+
+// // TriggerEvent 立即触发事件
+// func (tm *TimerManager) TriggerEvent(id string) {
+// 	tm.mutex.Lock()
+// 	defer tm.mutex.Unlock()
+// 	tm.triggerEvent(id)
+// }
+
+// // 内部触发事件方法(已加锁)
+// func (tm *TimerManager) triggerEvent(id string) {
+// 	if event, exists := tm.events[id]; exists {
+// 		event.Timer.Stop()
+// 		event.Callback()
+// 		delete(tm.events, id)
+// 	}
+// }
+
+// // RemoveEvent 移除事件
+// func (tm *TimerManager) RemoveEvent(id string) {
+// 	tm.mutex.Lock()
+// 	defer tm.mutex.Unlock()
+
+// 	if event, exists := tm.events[id]; exists {
+// 		if !event.Timer.Stop() {
+// 			select {
+// 			case <-event.Timer.C:
+// 			default:
+// 			}
+// 		}
+// 		delete(tm.events, id)
+// 	}
+// }
+
+// // ResetEvent 重新设置定时器
+// func (tm *TimerManager) ResetEvent(id string, delay time.Duration) {
+// 	tm.mutex.Lock()
+// 	defer tm.mutex.Unlock()
+
+// 	if event, exists := tm.events[id]; exists {
+// 		if !event.Timer.Stop() {
+// 			select {
+// 			case <-event.Timer.C:
+// 			default:
+// 			}
+// 		}
+// 		event.Timer.Reset(delay)
+// 		event.Timestamp = time.Now().Add(delay).Unix()
+// 	}
+// }
+
 package teen
 
 import (

+ 1 - 1
src/server/go.mod

@@ -6,6 +6,7 @@ require (
 	github.com/go-sql-driver/mysql v1.8.1
 	github.com/gomodule/redigo v2.0.0+incompatible
 	github.com/name5566/leaf v0.0.0-20221021105039-af71eb082cda
+	google.golang.org/protobuf v1.36.2
 	gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22
 )
 
@@ -13,7 +14,6 @@ require (
 	filippo.io/edwards25519 v1.1.0 // indirect
 	github.com/golang/protobuf v1.5.4 // indirect
 	github.com/gorilla/websocket v1.5.3 // indirect
-	google.golang.org/protobuf v1.36.2 // indirect
 	gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
 	gopkg.in/yaml.v2 v2.4.0 // indirect
 )

+ 30 - 1
src/server/hall/internal/handler.go

@@ -21,8 +21,37 @@ func init() {
 	handleMsg(&msg.PlayList{}, playListHandler)
 	handleMsg(&msg.Shop{}, shopHandler)
 	handleMsg(&msg.ResGameInfo{}, reqGameInfoHandler)
+	handleMsg(&msg.ResRoomID{}, reqRoomIdHandler)
+	handleMsg(&msg.ReqLeaveRoom{}, leaveTeenPattiRoomHandler)
+}
+func reqRoomIdHandler(args []interface{}) {
+	m := args[0].(*msg.ResRoomID)
+	a := args[1].(gate.Agent)
+	// log.Debug("reqGameInfoHandler, msg: %v, agent: %v", m, a)
+	log.Debug("reqRoomIdHandler, msg: %+v, agent: %+v", m, a)
+	switch m.GameId {
+	case "teen_patti":
+		events.EventChan <- events.Event{
+			Type:  events.EventGetTeenPattiRoomId,
+			Data:  m,
+			Agent: a,
+		}
+	}
+}
+func leaveTeenPattiRoomHandler(args []interface{}) {
+	m := args[0].(*msg.ReqLeaveRoom)
+	a := args[1].(gate.Agent)
+	// log.Debug("reqGameInfoHandler, msg: %v, agent: %v", m, a)
+	log.Debug("reqGameInfoHandler, msg: %+v, agent: %+v", m, a)
+	switch m.GameId {
+	case "teen_patti":
+		events.EventChan <- events.Event{
+			Type:  events.EventLeaveTeenPattiRoom,
+			Data:  m,
+			Agent: a,
+		}
+	}
 }
-
 func reqGameInfoHandler(args []interface{}) {
 	m := args[0].(*msg.ResGameInfo)
 	a := args[1].(gate.Agent)

+ 7 - 6
src/server/main.go

@@ -3,28 +3,29 @@ package main
 import (
 	"server/conf"
 	"server/game"
-	"server/gate"
-	"server/login"
-
 	"server/game/teen"
+	"server/gate"
 	"server/hall"
+	"server/login"
 
 	"github.com/name5566/leaf"
 	lconf "github.com/name5566/leaf/conf"
 )
 
-func main() {
+func setupConfig() {
 	lconf.LogLevel = conf.Server.LogLevel
 	lconf.LogPath = conf.Server.LogPath
 	lconf.LogFlag = conf.LogFlag
 	lconf.ConsolePort = conf.Server.ConsolePort
 	lconf.ProfilePath = conf.Server.ProfilePath
-
+}
+func main() {
+	setupConfig()
 	teen.InitGame()
 	leaf.Run(
-		game.Module,
 		gate.Module,
 		login.Module,
+		game.Module,
 		hall.Module,
 	)
 }

+ 10 - 0
src/server/models/user.model.go

@@ -3,6 +3,8 @@ package models
 import (
 	"server/conf"
 	mongodbmgr "server/db/mongodb"
+
+	// redismgr "server/db/redis"
 	"time"
 
 	"gopkg.in/mgo.v2/bson"
@@ -19,6 +21,14 @@ type User struct {
 	LastLogin time.Time `bson:"last_login"`
 }
 
+// func AddGameToList(userId string) {
+// 	// 推入 Redis 列表
+// 	err := redismgr.ListPush("user:"+userId+":games", "game1")
+// 	if err != nil {
+// 		log.Println("Error pushing to Redis:", err)
+// 	}
+// }
+
 // 获取最近三天有登录的用户,支持分页
 func GetThreeDayUsersWithPagination(skip, limit int) ([]User, error) {
 	var users []User

Datei-Diff unterdrückt, da er zu groß ist
+ 703 - 102
src/server/msg/common.pb.go


+ 5 - 1
src/server/msg/msg.go

@@ -47,7 +47,11 @@ func init() {
 	Processor.Register(&ReqHeartBeat{})
 	Processor.Register(&ResHeartBeat{})
 	Processor.Register(&NextRound{})
-	Processor.Register(&ResRoomID{})
+	Processor.Register(&ReqLeaveRoom{})
+	Processor.Register(&ReqUserInfo{})
+	Processor.Register(&SiteShowResult{})
+	Processor.Register(&ReqInsufficientPoints{})
+
 	Processor.Range(func(id uint16, t reflect.Type) {
 		log.Debug("消息ID: %d, 消息类型: %s\n", id, t.Elem().Name())
 		msgList = append(msgList, MsgInfo{

BIN
src/server/server


+ 85 - 0
src/server/user/user.go

@@ -0,0 +1,85 @@
+package user
+
+import (
+	"server/common"
+	mongodbmgr "server/db/mongodb"
+	redismgr "server/db/redis"
+
+	"github.com/name5566/leaf/log"
+)
+
+// 查找用户信息是否已经存在 Redis 中
+func GetUserInfoById(userId string) *common.UserData {
+	// 从 Redis 中获取用户数据
+	userData, err := redismgr.GetUserInfoFromRedis(userId)
+	if err != nil {
+		log.Debug("Error fetching user info from Redis:", err)
+		return nil
+	}
+
+	// 如果 Redis 中有数据,直接返回
+	if userData != nil {
+		log.Debug("User found in Redis: %s", userId)
+		return userData
+	}
+
+	// 如果 Redis 中没有数据,检查 MongoDB
+	log.Debug("User not found in Redis, checking MongoDB: %s", userId)
+	userData, err = redismgr.GetUserInfoFromRedis(userId)
+	if err != nil {
+		log.Debug("Error fetching user info from MongoDB:", err)
+		return nil
+	}
+
+	// 如果 MongoDB 中也没有数据,执行新增操作
+	if userData == nil {
+		log.Debug("User not found in MongoDB, creating new user: %s", userId)
+		userData = &common.UserData{
+			Id:       userId,
+			Nickname: userId, // 或者从请求中获取
+			Points:   1000,   // 默认积分
+			Room_id:  "0",    // 默认房间ID
+		}
+
+		// 保存到 MongoDB 和 Redis
+		err = mongodbmgr.AddUser(userData)
+		if err != nil {
+			log.Debug("Error saving new user to MongoDB:", err)
+			return nil
+		}
+
+		err = redismgr.SaveUserInfoToRedis(userData)
+		if err != nil {
+			log.Debug("Error saving new user to Redis:", err)
+			return nil
+		}
+	}
+
+	return userData
+}
+
+// 减少用户积分
+func DecreaseUserPoint(decrementAmount int32, userId string) {
+	userData := GetUserInfoById(userId)
+	// 获取存储在 agent 中的 UserData
+	// 减少用户积分
+	userData.Points -= decrementAmount
+	SetUserInfo(userData)
+	// 打印更新日志
+	log.Debug("User %s's points decreased by %d. New balance: %d", userId, decrementAmount, userData.Points)
+}
+func SetUserInfo(userData *common.UserData) {
+	redismgr.SaveUserInfoToRedis(userData)
+}
+
+// 更新用户积分
+func AddUserPoint(betAmount int32, userId string) {
+	// 查找用户数据
+	userData := GetUserInfoById(userId)
+
+	// 更新用户的积分
+	userData.Points += betAmount
+	SetUserInfo(userData)
+	// 打印更新日志
+	log.Debug("User %s's points updated: %d", userId, userData.Points)
+}

+ 14 - 8
src/server/user/userData.go

@@ -1,11 +1,17 @@
 package user
 
-import "server/game/room"
+// import (
+// 	"server/game/room"
+// )
 
-type UserData struct {
-	Id              string
-	Status          int
-	Teen_Patti_Room *room.Room
-	Head_Image      string
-	Nickname        string
-}
+// // import "server/game/room"
+
+// type UserData struct {
+// 	Id              string
+// 	Status          int
+// 	Teen_Patti_Room *room.Room
+// 	Head_Image      string
+// 	Nickname        string
+// 	Room_id         string
+// 	Points          int32
+// }

Einige Dateien werden nicht angezeigt, da zu viele Dateien in diesem Diff geändert wurden.