fix: 修复牌型识别、获胜显示、出牌区布局、移动端重连
1. 牌型识别: 新增三带一对、飞机、飞机带单/带对、四带二、四带两对 2. 获胜显示: 先显示最后出牌1秒后再弹出获胜对话框 3. 出牌区布局: 横向排列并换行 4. 移动端重连: 切回前台时自动重连WebSocket 5. 更新文档: README.md和API.md补充新增牌型
This commit is contained in:
@@ -75,59 +75,100 @@ func (cl *CardLogic) GetCardType(cards []models.Card) models.CardType {
|
||||
cl.SortCards(sorted)
|
||||
counts := cl.getValueCounts(sorted)
|
||||
|
||||
switch n {
|
||||
case 1:
|
||||
return models.CardTypeSingle
|
||||
case 2:
|
||||
if len(counts) == 1 {
|
||||
return models.CardTypePair
|
||||
// 统计各种点数的数量
|
||||
var singles, pairs, triples, quads []int
|
||||
for v, c := range counts {
|
||||
switch c {
|
||||
case 1:
|
||||
singles = append(singles, v)
|
||||
case 2:
|
||||
pairs = append(pairs, v)
|
||||
case 3:
|
||||
triples = append(triples, v)
|
||||
case 4:
|
||||
quads = append(quads, v)
|
||||
}
|
||||
return models.CardTypeInvalid
|
||||
case 3:
|
||||
if len(counts) == 1 {
|
||||
return models.CardTypeBomb
|
||||
}
|
||||
if cl.isStraight(sorted) {
|
||||
return models.CardTypeStraight
|
||||
}
|
||||
return models.CardTypeInvalid
|
||||
case 4:
|
||||
if len(counts) == 1 {
|
||||
return models.CardTypeBomb
|
||||
}
|
||||
if len(counts) == 2 {
|
||||
for _, c := range counts {
|
||||
if c == 3 {
|
||||
return models.CardTypeTripleOne
|
||||
}
|
||||
}
|
||||
}
|
||||
if cl.isStraight(sorted) {
|
||||
return models.CardTypeStraight
|
||||
}
|
||||
if cl.isDoubleStraight(sorted, counts) {
|
||||
return models.CardTypeDoubleStraight
|
||||
}
|
||||
return models.CardTypeInvalid
|
||||
default:
|
||||
if cl.isStraight(sorted) {
|
||||
return models.CardTypeStraight
|
||||
}
|
||||
if cl.isDoubleStraight(sorted, counts) {
|
||||
return models.CardTypeDoubleStraight
|
||||
}
|
||||
hasTriple := false
|
||||
for _, c := range counts {
|
||||
if c >= 3 {
|
||||
hasTriple = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if hasTriple && (n == 4 || n == 5) {
|
||||
return models.CardTypeTripleOne
|
||||
}
|
||||
return models.CardTypeInvalid
|
||||
}
|
||||
|
||||
// 炸弹(三张或以上相同)
|
||||
if len(counts) == 1 && n >= 3 {
|
||||
return models.CardTypeBomb
|
||||
}
|
||||
|
||||
// 四带二(4张相同+2单张)
|
||||
if len(quads) == 1 && len(singles) == 2 && len(pairs) == 0 && len(triples) == 0 && n == 6 {
|
||||
return models.CardTypeFourWithTwo
|
||||
}
|
||||
|
||||
// 四带两对(4张相同+2对)
|
||||
if len(quads) == 1 && len(pairs) == 2 && len(singles) == 0 && len(triples) == 0 && n == 8 {
|
||||
return models.CardTypeFourWithTwoPairs
|
||||
}
|
||||
|
||||
// 飞机(连续的三张)
|
||||
if len(triples) >= 2 && cl.isConsecutive(triples) {
|
||||
planeCount := len(triples)
|
||||
// 纯飞机
|
||||
if n == planeCount*3 {
|
||||
return models.CardTypeAirplane
|
||||
}
|
||||
// 飞机带单张
|
||||
if n == planeCount*3+planeCount && len(singles) == planeCount {
|
||||
return models.CardTypeAirplaneSingle
|
||||
}
|
||||
// 飞机带对子
|
||||
if n == planeCount*3+planeCount*2 && len(pairs) == planeCount {
|
||||
return models.CardTypeAirplanePair
|
||||
}
|
||||
}
|
||||
|
||||
// 单张
|
||||
if n == 1 {
|
||||
return models.CardTypeSingle
|
||||
}
|
||||
|
||||
// 对子
|
||||
if n == 2 && len(counts) == 1 {
|
||||
return models.CardTypePair
|
||||
}
|
||||
|
||||
// 三带一(3张相同+1单张)
|
||||
if n == 4 && len(triples) == 1 && len(singles) == 1 {
|
||||
return models.CardTypeTripleOne
|
||||
}
|
||||
|
||||
// 三带一对(3张相同+1对)
|
||||
if n == 5 && len(triples) == 1 && len(pairs) == 1 {
|
||||
return models.CardTypeTriplePair
|
||||
}
|
||||
|
||||
// 顺子
|
||||
if cl.isStraight(sorted) {
|
||||
return models.CardTypeStraight
|
||||
}
|
||||
|
||||
// 连对
|
||||
if cl.isDoubleStraight(sorted, counts) {
|
||||
return models.CardTypeDoubleStraight
|
||||
}
|
||||
|
||||
return models.CardTypeInvalid
|
||||
}
|
||||
|
||||
func (cl *CardLogic) isConsecutive(values []int) bool {
|
||||
if len(values) < 2 {
|
||||
return true
|
||||
}
|
||||
sort.Ints(values)
|
||||
for i := 1; i < len(values); i++ {
|
||||
if values[i] != values[i-1]+1 {
|
||||
return false
|
||||
}
|
||||
if values[i] >= 15 { // 2和王不能参与
|
||||
return false
|
||||
}
|
||||
}
|
||||
return values[0] < 15
|
||||
}
|
||||
|
||||
func (cl *CardLogic) isRocket(cards []models.Card) bool {
|
||||
@@ -230,9 +271,36 @@ func (cl *CardLogic) CanPlay(cards []models.Card, lastPlay *models.PlayRecord) b
|
||||
if cardType != lastPlay.CardType || len(cards) != len(lastPlay.Cards) {
|
||||
return false
|
||||
}
|
||||
|
||||
if cardType == models.CardTypeAirplane || cardType == models.CardTypeAirplaneSingle || cardType == models.CardTypeAirplanePair {
|
||||
return cl.compareAirplanes(cards, lastPlay.Cards)
|
||||
}
|
||||
|
||||
return cl.getMainValue(cards) > cl.getMainValue(lastPlay.Cards)
|
||||
}
|
||||
|
||||
func (cl *CardLogic) compareAirplanes(cards, lastCards []models.Card) bool {
|
||||
myTriples := cl.getTripleValues(cards)
|
||||
lastTriples := cl.getTripleValues(lastCards)
|
||||
if len(myTriples) != len(lastTriples) {
|
||||
return false
|
||||
}
|
||||
sort.Ints(myTriples)
|
||||
sort.Ints(lastTriples)
|
||||
return myTriples[len(myTriples)-1] > lastTriples[len(lastTriples)-1]
|
||||
}
|
||||
|
||||
func (cl *CardLogic) getTripleValues(cards []models.Card) []int {
|
||||
counts := cl.getValueCounts(cards)
|
||||
var triples []int
|
||||
for v, c := range counts {
|
||||
if c >= 3 {
|
||||
triples = append(triples, v)
|
||||
}
|
||||
}
|
||||
return triples
|
||||
}
|
||||
|
||||
func (cl *CardLogic) getMainValue(cards []models.Card) int {
|
||||
counts := cl.getValueCounts(cards)
|
||||
maxValue, maxCount := 0, 0
|
||||
|
||||
@@ -29,6 +29,11 @@ const (
|
||||
CardTypeDoubleStraight
|
||||
CardTypeBomb
|
||||
CardTypeRocket
|
||||
CardTypeAirplane
|
||||
CardTypeAirplaneSingle
|
||||
CardTypeAirplanePair
|
||||
CardTypeFourWithTwo
|
||||
CardTypeFourWithTwoPairs
|
||||
)
|
||||
|
||||
type Player struct {
|
||||
|
||||
@@ -185,6 +185,7 @@ func (c *Client) handleMessage(data []byte) {
|
||||
}
|
||||
room, _ := c.Hub.GameMgr.GetRoom(c.RoomID)
|
||||
if room != nil && room.State == models.RoomStateFinished {
|
||||
c.Hub.broadcastFinalState(c.RoomID, room)
|
||||
c.Hub.broadcastToRoom(c.RoomID, models.Message{
|
||||
Type: models.MsgTypeGameOver,
|
||||
Data: map[string]string{"winnerId": room.LastWinner},
|
||||
@@ -313,6 +314,34 @@ func (h *Hub) broadcastRoomState(roomID string) {
|
||||
}
|
||||
}
|
||||
|
||||
func (h *Hub) broadcastFinalState(roomID string, room *models.Room) {
|
||||
h.mu.RLock()
|
||||
clients := make([]*Client, 0)
|
||||
for _, c := range h.Clients {
|
||||
if c.RoomID == roomID {
|
||||
clients = append(clients, c)
|
||||
}
|
||||
}
|
||||
h.mu.RUnlock()
|
||||
|
||||
for _, c := range clients {
|
||||
state := h.GameMgr.GetRoomState(roomID, c.ID)
|
||||
if state != nil {
|
||||
state.State = models.RoomStatePlaying
|
||||
data, _ := json.Marshal(models.Message{
|
||||
Type: models.MsgTypeState,
|
||||
Data: state,
|
||||
Timestamp: time.Now().Unix(),
|
||||
})
|
||||
select {
|
||||
case c.Send <- data:
|
||||
case <-c.done:
|
||||
default:
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (h *Hub) BroadcastRoomLeft(roomID, playerID string) {
|
||||
h.mu.RLock()
|
||||
clients := make([]*Client, 0)
|
||||
|
||||
Reference in New Issue
Block a user