123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358 |
- package game
- import (
- "fmt"
- "github.com/davy66666/poker-go/src/server/protocol"
- "log"
- "microGame/app/usercenter/cmd/api/internal/algorithm"
- "microGame/app/usercenter/model/room"
- "microGame/pkg/common"
- "microGame/pkg/result"
- "sync"
- "time"
- )
- var once sync.Once
- const (
- RUNNING uint8 = 1
- GAMEOVER uint8 = 0
- SB uint32 = 0
- BB uint32 = 0
- CHIPS uint32 = 1000
- )
- type Room struct {
- *room.Room
- Rid int64
- Mutex sync.Mutex // 并发安全锁
- Players []*Player
- observes []*Player // 站起的玩家
- AutoSitDown []*Player // 自动坐下队列
- Phase string // 当前阶段(pre-flop, flop, turn, river, showdown)
- remain int
- allin int
- n uint8
- status uint8 //游戏是否进行中0:game over, 1:running
- isRunning bool //判断goroutine是否已经启动
- SB uint32 // 小盲注
- BB uint32 // 大盲注
- Cards algorithm.Cards // 公共牌
- Pot []uint32 // 奖池筹码数, 第一项为主池,其他项(若存在)为边池
- Timeout time.Duration // 倒计时超时时间(秒)
- Button uint8 // 当前庄家座位号,从1开始
- Chips []uint32 // 玩家本局下注的总筹码数,与Players一一对应
- Bet uint32 // 当前回合 上一玩家下注额
- Max uint8 // 房间最大玩家人数
- MaxChips uint32
- MinChips uint32
- Done chan struct{} //销毁room 退出goroutine
- NextRound chan struct{} // 游戏自动开始
- }
- func NewRoom(max uint8, sb, bb uint32, chips uint32, timeout uint8) *Room {
- if max <= 0 || max > 9 {
- max = 9 // default 9 Players
- }
- r := &Room{
- Chips: make([]uint32, max),
- Players: make([]*Player, max),
- Pot: make([]uint32, 0, max),
- Timeout: time.Second * time.Duration(timeout),
- SB: sb,
- BB: bb,
- Max: max,
- Done: make(chan struct{}),
- }
- //room入库记录room 生成room id
- return r
- }
- type startDelay struct {
- kind uint8
- }
- func (r *Room) New(m interface{}) *Room {
- if msg, ok := m.(*protocol.JoinRoom); ok {
- if len(msg.RoomNumber) == 0 {
- //r := room.FindRoom()
- //return r
- }
- //数据库查询room 如果存在直接返回
- //r := room.GetRoom(msg.RoomNumber)
- //if r != nil {
- // //return r
- //}
- //room.Insert()
- return NewRoom(9, SB, BB, CHIPS, Timeout)
- }
- return nil
- }
- func (r *Room) isEmpty() bool {
- return len(r.Players) == 0
- }
- func (r *Room) WriteMsg(msg interface{}, exc ...int64) {
- // 将 exc 列表转换成 map,提高排除用户的查询效率
- excMap := make(map[int64]struct{}, len(exc))
- for _, uid := range exc {
- excMap[uid] = struct{}{}
- }
- // 遍历玩家并广播消息
- for _, v := range r.Players {
- if v == nil {
- continue
- }
- // 如果玩家的 UID 在排除列表中,则跳过该玩家
- if _, excluded := excMap[v.GetUid()]; excluded {
- continue
- }
- v.WriteMsg(msg)
- }
- }
- func (r *Room) handleGameLogic() {
- fmt.Printf("房间 %d 处理游戏逻辑\n", r.Rid)
- // 示例:检查是否需要下一轮发牌,或者执行其他游戏逻辑
- r.nextRound()
- }
- func (r *Room) startGame() {
- r.Mutex.Lock()
- // 如果已经有 Goroutine 运行,不重复启动
- if r.isRunning {
- log.Printf("房间 %d 已经在运行\n", r.Rid)
- r.Mutex.Unlock()
- return
- }
- // 标记房间正在运行
- r.isRunning = true
- r.Mutex.Unlock()
- // 启动新的 Goroutine 来管理房间
- go func() {
- go common.RunWithRecover(func() {
- r.modifyRoomStatus()
- })
- r.nextRound()
- // 监听房间状态并执行游戏逻辑
- for {
- select {
- case <-r.NextRound:
- // 检查游戏状态并进行下一轮
- r.nextRound()
- case <-r.Done: // 监听房间关闭信号
- close(r.NextRound)
- log.Printf("房间 %d 关闭,退出 Goroutine\n", r.Rid)
- return
- }
- }
- }()
- }
- // **下一轮**
- func (r *Room) nextRound() bool {
- r.Mutex.Lock()
- log.Println("进入下一轮")
- // 将符合条件的观察者转为玩家
- for k := 0; k < len(r.observes); k++ {
- observer := r.observes[k]
- if observer.chips > 0 && uint8(len(r.Players)) < r.Max {
- observer.SetSitDown()
- r.Players = append(r.Players, observer)
- // 删除观察者
- r.observes = append(r.observes[:k], r.observes[k+1:]...)
- k-- // 调整索引,确保不会跳过下一个观察者
- log.Printf("观察者 %d 转为玩家", observer.Uid)
- }
- }
- // 检查是否继续游戏
- if len(r.Players) < 2 {
- log.Println("游戏结束")
- r.Mutex.Unlock()
- r.goNextGame()
- return false
- }
- //游戏正在进行
- r.status = 1
- r.Mutex.Unlock()
- //发牌 下注
- fmt.Println("开始发牌!")
- startDelay := &startDelay{
- kind: 0,
- }
- //处理游戏下注
- r.startDelay(startDelay)
- //// 进入新的一轮
- //// 5 秒后自动进入下一轮
- go common.RunWithRecover(func() {
- r.Broadcast(result.WSSuccess("start", startDelay), true)
- r.goNextGame()
- })
- return true
- }
- func (r *Room) goNextGame() {
- go func() {
- time.Sleep(5 * time.Second)
- r.NextRound <- struct{}{}
- }()
- }
- func (r *Room) modifyRoomStatus() {
- //r.Started = false
- r.status = 0
- }
- func (r *Room) Broadcast(msg interface{}, all bool, exc ...int64) {
- // 将 exc 列表转换成 map,提高排除用户的查询效率
- var excMap map[int64]struct{}
- if len(exc) > 0 {
- excMap = make(map[int64]struct{}, len(exc))
- for _, uid := range exc {
- excMap[uid] = struct{}{}
- }
- }
- // 遍历玩家并广播消息
- for _, v := range r.Players {
- if v == nil || (!all && v.IsGaming()) {
- continue
- }
- if _, excluded := excMap[v.GetUid()]; excluded {
- continue
- }
- v.WriteMsg(msg)
- }
- // 遍历观察者并广播消息
- for _, v := range r.observes {
- if v == nil {
- continue
- }
- if _, excluded := excMap[v.Uid]; excluded {
- continue
- }
- v.WriteMsg(msg)
- }
- }
- func (r *Room) addPlayer(o *Player) uint8 {
- for _, v := range r.Players {
- if v != nil && v.GetUid() == o.Uid {
- return 0
- }
- }
- for k, v := range r.Players {
- if v == nil {
- r.Players[k] = o
- o.Pos = uint8(k + 1)
- o.SetSitDown()
- return o.Pos
- }
- }
- return 0
- }
- func (r *Room) removePlayer(o *Player) uint8 {
- for k, v := range r.Players {
- if v != nil && v.GetUid() == o.Uid {
- v.SetPos(0)
- r.Players[k] = nil
- return uint8(k + 1)
- }
- }
- return 0
- }
- func (r *Room) addObserve(o *Player) uint8 {
- for _, v := range r.observes {
- if v != nil && v.Uid == o.Uid {
- return 0
- }
- }
- o.SetObserve()
- r.observes = append(r.observes, o)
- return 0
- }
- func (r *Room) removeObserve(o *Player) {
- for k, v := range r.observes {
- if v != nil && v.Uid == o.Uid {
- r.observes = append(r.observes[:k], r.observes[k+1:]...)
- return
- }
- }
- }
- func (r *Room) CloseRoomChannel() {
- once.Do(func() {
- close(r.Done)
- fmt.Println("通道被关闭")
- })
- }
- // Each 从指定索引 `start` 开始遍历玩家列表,并对满足条件的玩家执行回调函数 `f`
- func (r *Room) Each(start uint8, f func(o *Player) bool) {
- volume := r.Cap()
- if volume == 0 { // 避免除零错误
- return
- }
- end := (volume + start - 1) % volume
- i := start
- for {
- player := r.Players[i]
- if player != nil && player.IsGaming() {
- if !f(player) { // 如果回调返回 false,则终止循环
- return
- }
- }
- if i == end { // 处理最后一个元素,防止额外调用
- break
- }
- i = (i + 1) % volume
- }
- }
- func (r *Room) Cap() uint8 {
- return r.Max
- }
- func (r *Room) Len() uint8 {
- var num uint8
- for _, v := range r.Players {
- if v != nil {
- num++
- }
- }
- return num
- }
- func (r *Room) GetNumber() string {
- return r.RoomNumber
- }
- func (r *Room) SetNumber(value string) {
- r.RoomNumber = value
- }
- func (r *Room) Data() interface{} { return r.Room }
- func (r *Room) FindRoom() bool {
- r.Mutex.Lock()
- if r.Len() < r.Cap() {
- return true
- }
- r.Mutex.Unlock()
- return false
- }
|