Files
nl-game-api/internal/ai/ddz_rule_test.go
2026-08-14 13:17:03 +08:00

133 lines
4.6 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package ai
import (
"testing"
"nl-game-api-gin/internal/gamecore/ddz"
)
// card 按点数造一张牌(花色取 0-3 循环,保证同点数多张时编码不同)
// 点数 r3-1515=216=小王17=大王
func card(r, i int) int {
if r == 16 {
return 52
}
if r == 17 {
return 53
}
return (r-3)*4 + i
}
// TestFarmerNotBeatPartner 平民不压队友队友出的中等牌J 以上)应选择过牌
func TestFarmerNotBeatPartner(t *testing.T) {
a := &ruleDdzAI{difficulty: DiffMedium, name: "测试"}
// 手牌里有 2能压 J但队友出的 J 不该压
hand := []int{card(15, 0), card(10, 0), card(5, 0), card(5, 1)}
last := ddz.Parse([]int{card(11, 0)}) // 队友出单张 J
pick, _ := a.DecidePlay(hand, last, PlayContext{
IsLandlord: false, LastIsPartner: true,
PartnerCards: 8, LandlordCards: 10, OppMin: 10,
})
if pick != nil {
t.Fatalf("平民不应压队友的大牌,却出了 %v", pick)
}
}
// TestFarmerBeatPartnerSmall 队友前期小牌可以用小牌接管,但不能动 2/王/炸弹
func TestFarmerBeatPartnerSmall(t *testing.T) {
a := &ruleDdzAI{difficulty: DiffMedium, name: "测试"}
hand := []int{card(15, 0), card(8, 0), card(6, 0), card(6, 1)}
last := ddz.Parse([]int{card(4, 0)}) // 队友出单张 4早期小牌
pick, _ := a.DecidePlay(hand, last, PlayContext{
IsLandlord: false, LastIsPartner: true,
PartnerCards: 10, LandlordCards: 12, OppMin: 12,
})
if pick == nil {
t.Fatal("队友的小牌应该用小牌接管节奏")
}
if c := ddz.Parse(pick); c.MainRank > 12 {
t.Fatalf("接队友的牌不应动大牌,却出了主点数 %d", c.MainRank)
}
}
// TestFarmerFinishOverridesCoop 能一手出完时即使是队友的牌也直接出完取胜
func TestFarmerFinishOverridesCoop(t *testing.T) {
a := &ruleDdzAI{difficulty: DiffMedium, name: "测试"}
hand := []int{card(14, 0)} // 只剩一张 A
last := ddz.Parse([]int{card(11, 0)}) // 队友出单张 J
pick, _ := a.DecidePlay(hand, last, PlayContext{
IsLandlord: false, LastIsPartner: true,
PartnerCards: 3, LandlordCards: 9, OppMin: 9,
})
if len(pick) != 1 {
t.Fatalf("能直接出完应立即取胜,实际出牌 %v", pick)
}
}
// TestFarmerFeedsPartnerSingle 队友只剩 1 张时,平民自由出牌应喂最小单张
func TestFarmerFeedsPartnerSingle(t *testing.T) {
a := &ruleDdzAI{difficulty: DiffMedium, name: "测试"}
hand := []int{card(15, 0), card(12, 0), card(12, 1), card(3, 0)}
pick, _ := a.DecidePlay(hand, nil, PlayContext{
IsLandlord: false, LastIsPartner: false,
PartnerCards: 1, LandlordCards: 8, OppMin: 8,
})
c := ddz.Parse(pick)
if c == nil || c.Type != ddz.ComboSingle {
t.Fatalf("队友剩 1 张应喂单张,实际 %v", pick)
}
if c.MainRank != 3 {
t.Fatalf("应喂最小单张 3实际主点数 %d", c.MainRank)
}
}
// TestFarmerFeedsPartnerPair 队友只剩 2 张时优先喂最小对子
func TestFarmerFeedsPartnerPair(t *testing.T) {
a := &ruleDdzAI{difficulty: DiffMedium, name: "测试"}
hand := []int{card(15, 0), card(9, 0), card(9, 1), card(4, 0), card(4, 1)}
pick, _ := a.DecidePlay(hand, nil, PlayContext{
IsLandlord: false, LastIsPartner: false,
PartnerCards: 2, LandlordCards: 8, OppMin: 8,
})
c := ddz.Parse(pick)
if c == nil || c.Type != ddz.ComboPair {
t.Fatalf("队友剩 2 张应喂对子,实际 %v", pick)
}
if c.MainRank != 4 {
t.Fatalf("应喂最小对子 4实际主点数 %d", c.MainRank)
}
}
// TestFarmerBlocksLandlordEndgame 地主只剩 1 张时,平民领出应避免送小单牌
func TestFarmerBlocksLandlordEndgame(t *testing.T) {
a := &ruleDdzAI{difficulty: DiffMedium, name: "测试"}
// 手牌:小单 3、对 7、大单 A
hand := []int{card(14, 0), card(7, 0), card(7, 1), card(3, 0)}
pick, _ := a.DecidePlay(hand, nil, PlayContext{
IsLandlord: false, LastIsPartner: false,
PartnerCards: 10, LandlordCards: 1, OppMin: 1,
})
c := ddz.Parse(pick)
if c == nil {
t.Fatal("应有出牌")
}
// 不应送出最小单张 3地主最后一张很可能压过
if c.Type == ddz.ComboSingle && c.MainRank == 3 {
t.Fatal("地主只剩 1 张时不应送最小单牌")
}
}
// TestLandlordUnchanged 地主视角:正常出能压住的最小牌,不受配合逻辑影响
func TestLandlordUnchanged(t *testing.T) {
a := &ruleDdzAI{difficulty: DiffMedium, name: "测试"}
hand := []int{card(15, 0), card(10, 0), card(6, 0)}
last := ddz.Parse([]int{card(5, 0)})
pick, _ := a.DecidePlay(hand, last, PlayContext{
IsLandlord: true, LandlordCards: 3, OppMin: 10,
})
c := ddz.Parse(pick)
if c == nil || c.MainRank != 6 {
t.Fatalf("地主应出能压住的最小牌 6实际 %v", pick)
}
}