package game import ( "errors" "sonet/pkg/utils/state" "sync" ) type MjTable struct { state.State TableId string MasterId string // 房主id Stage int8 // 游戏阶段:1.等待开始;2.游戏开始;3.undefined; 4.结算阶段 GameTimes int32 // 游戏次数 PlayerCount int16 Players [4]*MjPlayer // 玩家列表 PlayerDirects [4]int8 // 玩家方向: 0.东 1.南 2.西 3.北 MjWall *MjWall // 麻将牌堆 HitPlayerId string // 回合出牌玩家 Hits []int8 Lock *sync.Mutex } func NewMjTable(tableId string, masterId string) *MjTable { return &MjTable{ TableId: tableId, MasterId: masterId, Stage: 1, Players: [4]*MjPlayer{}, PlayerDirects: [4]int8{0, 1, 2, 3}, MjWall: &MjWall{}, Lock: &sync.Mutex{}, } } func (t *MjTable) Init() { t.InitState(t, state.WithState2Mapping(func(states state.S, setters *[]state.Setter) { if mjWall, ok := states["EastPlayerIndex"]; ok { delete(states, "EastPlayerIndex") states["eastPlayerId"] = mjWall.(*MjWall).RestNum() } if mjWall, ok := states["MjWall"]; ok { delete(states, "MjWall") states["RestTiles"] = mjWall.(*MjWall).RestNum() } })) } // GameStart 下一句游戏 func (t *MjTable) GameStart() (err error) { // 换东南西北 if t.GameTimes > 0 { for i := 0; i < 4; i++ { t.PlayerDirects[i] = (t.PlayerDirects[i] + 1) % 4 } } // 初始化麻将牌堆 t.MjWall.Init() return t.SetStateEx(state.S{ "Stage": int8(2), "PlayerDirects": t.PlayerDirects, "GameTimes": t.GameTimes + 1, "MjWall": t.MjWall, "Hits": []int8{}, }) } // GetEastPlayerIndex 东玩家索引 func (t *MjTable) GetEastPlayerIndex() int { for i, direct := range t.PlayerDirects { if direct == 0 { return i } } return 0 } // EastPlayerSit 东玩家(为nil则顺时针最近玩家)索引位置 func (t *MjTable) EastPlayerSit() int { for i, direct := range t.PlayerDirects { if direct == 0 { // 东 if t.Players[i] != nil { return i } else { for j := 1; j < 4; j++ { sit := (i + j) % 4 if t.Players[sit] != nil { return sit } } } break } } return 0 } // PrevHit 最后出的一张牌 func (t *MjTable) PrevHit() int8 { length := len(t.Hits) if length == 0 { return 0 } return t.Hits[length-1] } func (t *MjTable) GetPlayer(playerId string) *MjPlayer { for _, p := range t.Players { if p != nil && p.PlayerId == playerId { return p } } return nil } func (t *MjTable) GetPlayers(excludePlayerIds ...string) (players []*MjPlayer) { excludes := make(map[string]bool, len(excludePlayerIds)) for _, exclude := range excludePlayerIds { excludes[exclude] = true } players = make([]*MjPlayer, 0, t.PlayerCount) for _, player := range t.Players { if player != nil { if excludes[player.PlayerId] { // 跳过要忽略的 continue } players = append(players, player) } } return } func (t *MjTable) GetPlayerIds(excludePlayerIds ...string) (playerIds []string) { players := t.GetPlayers(excludePlayerIds...) playerIds = make([]string, 0, len(players)) for _, p := range players { playerIds = append(playerIds, p.PlayerId) } return } func (t *MjTable) PlayerJoin(player *MjPlayer) (int, error) { for i := 0; i < 4; i++ { if t.Players[i] == nil { err := t.SetStateEx(state.S{ "Players": state.SetArray(i, player), }) if err != nil { return i, err } player.MjTable = t player.Status = 1 t.PlayerCount++ return i, err } } return 0, errors.New("牌桌已满") } func (t *MjTable) PlayerLeave(playerId string) (err error) { for i := 0; i < 4; i++ { if t.Players[i] != nil && t.Players[i].PlayerId == playerId { player := t.Players[i] err = t.SetStateEx(state.S{ "Players": state.SetArray(i, nil), }) if err != nil { return } player.MjTable = nil t.PlayerCount-- break } } return } // NextDrawPlayer 下一摸牌玩家 func (t *MjTable) NextDrawPlayer(curPlayerId string) (nextPlayer *MjPlayer, err error) { if t.MjWall.RestNum() == 0 { // 没牌了结算 err = t.GameOver() return } for i, p := range t.Players { if p != nil && p.PlayerId == curPlayerId { for j := 1; j <= 4; j++ { next := t.Players[(i+j)%4] if next != nil && next.Status != 3 { nextPlayer = next return } } break } } // 没下个玩家了结算 err = t.GameOver() return } // GameOver 游戏结束, 结算 func (t *MjTable) GameOver() (err error) { // 游戏结束 for _, player := range t.GetPlayers() { player.SetState(state.S{ "Status": int8(4), "MjHand": player.MjHand, "MjDraw": player.MjDraw, }) player.State2Broadcast() } t.SetState(state.S{"Stage": int8(4)}) t.State2Broadcast() return } // PlayerWin 计算胡牌倍数 func (t *MjTable) PlayerWin(player *MjPlayer, selfDraw bool, wins []HandWin) (settle *WinSettle) { settle = &WinSettle{ Weights: player.WinWeights(selfDraw), } for _, win := range wins { winType := win.GetWinType(player.MjPeng, player.MjGang) calcWeight := winType.Weight() for _, weight := range settle.Weights { calcWeight *= weight.Weight() } if calcWeight > settle.Weight { settle.Weight = calcWeight settle.WinType = winType } } return }