package ws import "hakemsho/internal/game" // هوشِ باتِ حکم — تصمیم‌گیریِ اکتشافی (heuristic) که فقط از دستِ خودِ بات و // کارت‌های روی زمین استفاده می‌کند (بدونِ دیدنِ دستِ دیگران؛ بازیِ منصفانه). // // راهبردها: // - انتخابِ حکم: خالی که هم بلند است و هم کارتِ قوی دارد. // - رهبریِ دست: آسِ غیرِحکم را نقد کن؛ وگرنه از بلندترین خالِ غیرِحکم کارتِ کم بریز. // - دنبال‌کردن: اگر شریک می‌برد کارتِ کم بریز؛ اگر حریف می‌برد با کمترین کارتِ // برنده ببر؛ اگر خالِ زمینه را نداری و حریف می‌برد با کمترین حکم بِبُر. // botChooseTrump خالِ حکم را بر اساسِ طول + قدرتِ کارت‌ها انتخاب می‌کند. func botChooseTrump(hand []game.Card) game.Suit { var score [4]float64 for _, c := range hand { score[c.Suit] += 1 switch c.Rank { case game.Ace: score[c.Suit] += 2.0 case game.King: score[c.Suit] += 1.3 case game.Queen: score[c.Suit] += 0.7 case game.Jack: score[c.Suit] += 0.3 } } best, bestScore := game.Suit(0), -1.0 for s := game.Suit(0); s < 4; s++ { if score[s] > bestScore { bestScore, best = score[s], s } } return best } // botPlay کارتی که بات باید بازی کند را برمی‌گرداند. seen حافظه‌ی کارت‌های // بازی‌شده‌ی این هَند است (کارت‌شماری برای تصمیمِ بهتر). func botPlay(hand []game.Card, trick []game.TrickCard, trump, lead game.Suit, seen [4]uint16) game.Card { if len(trick) == 0 { return botLead(hand, trump, seen) } best := trickBest(trick, trump, lead) // جایگاه‌ها متناوب‌اند؛ کارتی که دو نوبت قبل انداخته شده متعلق به شریکِ باتِ // در نوبت است. اگر آن (یا هر کارتِ شریک) بهترین کارتِ فعلی باشد، شریک می‌برد. partnerWins := false if len(trick) >= 2 { partnerWins = best.Seat == trick[len(trick)-2].Seat } followers := cardsOfSuit(hand, lead) if len(followers) > 0 { if partnerWins { return lowest(followers) // شریک می‌برد ⇒ کارتِ کم بریز } if w, ok := lowestWinner(followers, best.Card, trump, lead); ok { return w // با کمترین کارتِ برنده ببر } return lowest(followers) // نمی‌توانی ببری ⇒ کارتِ کم } // خالِ زمینه را نداریم (void). trumps := cardsOfSuit(hand, trump) if partnerWins || len(trumps) == 0 { return lowestDiscard(hand, trump) } if w, ok := lowestWinner(trumps, best.Card, trump, lead); ok { return w // با کمترین حکمِ برنده بِبُر } return lowestDiscard(hand, trump) // حکمت هم نمی‌برد ⇒ دورریز } // botLead راهبردِ رهبریِ دست با کارت‌شماری: // ۱. کارتِ «برنده‌ی قطعی» (بالاترین کارتِ باقی‌مانده‌ی یک خالِ غیرِحکم) را نقد کن. // ۲. اگر کنترلِ حکم داری (بالاترین حکمِ باقی‌مانده + ۳ حکم یا بیشتر) حکم بکِش. // ۳. وگرنه از کوتاه‌ترین خالِ غیرِحکم کارتِ کم بریز تا زود void شوی و بعداً بِبُری // (اگر حکم داری)؛ در نبودِ حکم از بلندترین خال کارتِ کم. func botLead(hand []game.Card, trump game.Suit, seen [4]uint16) game.Card { counts := suitCounts(hand) // ۱. برنده‌ی قطعی: کارتی از خالِ غیرِحکم که هیچ کارتِ بالاتری از آن بیرون نمانده. var boss *game.Card for i := range hand { c := hand[i] if c.Suit == trump { continue } if higherOut(seen, hand, c.Suit, c.Rank) == 0 { if boss == nil || counts[c.Suit] > counts[boss.Suit] || (counts[c.Suit] == counts[boss.Suit] && c.Rank > boss.Rank) { cc := c boss = &cc } } } if boss != nil { return *boss } // ۲. کشیدنِ حکم: اگر بالاترین حکمِ باقی‌مانده دستِ ماست و حکمِ زیاد داریم. trumps := cardsOfSuit(hand, trump) if len(trumps) >= 3 { top := highest(trumps) if higherOut(seen, hand, trump, top.Rank) == 0 { return top } } // ۳. کوتاه‌ترین خالِ غیرِحکم (برای void شدن) اگر حکم داریم؛ وگرنه بلندترین. haveTrump := len(trumps) > 0 bestSuit, bestLen := game.Suit(255), -1 for s := game.Suit(0); s < 4; s++ { if s == trump || counts[s] == 0 { continue } better := false if bestLen < 0 { better = true } else if haveTrump { better = counts[s] < bestLen // کوتاه‌ترین } else { better = counts[s] > bestLen // بلندترین } if better { bestLen, bestSuit = counts[s], s } } if bestSuit != game.Suit(255) { return lowest(cardsOfSuit(hand, bestSuit)) } // فقط حکم مانده ⇒ بالاترین حکم را بزن. return highest(trumps) } // suitCounts تعدادِ کارتِ هر خال در دست. func suitCounts(hand []game.Card) [4]int { var c [4]int for _, x := range hand { c[x.Suit]++ } return c } // higherOut تعدادِ کارت‌های هم‌خالِ بالاتر از rank که هنوز بازی نشده‌اند و در دستِ // خودِ بات هم نیستند (یعنی دستِ حریف/شریک‌اند). ۰ یعنی این کارت «برنده‌ی خال» است. func higherOut(seen [4]uint16, hand []game.Card, suit game.Suit, rank game.Rank) int { n := 0 for r := rank + 1; r <= game.Ace; r++ { if seen[suit]&(1<<(r-2)) != 0 { continue // بازی شده } held := false for _, c := range hand { if c.Suit == suit && c.Rank == r { held = true break } } if !held { n++ } } return n } // --- کمکی‌ها --- func trickBest(trick []game.TrickCard, trump, lead game.Suit) game.TrickCard { best := trick[0] for _, tc := range trick[1:] { if game.Beats(tc.Card, best.Card, trump, lead) { best = tc } } return best } func cardsOfSuit(hand []game.Card, s game.Suit) []game.Card { var out []game.Card for _, c := range hand { if c.Suit == s { out = append(out, c) } } return out } func lowest(cards []game.Card) game.Card { best := cards[0] for _, c := range cards[1:] { if c.Rank < best.Rank { best = c } } return best } func highest(cards []game.Card) game.Card { best := cards[0] for _, c := range cards[1:] { if c.Rank > best.Rank { best = c } } return best } // lowestWinner کمترین کارتِ candidate که کارتِ فعلیِ برنده را می‌برد. func lowestWinner(candidates []game.Card, cur game.Card, trump, lead game.Suit) (game.Card, bool) { var best game.Card found := false for _, c := range candidates { if game.Beats(c, cur, trump, lead) && (!found || c.Rank < best.Rank) { best, found = c, true } } return best, found } // lowestDiscard کمترین کارتِ غیرِحکم را برای دورریز برمی‌گرداند؛ اگر فقط حکم مانده، // کمترین حکم. func lowestDiscard(hand []game.Card, trump game.Suit) game.Card { var nonTrump []game.Card for _, c := range hand { if c.Suit != trump { nonTrump = append(nonTrump, c) } } if len(nonTrump) > 0 { return lowest(nonTrump) } return lowest(hand) }