feat(计分): 实现血战计分V1核心逻辑
- 新增血战计分服务,支持七对、清一色等基础番型及杠上花等特殊加番 - 扩展结算结果结构,包含番型明细与支付分数计算 - 新增PostGangContext记录杠后补摸窗口,用于判断杠上花/杠上炮 - 完善胡牌判定器,新增七对和对对胡识别方法 - 更新开发计划文档,补充注释规范要求 - 添加计分相关单元测试,确保核心逻辑正确性
This commit is contained in:
@@ -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())
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user