Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -187,9 +187,12 @@ private DiscardChoice bestDiscardChoice(
}
DiscardChoice best = null;
EnumMap<MahjongTile, GbTingResponse> tingMemo = new EnumMap<>(MahjongTile.class);
MahjongTile previousDiscarded = null;
int previousDiscardPreference = 0;
for (int i = 0; i < hand.size(); i++) {
MahjongTile discarded = hand.get(i);
GbTingResponse ting = tingMemo.get(discarded);
boolean tingMemoHit = ting != null;
if (ting == null) {
List<MahjongTile> remaining = new ArrayList<>(hand);
remaining.remove(i);
Expand All @@ -198,7 +201,13 @@ private DiscardChoice bestDiscardChoice(
tingMemo.put(discarded, ting);
}
}
DiscardChoice candidate = new DiscardChoice(i, readyScore(ting), discardPreference(hand, discarded));
long candidateReadyScore = readyScore(ting);
int candidateDiscardPreference = tingMemoHit && discarded == previousDiscarded
? previousDiscardPreference
: discardPreference(hand, discarded);
DiscardChoice candidate = new DiscardChoice(i, candidateReadyScore, candidateDiscardPreference);
previousDiscarded = discarded;
previousDiscardPreference = candidateDiscardPreference;
if (best == null || candidate.compareTo(best) > 0) {
best = candidate;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,56 @@ class GbBotDecisionServiceTest {
)
}

@Test
fun `discard suggestion keeps the earliest index for equal duplicate candidates`() {
val service = GbBotDecisionService(8)
val hand = listOf(MahjongTile.M1, MahjongTile.M1, MahjongTile.M1)
var evaluations = 0

val suggestedIndex =
service.suggestedDiscardIndex(hand, emptyList()) { _, _ ->
evaluations++
GbTingResponse(true, emptyList(), null)
}

assertEquals(0, suggestedIndex)
assertEquals(1, evaluations)
}

@Test
fun `discard suggestion recomputes null before memoizing a later duplicate response`() {
val service = GbBotDecisionService(8)
val hand = listOf(MahjongTile.M1, MahjongTile.M1, MahjongTile.M1)
val ready = GbTingResponse(true, listOf(GbTingCandidate("W1", 8)), null)
var evaluations = 0

val suggestedIndex =
service.suggestedDiscardIndex(hand, emptyList()) { _, _ ->
evaluations++
if (evaluations == 1) null else ready
}

assertEquals(1, suggestedIndex)
assertEquals(2, evaluations)
}

@Test
fun `discard suggestion does not reuse red five preference for the exact regular tile`() {
val service = GbBotDecisionService(8)
val hand = listOf(MahjongTile.M5, MahjongTile.M5_RED, MahjongTile.M5)
val ready = GbTingResponse(true, listOf(GbTingCandidate("W1", 8)), null)
var evaluations = 0

val suggestedIndex =
service.suggestedDiscardIndex(hand, emptyList()) { remaining, _ ->
evaluations++
if (MahjongTile.M5_RED in remaining) ready else GbTingResponse(true, emptyList(), null)
}

assertEquals(0, suggestedIndex)
assertEquals(2, evaluations)
}

@Test
fun `discard suggestion preserves null evaluator recomputation`() {
val service = GbBotDecisionService(8)
Expand Down
Loading