Skip to content

Commit 0ebc5fc

Browse files
perf(gb): allocate only the selected discard choice (#83)
Co-authored-by: Arbousier1 <elderli@foxmail.com>
1 parent 0649bba commit 0ebc5fc

2 files changed

Lines changed: 68 additions & 5 deletions

File tree

src/main/java/top/ellan/mahjong/table/core/round/GbBotDecisionService.java

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,9 @@ private DiscardChoice bestDiscardChoice(
185185
if (hand == null || hand.isEmpty()) {
186186
return null;
187187
}
188-
DiscardChoice best = null;
188+
int bestIndex = -1;
189+
long bestReadyScore = 0;
190+
int bestDiscardPreference = 0;
189191
EnumMap<MahjongTile, GbTingResponse> tingMemo = new EnumMap<>(MahjongTile.class);
190192
MahjongTile previousDiscarded = null;
191193
int previousDiscardPreference = 0;
@@ -205,14 +207,17 @@ private DiscardChoice bestDiscardChoice(
205207
int candidateDiscardPreference = tingMemoHit && discarded == previousDiscarded
206208
? previousDiscardPreference
207209
: discardPreference(hand, discarded);
208-
DiscardChoice candidate = new DiscardChoice(i, candidateReadyScore, candidateDiscardPreference);
209210
previousDiscarded = discarded;
210211
previousDiscardPreference = candidateDiscardPreference;
211-
if (best == null || candidate.compareTo(best) > 0) {
212-
best = candidate;
212+
if (bestIndex < 0
213+
|| candidateReadyScore > bestReadyScore
214+
|| (candidateReadyScore == bestReadyScore && candidateDiscardPreference > bestDiscardPreference)) {
215+
bestIndex = i;
216+
bestReadyScore = candidateReadyScore;
217+
bestDiscardPreference = candidateDiscardPreference;
213218
}
214219
}
215-
return best;
220+
return bestIndex < 0 ? null : new DiscardChoice(bestIndex, bestReadyScore, bestDiscardPreference);
216221
}
217222

218223
private BotState simulateKan(List<MahjongTile> sourceHand, List<GbMeldState> sourceMelds, MahjongTile target) {

src/test/kotlin/top/ellan/mahjong/table/core/round/GbBotDecisionServiceTest.kt

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,64 @@ class GbBotDecisionServiceTest {
4545
assertEquals(3, suggestedIndex)
4646
}
4747

48+
@Test
49+
fun `discard suggestion compares ready score before discard preference`() {
50+
val service = GbBotDecisionService(8)
51+
val hand = listOf(MahjongTile.EAST, MahjongTile.M2, MahjongTile.M3)
52+
val ready = GbTingResponse(true, listOf(GbTingCandidate("W1", 8)), null)
53+
assertTrue(
54+
GbBotDecisionService.discardPreference(hand, MahjongTile.EAST) >
55+
GbBotDecisionService.discardPreference(hand, MahjongTile.M2),
56+
)
57+
58+
val suggestedIndex =
59+
service.suggestedDiscardIndex(hand, emptyList()) { remaining, _ ->
60+
if (MahjongTile.M2 !in remaining) {
61+
ready
62+
} else {
63+
GbTingResponse(true, emptyList(), null)
64+
}
65+
}
66+
67+
assertEquals(1, suggestedIndex)
68+
}
69+
70+
@Test
71+
fun `discard suggestion compares discard preference after equal ready scores`() {
72+
val service = GbBotDecisionService(8)
73+
val hand = listOf(MahjongTile.M2, MahjongTile.M3, MahjongTile.EAST)
74+
75+
val suggestedIndex =
76+
service.suggestedDiscardIndex(hand, emptyList()) { _, _ ->
77+
GbTingResponse(true, emptyList(), null)
78+
}
79+
80+
assertEquals(2, suggestedIndex)
81+
}
82+
83+
@Test
84+
fun `discard suggestion evaluates unique candidates in hand order`() {
85+
val service = GbBotDecisionService(8)
86+
val hand = listOf(MahjongTile.M1, MahjongTile.P1, MahjongTile.S1)
87+
val evaluatedHands = mutableListOf<List<MahjongTile>>()
88+
89+
val suggestedIndex =
90+
service.suggestedDiscardIndex(hand, emptyList()) { remaining, _ ->
91+
evaluatedHands += remaining.toList()
92+
GbTingResponse(true, emptyList(), null)
93+
}
94+
95+
assertEquals(0, suggestedIndex)
96+
assertEquals(
97+
listOf(
98+
listOf(MahjongTile.P1, MahjongTile.S1),
99+
listOf(MahjongTile.M1, MahjongTile.S1),
100+
listOf(MahjongTile.M1, MahjongTile.P1),
101+
),
102+
evaluatedHands,
103+
)
104+
}
105+
48106
@Test
49107
fun `discard suggestion evaluates the first remaining hand once per duplicated tile`() {
50108
val service = GbBotDecisionService(8)

0 commit comments

Comments
 (0)