feat(计分): 实现血战计分V1核心逻辑

- 新增血战计分服务,支持七对、清一色等基础番型及杠上花等特殊加番
- 扩展结算结果结构,包含番型明细与支付分数计算
- 新增PostGangContext记录杠后补摸窗口,用于判断杠上花/杠上炮
- 完善胡牌判定器,新增七对和对对胡识别方法
- 更新开发计划文档,补充注释规范要求
- 添加计分相关单元测试,确保核心逻辑正确性
This commit is contained in:
hujun
2026-03-20 14:50:19 +08:00
parent d038a8732d
commit 34809fd0f3
17 changed files with 804 additions and 29 deletions

View File

@@ -2,6 +2,8 @@ package com.xuezhanmaster.game.event;
import com.xuezhanmaster.game.domain.ActionType;
import com.xuezhanmaster.game.domain.ScoreChange;
import com.xuezhanmaster.game.domain.SettlementDetail;
import com.xuezhanmaster.game.domain.SettlementFan;
import com.xuezhanmaster.game.domain.SettlementResult;
import com.xuezhanmaster.game.domain.SettlementType;
import org.junit.jupiter.api.Test;
@@ -54,6 +56,12 @@ class GameEventTest {
2,
0,
"9筒",
new SettlementDetail(
1,
2,
4,
List.of(new SettlementFan("QING_YI_SE", "清一色", 2))
),
List.of(
new ScoreChange(2, 1, 3),
new ScoreChange(0, -1, -2)
@@ -69,7 +77,12 @@ class GameEventTest {
.containsEntry("actionType", "HU")
.containsEntry("sourceSeatNo", 0)
.containsEntry("triggerTile", "9筒");
assertThat(settlementApplied.payload()).containsKey("settlementDetail");
assertThat(settlementApplied.payload()).containsKey("scoreChanges");
assertThat((java.util.Map<String, Object>) settlementApplied.payload().get("settlementDetail"))
.containsEntry("baseScore", 1)
.containsEntry("totalFan", 2)
.containsEntry("paymentScore", 4);
assertThat(scoreChanged.eventType()).isEqualTo(GameEventType.SCORE_CHANGED);
assertThat(scoreChanged.payload())

View File

@@ -0,0 +1,128 @@
package com.xuezhanmaster.game.service;
import com.xuezhanmaster.game.domain.GameSeat;
import com.xuezhanmaster.game.domain.SettlementDetail;
import com.xuezhanmaster.game.domain.Tile;
import com.xuezhanmaster.game.domain.TileSuit;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
class BloodBattleScoringServiceTest {
private final HuEvaluator huEvaluator = new HuEvaluator();
private final BloodBattleScoringService scoringService = new BloodBattleScoringService(huEvaluator);
@Test
void shouldEvaluateQingQiDuiWithGen() {
GameSeat winnerSeat = new GameSeat(0, false, "user-1", "玩家一");
winnerSeat.receiveTile(new Tile(TileSuit.WAN, 1));
winnerSeat.receiveTile(new Tile(TileSuit.WAN, 1));
winnerSeat.receiveTile(new Tile(TileSuit.WAN, 1));
winnerSeat.receiveTile(new Tile(TileSuit.WAN, 1));
winnerSeat.receiveTile(new Tile(TileSuit.WAN, 2));
winnerSeat.receiveTile(new Tile(TileSuit.WAN, 2));
winnerSeat.receiveTile(new Tile(TileSuit.WAN, 3));
winnerSeat.receiveTile(new Tile(TileSuit.WAN, 3));
winnerSeat.receiveTile(new Tile(TileSuit.WAN, 4));
winnerSeat.receiveTile(new Tile(TileSuit.WAN, 4));
winnerSeat.receiveTile(new Tile(TileSuit.WAN, 5));
winnerSeat.receiveTile(new Tile(TileSuit.WAN, 5));
winnerSeat.receiveTile(new Tile(TileSuit.WAN, 6));
winnerSeat.receiveTile(new Tile(TileSuit.WAN, 6));
SettlementDetail settlementDetail = scoringService.buildSelfDrawHuDetail(winnerSeat, false, false);
assertThat(settlementDetail.totalFan()).isEqualTo(5);
assertThat(settlementDetail.paymentScore()).isEqualTo(32);
assertThat(settlementDetail.fans())
.extracting(fan -> fan.code())
.containsExactly("QI_DUI", "QING_YI_SE", "GEN");
}
@Test
void shouldEvaluateJinGouDiaoAndRobbingGangHu() {
GameSeat winnerSeat = new GameSeat(0, false, "user-1", "玩家一");
winnerSeat.addPengMeld(new Tile(TileSuit.WAN, 1), 1);
winnerSeat.addPengMeld(new Tile(TileSuit.WAN, 2), 1);
winnerSeat.addMingGangMeld(new Tile(TileSuit.WAN, 3), 1);
winnerSeat.addAnGangMeld(new Tile(TileSuit.WAN, 4));
winnerSeat.receiveTile(new Tile(TileSuit.WAN, 9));
winnerSeat.receiveTile(new Tile(TileSuit.WAN, 9));
SettlementDetail settlementDetail = scoringService.buildRobbingGangHuDetail(winnerSeat);
assertThat(settlementDetail.totalFan()).isEqualTo(7);
assertThat(settlementDetail.paymentScore()).isEqualTo(128);
assertThat(settlementDetail.fans())
.extracting(fan -> fan.code())
.containsExactly("DUI_DUI_HU", "JIN_GOU_DIAO", "QING_YI_SE", "GEN", "GEN", "QIANG_GANG_HU");
}
@Test
void shouldExposeFixedGangScores() {
assertThat(scoringService.buildExposedGangDetail().paymentScore()).isEqualTo(2);
assertThat(scoringService.buildSupplementalGangDetail().paymentScore()).isEqualTo(1);
assertThat(scoringService.buildConcealedGangDetail().paymentScore()).isEqualTo(2);
}
@Test
void shouldAddGangShangHuaAndGangShangPaoFans() {
GameSeat winnerSeat = new GameSeat(0, false, "user-1", "玩家一");
winnerSeat.receiveTile(new Tile(TileSuit.WAN, 1));
winnerSeat.receiveTile(new Tile(TileSuit.WAN, 1));
winnerSeat.receiveTile(new Tile(TileSuit.WAN, 1));
winnerSeat.receiveTile(new Tile(TileSuit.WAN, 2));
winnerSeat.receiveTile(new Tile(TileSuit.WAN, 3));
winnerSeat.receiveTile(new Tile(TileSuit.WAN, 4));
winnerSeat.receiveTile(new Tile(TileSuit.TONG, 2));
winnerSeat.receiveTile(new Tile(TileSuit.TONG, 3));
winnerSeat.receiveTile(new Tile(TileSuit.TONG, 4));
winnerSeat.receiveTile(new Tile(TileSuit.TIAO, 5));
winnerSeat.receiveTile(new Tile(TileSuit.TIAO, 6));
winnerSeat.receiveTile(new Tile(TileSuit.TIAO, 7));
winnerSeat.receiveTile(new Tile(TileSuit.TONG, 9));
winnerSeat.receiveTile(new Tile(TileSuit.TONG, 9));
SettlementDetail gangShangHua = scoringService.buildSelfDrawHuDetail(winnerSeat, true, false);
SettlementDetail gangShangPao = scoringService.buildDiscardHuDetail(winnerSeat, true, false);
assertThat(gangShangHua.totalFan()).isEqualTo(1);
assertThat(gangShangHua.paymentScore()).isEqualTo(2);
assertThat(gangShangHua.fans()).extracting(fan -> fan.code()).containsExactly("GANG_SHANG_HUA");
assertThat(gangShangPao.totalFan()).isEqualTo(1);
assertThat(gangShangPao.paymentScore()).isEqualTo(2);
assertThat(gangShangPao.fans()).extracting(fan -> fan.code()).containsExactly("GANG_SHANG_PAO");
}
@Test
void shouldAddHaiDiFans() {
GameSeat winnerSeat = new GameSeat(0, false, "user-1", "玩家一");
winnerSeat.receiveTile(new Tile(TileSuit.WAN, 1));
winnerSeat.receiveTile(new Tile(TileSuit.WAN, 1));
winnerSeat.receiveTile(new Tile(TileSuit.WAN, 1));
winnerSeat.receiveTile(new Tile(TileSuit.WAN, 2));
winnerSeat.receiveTile(new Tile(TileSuit.WAN, 3));
winnerSeat.receiveTile(new Tile(TileSuit.WAN, 4));
winnerSeat.receiveTile(new Tile(TileSuit.TONG, 2));
winnerSeat.receiveTile(new Tile(TileSuit.TONG, 3));
winnerSeat.receiveTile(new Tile(TileSuit.TONG, 4));
winnerSeat.receiveTile(new Tile(TileSuit.TIAO, 5));
winnerSeat.receiveTile(new Tile(TileSuit.TIAO, 6));
winnerSeat.receiveTile(new Tile(TileSuit.TIAO, 7));
winnerSeat.receiveTile(new Tile(TileSuit.TONG, 9));
winnerSeat.receiveTile(new Tile(TileSuit.TONG, 9));
SettlementDetail haiDiLaoYue = scoringService.buildSelfDrawHuDetail(winnerSeat, false, true);
SettlementDetail haiDiPao = scoringService.buildDiscardHuDetail(winnerSeat, false, true);
assertThat(haiDiLaoYue.totalFan()).isEqualTo(1);
assertThat(haiDiLaoYue.paymentScore()).isEqualTo(2);
assertThat(haiDiLaoYue.fans()).extracting(fan -> fan.code()).containsExactly("HAI_DI_LAO_YUE");
assertThat(haiDiPao.totalFan()).isEqualTo(1);
assertThat(haiDiPao.paymentScore()).isEqualTo(2);
assertThat(haiDiPao.fans()).extracting(fan -> fan.code()).containsExactly("HAI_DI_PAO");
}
}

View File

@@ -34,13 +34,14 @@ class GameSessionServiceTest {
private final RoomService roomService = new RoomService();
private final HuEvaluator huEvaluator = new HuEvaluator();
private final BloodBattleScoringService bloodBattleScoringService = new BloodBattleScoringService(huEvaluator);
private final GameSessionService gameSessionService = new GameSessionService(
roomService,
new GameEngine(new DeckFactory()),
new GameActionProcessor(huEvaluator),
new ResponseActionWindowBuilder(huEvaluator),
new ResponseActionResolver(),
new SettlementService(),
new SettlementService(bloodBattleScoringService),
huEvaluator,
new StrategyService(),
new PlayerVisibilityService(),
@@ -255,8 +256,8 @@ class GameSessionServiceTest {
);
assertThat(afterGang.currentSeatNo()).isEqualTo(1);
assertThat(afterGang.selfSeat().score()).isEqualTo(1);
assertThat(afterGang.seats().get(0).score()).isEqualTo(-1);
assertThat(afterGang.selfSeat().score()).isEqualTo(2);
assertThat(afterGang.seats().get(0).score()).isEqualTo(-2);
assertThat(afterGang.selfSeat().melds()).containsExactly("明杠:" + discardTile);
assertThat(session.getEvents())
.extracting(event -> event.eventType())
@@ -420,17 +421,56 @@ class GameSessionServiceTest {
assertThat(afterGang.selfSeat().handTiles()).hasSize(11);
}
@Test
void shouldScoreGangShangHuaAfterReplacementDrawHu() {
RoomSummaryResponse room = roomService.createRoom(new CreateRoomRequest("host-1", true));
roomService.joinRoom(room.roomId(), new com.xuezhanmaster.room.dto.JoinRoomRequest("player-2", "玩家二"));
roomService.joinRoom(room.roomId(), new com.xuezhanmaster.room.dto.JoinRoomRequest("player-3", "玩家三"));
roomService.joinRoom(room.roomId(), new com.xuezhanmaster.room.dto.JoinRoomRequest("player-4", "玩家四"));
roomService.toggleReady(room.roomId(), new ToggleReadyRequest("host-1", true));
roomService.toggleReady(room.roomId(), new ToggleReadyRequest("player-2", true));
roomService.toggleReady(room.roomId(), new ToggleReadyRequest("player-3", true));
roomService.toggleReady(room.roomId(), new ToggleReadyRequest("player-4", true));
GameStateResponse started = gameSessionService.startGame(room.roomId(), "host-1");
gameSessionService.selectLackSuit(started.gameId(), "host-1", TileSuit.WAN.name());
gameSessionService.selectLackSuit(started.gameId(), "player-2", TileSuit.TONG.name());
gameSessionService.selectLackSuit(started.gameId(), "player-3", TileSuit.TIAO.name());
gameSessionService.selectLackSuit(started.gameId(), "player-4", TileSuit.WAN.name());
GameSession session = getSession(started.gameId());
prepareGangShangHuaHand(session.getTable().getSeats().get(0), "3万");
setNextWallTile(session, "9筒");
GameStateResponse afterGang = gameSessionService.performAction(
started.gameId(),
new GameActionRequest("host-1", "GANG", "3万", null)
);
GameStateResponse afterHu = gameSessionService.performAction(
started.gameId(),
new GameActionRequest("host-1", "HU", null, null)
);
assertThat(afterGang.currentSeatNo()).isEqualTo(0);
assertThat(afterHu.selfSeat().won()).isTrue();
assertThat(afterHu.selfSeat().score()).isEqualTo(12);
assertThat(afterHu.seats()).extracting(seat -> seat.score()).contains(12, -4, -4, -4);
assertThat(latestSettlementFanCodes(session)).contains("GANG_SHANG_HUA");
}
@Test
void shouldRejectSelfConcealedGangWithoutFourMatchingTiles() {
RoomSummaryResponse room = roomService.createRoom(new CreateRoomRequest("host-1", true));
roomService.toggleReady(room.roomId(), new ToggleReadyRequest("host-1", true));
GameStateResponse started = gameSessionService.startGame(room.roomId(), "host-1");
gameSessionService.selectLackSuit(started.gameId(), "host-1", TileSuit.WAN.name());
GameStateResponse afterLack = gameSessionService.selectLackSuit(started.gameId(), "host-1", TileSuit.WAN.name());
GameSession session = getSession(started.gameId());
String invalidGangTile = findTileWithLessThanFourCopies(session.getTable().getSeats().get(0));
assertThatThrownBy(() -> gameSessionService.performAction(
started.gameId(),
new GameActionRequest("host-1", "GANG", "3万", null)
afterLack.gameId(),
new GameActionRequest("host-1", "GANG", invalidGangTile, null)
))
.isInstanceOf(BusinessException.class)
.extracting(throwable -> ((BusinessException) throwable).getCode())
@@ -504,11 +544,106 @@ class GameSessionServiceTest {
assertThat(afterHu.phase()).isEqualTo("PLAYING");
assertThat(afterHu.currentSeatNo()).isEqualTo(2);
assertThat(afterHu.selfSeat().won()).isTrue();
assertThat(afterHu.selfSeat().score()).isEqualTo(1);
assertThat(afterHu.seats().get(0).score()).isEqualTo(-1);
assertThat(afterHu.selfSeat().score()).isEqualTo(2);
assertThat(afterHu.seats().get(0).score()).isEqualTo(-2);
assertThat(afterHu.seats().get(0).melds()).containsExactly("碰:9筒");
}
@Test
void shouldScoreGangShangPaoOnDiscardAfterReplacementDraw() {
RoomSummaryResponse room = roomService.createRoom(new CreateRoomRequest("host-1", true));
roomService.joinRoom(room.roomId(), new com.xuezhanmaster.room.dto.JoinRoomRequest("player-2", "玩家二"));
roomService.toggleReady(room.roomId(), new ToggleReadyRequest("host-1", true));
roomService.toggleReady(room.roomId(), new ToggleReadyRequest("player-2", true));
GameStateResponse started = gameSessionService.startGame(room.roomId(), "host-1");
gameSessionService.selectLackSuit(started.gameId(), "host-1", TileSuit.WAN.name());
gameSessionService.selectLackSuit(started.gameId(), "player-2", TileSuit.TONG.name());
GameSession session = getSession(started.gameId());
prepareGangShangPaoHand(session.getTable().getSeats().get(0), "3万");
prepareWinningHand(session.getTable().getSeats().get(1));
removeMatchingTilesFromOtherSeats(session, "9筒", 0, 1);
setNextWallTile(session, "8条");
GameStateResponse afterGang = gameSessionService.performAction(
started.gameId(),
new GameActionRequest("host-1", "GANG", "3万", null)
);
GameStateResponse afterHu = gameSessionService.discardTile(started.gameId(), "host-1", "9筒");
afterHu = gameSessionService.performAction(
started.gameId(),
new GameActionRequest("player-2", "HU", "9筒", 0)
);
assertThat(afterGang.currentSeatNo()).isEqualTo(0);
assertThat(afterHu.selfSeat().won()).isTrue();
assertThat(afterHu.selfSeat().score()).isEqualTo(0);
assertThat(afterHu.seats().get(0).score()).isEqualTo(4);
assertThat(latestSettlementFanCodes(session)).contains("GANG_SHANG_PAO");
}
@Test
void shouldScoreHaiDiLaoYueOnLastTileSelfDraw() {
RoomSummaryResponse room = roomService.createRoom(new CreateRoomRequest("host-1", true));
roomService.joinRoom(room.roomId(), new com.xuezhanmaster.room.dto.JoinRoomRequest("player-2", "玩家二"));
roomService.joinRoom(room.roomId(), new com.xuezhanmaster.room.dto.JoinRoomRequest("player-3", "玩家三"));
roomService.joinRoom(room.roomId(), new com.xuezhanmaster.room.dto.JoinRoomRequest("player-4", "玩家四"));
roomService.toggleReady(room.roomId(), new ToggleReadyRequest("host-1", true));
roomService.toggleReady(room.roomId(), new ToggleReadyRequest("player-2", true));
roomService.toggleReady(room.roomId(), new ToggleReadyRequest("player-3", true));
roomService.toggleReady(room.roomId(), new ToggleReadyRequest("player-4", true));
GameStateResponse started = gameSessionService.startGame(room.roomId(), "host-1");
gameSessionService.selectLackSuit(started.gameId(), "host-1", TileSuit.WAN.name());
gameSessionService.selectLackSuit(started.gameId(), "player-2", TileSuit.TONG.name());
gameSessionService.selectLackSuit(started.gameId(), "player-3", TileSuit.TIAO.name());
gameSessionService.selectLackSuit(started.gameId(), "player-4", TileSuit.WAN.name());
GameSession session = getSession(started.gameId());
prepareSelfDrawWinningHand(session.getTable().getSeats().get(0));
session.getTable().getWallTiles().clear();
GameStateResponse afterHu = gameSessionService.performAction(
started.gameId(),
new GameActionRequest("host-1", "HU", null, null)
);
assertThat(afterHu.selfSeat().won()).isTrue();
assertThat(afterHu.selfSeat().score()).isEqualTo(6);
assertThat(afterHu.seats()).extracting(seat -> seat.score()).contains(6, -2, -2, -2);
assertThat(latestSettlementFanCodes(session)).contains("HAI_DI_LAO_YUE");
}
@Test
void shouldScoreHaiDiPaoOnLastTileDiscardHu() {
RoomSummaryResponse room = roomService.createRoom(new CreateRoomRequest("host-1", true));
roomService.joinRoom(room.roomId(), new com.xuezhanmaster.room.dto.JoinRoomRequest("player-2", "玩家二"));
roomService.toggleReady(room.roomId(), new ToggleReadyRequest("host-1", true));
roomService.toggleReady(room.roomId(), new ToggleReadyRequest("player-2", true));
GameStateResponse started = gameSessionService.startGame(room.roomId(), "host-1");
gameSessionService.selectLackSuit(started.gameId(), "host-1", TileSuit.WAN.name());
gameSessionService.selectLackSuit(started.gameId(), "player-2", TileSuit.TONG.name());
GameSession session = getSession(started.gameId());
prepareWinningHand(session.getTable().getSeats().get(1));
ensureSeatHasMatchingTiles(session.getTable().getSeats().get(0), "9筒", 1);
removeMatchingTilesFromOtherSeats(session, "9筒", 0, 1);
session.getTable().getWallTiles().clear();
gameSessionService.discardTile(started.gameId(), "host-1", "9筒");
GameStateResponse afterHu = gameSessionService.performAction(
started.gameId(),
new GameActionRequest("player-2", "HU", "9筒", 0)
);
assertThat(afterHu.selfSeat().won()).isTrue();
assertThat(afterHu.selfSeat().score()).isEqualTo(2);
assertThat(afterHu.seats().get(0).score()).isEqualTo(-2);
assertThat(latestSettlementFanCodes(session)).contains("HAI_DI_PAO");
}
@Test
void shouldRejectSelfDrawHuWhenHandIsNotWinning() {
RoomSummaryResponse room = roomService.createRoom(new CreateRoomRequest("host-1", true));
@@ -575,6 +710,21 @@ class GameSessionServiceTest {
};
}
private String findTileWithLessThanFourCopies(GameSeat seat) {
for (Tile tile : seat.getHandTiles()) {
int matchCount = 0;
for (Tile handTile : seat.getHandTiles()) {
if (handTile.equals(tile)) {
matchCount++;
}
}
if (matchCount < 4) {
return tile.getDisplayName();
}
}
throw new IllegalStateException("测试手牌意外全部为四张同牌");
}
private void prepareWinningHand(GameSeat seat) {
seat.getHandTiles().clear();
seat.receiveTile(new Tile(TileSuit.WAN, 1));
@@ -628,6 +778,42 @@ class GameSessionServiceTest {
seat.receiveTile(new Tile(TileSuit.WAN, 9));
}
private void prepareGangShangHuaHand(GameSeat seat, String gangTileDisplayName) {
seat.getHandTiles().clear();
Tile gangTile = parseTile(gangTileDisplayName);
for (int i = 0; i < 4; i++) {
seat.receiveTile(gangTile);
}
seat.receiveTile(new Tile(TileSuit.WAN, 1));
seat.receiveTile(new Tile(TileSuit.WAN, 1));
seat.receiveTile(new Tile(TileSuit.WAN, 1));
seat.receiveTile(new Tile(TileSuit.WAN, 2));
seat.receiveTile(new Tile(TileSuit.WAN, 3));
seat.receiveTile(new Tile(TileSuit.WAN, 4));
seat.receiveTile(new Tile(TileSuit.TONG, 2));
seat.receiveTile(new Tile(TileSuit.TONG, 3));
seat.receiveTile(new Tile(TileSuit.TONG, 4));
seat.receiveTile(new Tile(TileSuit.TONG, 9));
}
private void prepareGangShangPaoHand(GameSeat seat, String gangTileDisplayName) {
seat.getHandTiles().clear();
Tile gangTile = parseTile(gangTileDisplayName);
for (int i = 0; i < 4; i++) {
seat.receiveTile(gangTile);
}
seat.receiveTile(new Tile(TileSuit.WAN, 1));
seat.receiveTile(new Tile(TileSuit.WAN, 1));
seat.receiveTile(new Tile(TileSuit.WAN, 1));
seat.receiveTile(new Tile(TileSuit.WAN, 2));
seat.receiveTile(new Tile(TileSuit.WAN, 3));
seat.receiveTile(new Tile(TileSuit.WAN, 4));
seat.receiveTile(new Tile(TileSuit.TONG, 2));
seat.receiveTile(new Tile(TileSuit.TONG, 3));
seat.receiveTile(new Tile(TileSuit.TONG, 4));
seat.receiveTile(new Tile(TileSuit.TONG, 9));
}
private void prepareSupplementalGangHand(GameSeat seat, String gangTileDisplayName) {
seat.getHandTiles().clear();
seat.getMeldGroups().clear();
@@ -645,4 +831,21 @@ class GameSessionServiceTest {
seat.receiveTile(new Tile(TileSuit.TIAO, 9));
seat.receiveTile(new Tile(TileSuit.WAN, 9));
}
private void setNextWallTile(GameSession session, String tileDisplayName) {
session.getTable().getWallTiles().set(0, parseTile(tileDisplayName));
}
@SuppressWarnings("unchecked")
private java.util.List<String> latestSettlementFanCodes(GameSession session) {
return session.getEvents().stream()
.filter(event -> event.eventType() == GameEventType.SETTLEMENT_APPLIED)
.reduce((first, second) -> second)
.map(event -> (Map<String, Object>) event.payload().get("settlementDetail"))
.map(detail -> (java.util.List<Map<String, Object>>) detail.get("fans"))
.map(fans -> fans.stream()
.map(item -> String.valueOf(item.get("code")))
.toList())
.orElseGet(java.util.List::of);
}
}

View File

@@ -59,4 +59,26 @@ class HuEvaluatorTest {
assertThat(result).isFalse();
}
@Test
void shouldRecognizeSevenPairsWinningHand() {
boolean result = huEvaluator.canHu(List.of(
new Tile(TileSuit.WAN, 1),
new Tile(TileSuit.WAN, 1),
new Tile(TileSuit.WAN, 2),
new Tile(TileSuit.WAN, 2),
new Tile(TileSuit.WAN, 3),
new Tile(TileSuit.WAN, 3),
new Tile(TileSuit.TONG, 4),
new Tile(TileSuit.TONG, 4),
new Tile(TileSuit.TONG, 5),
new Tile(TileSuit.TONG, 5),
new Tile(TileSuit.TIAO, 6),
new Tile(TileSuit.TIAO, 6),
new Tile(TileSuit.TIAO, 9),
new Tile(TileSuit.TIAO, 9)
));
assertThat(result).isTrue();
}
}