fix: rule game for kot and hakem kot
This commit is contained in:
@@ -46,3 +46,12 @@ func (g gameSettler) RecordGame(room, playersJSON string, winnerTeam int, kot bo
|
||||
slog.Error("record game", "room", room, "err", err)
|
||||
}
|
||||
}
|
||||
|
||||
// TargetHands تعداد هَندِ لازم برای برد بازی در این tier (مثلاً مبتدی=۳).
|
||||
func (g gameSettler) TargetHands(tier string) int {
|
||||
t := economy.FindTier(tier)
|
||||
if t == nil || t.Hands <= 0 {
|
||||
return 7
|
||||
}
|
||||
return t.Hands
|
||||
}
|
||||
|
||||
@@ -46,7 +46,8 @@ type TrickCard struct {
|
||||
type HandResult struct {
|
||||
WinnerTeam int `json:"winner_team"`
|
||||
Kot bool `json:"kot"` // کُت: تیم بازنده هیچ دستی نبرد
|
||||
Points int `json:"points"` // امتیاز این هَند (۱ معمولی، ۲ کُت)
|
||||
HakemKot bool `json:"hakem_kot"` // حاکمکُت: تیمِ حاکم کُت شد (۳ امتیاز)
|
||||
Points int `json:"points"` // امتیاز این هَند (۱ معمولی، ۲ کُت، ۳ حاکمکُت)
|
||||
NextHakem int `json:"next_hakem"`
|
||||
}
|
||||
|
||||
@@ -269,6 +270,11 @@ func (g *Game) checkHandOver() {
|
||||
points := 1
|
||||
if kot {
|
||||
points = 2
|
||||
// حاکمکُت: اگر تیمِ بازنده همان تیمِ حاکم باشد (حاکم صفر دست برده)،
|
||||
// کُت سنگینتر است ⇒ ۳ امتیاز.
|
||||
if Team(g.Hakem) == loserTeam {
|
||||
points = 3
|
||||
}
|
||||
}
|
||||
g.Scores[winnerTeam] += points
|
||||
|
||||
@@ -281,6 +287,7 @@ func (g *Game) checkHandOver() {
|
||||
g.LastResult = &HandResult{
|
||||
WinnerTeam: winnerTeam,
|
||||
Kot: kot,
|
||||
HakemKot: points == 3,
|
||||
Points: points,
|
||||
NextHakem: nextHakem,
|
||||
}
|
||||
|
||||
@@ -114,6 +114,49 @@ func TestFullGamePlaysOut(t *testing.T) {
|
||||
t.Logf("final scores: %v", g.Scores)
|
||||
}
|
||||
|
||||
// TestTargetScoreRespected بررسی میکند بازی دقیقاً وقتی یک تیم به TargetScore
|
||||
// رسید تمام میشود (نه زودتر، نه دیرتر) — برای میزهایی مثل مبتدی (۳ دست).
|
||||
func TestTargetScoreRespected(t *testing.T) {
|
||||
for _, target := range []int{1, 3, 5, 7} {
|
||||
for trial := 0; trial < 20; trial++ {
|
||||
g := NewGame(target, FirstHakem())
|
||||
for g.Phase != PhaseGameOver {
|
||||
switch {
|
||||
case g.TrickDone:
|
||||
if err := g.CollectTrick(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
case g.Phase == PhaseChooseTrump:
|
||||
if err := g.ChooseTrump(g.Hakem, g.hands[g.Hakem][0].Suit); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
case g.Phase == PhaseHandOver:
|
||||
if err := g.NextHand(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
default: // playing
|
||||
if err := g.PlayCard(g.Turn, legalMove(g, g.Turn)); err != nil {
|
||||
t.Fatalf("play: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
w := g.Scores[0]
|
||||
if g.Scores[1] > w {
|
||||
w = g.Scores[1]
|
||||
}
|
||||
if w < target {
|
||||
t.Fatalf("target %d: game over but max score %d < target (scores %v)",
|
||||
target, w, g.Scores)
|
||||
}
|
||||
// بازنده نباید به هدف رسیده باشد (بازی باید بهمحضِ رسیدنِ یک تیم تمام شود).
|
||||
loser := g.Scores[0] + g.Scores[1] - w
|
||||
if loser >= target {
|
||||
t.Fatalf("target %d: both teams reached target %v", target, g.Scores)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// legalMove یک حرکت مجاز برای ربات انتخاب میکند (follow suit در صورت امکان).
|
||||
func legalMove(g *Game, seat int) Card {
|
||||
hand := g.hands[seat]
|
||||
|
||||
@@ -58,6 +58,7 @@ type handOverMsg struct {
|
||||
Type string `json:"type"` // "hand_over"
|
||||
WinnerTeam int `json:"winner_team"`
|
||||
Kot bool `json:"kot"`
|
||||
HakemKot bool `json:"hakem_kot"`
|
||||
Points int `json:"points"`
|
||||
Scores [2]int `json:"scores"`
|
||||
}
|
||||
|
||||
+7
-2
@@ -101,7 +101,12 @@ func (r *Room) run() {
|
||||
|
||||
// start بازی را ساخته، matched را برای انسانها فرستاده و وضعیت اولیه را پخش میکند.
|
||||
func (r *Room) start() {
|
||||
r.game = game.NewGame(targetScore, game.FirstHakem())
|
||||
// تعداد هَندِ لازم برای برد بازی از روی نوعِ میز (tier) تعیین میشود.
|
||||
target := r.settler.TargetHands(r.tier)
|
||||
if target <= 0 {
|
||||
target = targetScore
|
||||
}
|
||||
r.game = game.NewGame(target, game.FirstHakem())
|
||||
for seat, s := range r.seats {
|
||||
if s.client != nil {
|
||||
s.client.trySend(mustJSON(r.matchedFor(seat)))
|
||||
@@ -127,7 +132,7 @@ func (r *Room) react() {
|
||||
res := r.game.LastResult
|
||||
r.broadcast(mustJSON(handOverMsg{
|
||||
Type: "hand_over", WinnerTeam: res.WinnerTeam, Kot: res.Kot,
|
||||
Points: res.Points, Scores: r.game.Scores,
|
||||
HakemKot: res.HakemKot, Points: res.Points, Scores: r.game.Scores,
|
||||
}))
|
||||
if err := r.game.NextHand(); err != nil {
|
||||
slog.Error("next hand", "room", r.ID, "err", err)
|
||||
|
||||
@@ -7,6 +7,8 @@ type Settler interface {
|
||||
Refund(userID int64, tier string) // بازگرداندن ورودی در صورت لغو
|
||||
AwardWinner(userID int64, tier string) // جایزه/XP/جام به برنده
|
||||
RecordGame(room, playersJSON string, winnerTeam int, kot bool)
|
||||
// TargetHands تعداد هَندِ لازم برای برد بازی در این tier (مثلاً مبتدی=۳).
|
||||
TargetHands(tier string) int
|
||||
}
|
||||
|
||||
// noopSettler پیشفرض (برای تستها و حالت بدون اقتصاد).
|
||||
@@ -16,3 +18,4 @@ func (noopSettler) ChargeEntry(int64, string) error { return nil }
|
||||
func (noopSettler) Refund(int64, string) {}
|
||||
func (noopSettler) AwardWinner(int64, string) {}
|
||||
func (noopSettler) RecordGame(string, string, int, bool) {}
|
||||
func (noopSettler) TargetHands(string) int { return 7 }
|
||||
|
||||
@@ -346,6 +346,9 @@ func (s *recordingSettler) RecordGame(string, string, int, bool) {
|
||||
s.records++
|
||||
s.mu.Unlock()
|
||||
}
|
||||
|
||||
// برای سرعتِ تست، بازی پس از ۲ هَند تمام میشود.
|
||||
func (s *recordingSettler) TargetHands(string) int { return 2 }
|
||||
func (s *recordingSettler) snapshot() (c, r, a, rec int) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
Reference in New Issue
Block a user