add Same/Same Wall/Plus rules logic.
This commit is contained in:
@@ -372,12 +372,14 @@ public class DuelTableBlockEntity extends BlockEntity {
|
||||
}
|
||||
|
||||
pendingAnimations.addLast(new AnimationStep(AnimationType.PLACEMENT, new int[] {moveResult.playedCell().index()}, owner));
|
||||
if (!moveResult.capturedCells().isEmpty()) {
|
||||
int[] captureSlots = moveResult.capturedCells().stream()
|
||||
for (List<BoardCell> captureWave : moveResult.captureWaves()) {
|
||||
int[] captureSlots = captureWave.stream()
|
||||
.mapToInt(BoardCell::index)
|
||||
.toArray();
|
||||
if (captureSlots.length > 0) {
|
||||
pendingAnimations.addLast(new AnimationStep(AnimationType.CAPTURE, captureSlots, owner));
|
||||
}
|
||||
}
|
||||
advanceAnimationStep();
|
||||
}
|
||||
|
||||
|
||||
@@ -7,21 +7,29 @@ import net.minecraft.resources.ResourceLocation;
|
||||
public record MoveResult(
|
||||
boolean valid,
|
||||
String errorMessage,
|
||||
List<List<BoardCell>> captureWaves,
|
||||
List<BoardCell> capturedCells,
|
||||
List<String> battleLog,
|
||||
String playedCardName,
|
||||
ResourceLocation playedCardId,
|
||||
BoardCell playedCell) {
|
||||
public static MoveResult success(
|
||||
List<BoardCell> capturedCells,
|
||||
List<List<BoardCell>> captureWaves,
|
||||
List<String> battleLog,
|
||||
String playedCardName,
|
||||
ResourceLocation playedCardId,
|
||||
BoardCell playedCell) {
|
||||
return new MoveResult(true, "", List.copyOf(capturedCells), List.copyOf(battleLog), playedCardName, playedCardId, playedCell);
|
||||
List<List<BoardCell>> immutableWaves = captureWaves.stream()
|
||||
.map(List::copyOf)
|
||||
.toList();
|
||||
List<BoardCell> flattenedCaptures = immutableWaves.stream()
|
||||
.flatMap(List::stream)
|
||||
.distinct()
|
||||
.toList();
|
||||
return new MoveResult(true, "", immutableWaves, flattenedCaptures, List.copyOf(battleLog), playedCardName, playedCardId, playedCell);
|
||||
}
|
||||
|
||||
public static MoveResult failure(String errorMessage) {
|
||||
return new MoveResult(false, errorMessage, List.of(), List.of(), "", null, null);
|
||||
return new MoveResult(false, errorMessage, List.of(), List.of(), List.of(), "", null, null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,11 @@ package com.trunksbomb.minetriad.game;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
public final class TriadMatch {
|
||||
private final MatchParticipant firstParticipant;
|
||||
@@ -75,14 +79,111 @@ public final class TriadMatch {
|
||||
board[move.targetCell().index()] = new PlacedCard(move.participant(), playedCard);
|
||||
|
||||
List<String> battleLog = new ArrayList<>();
|
||||
List<BoardCell> capturedCells = resolveCaptures(move.targetCell(), move.participant(), playedCard, battleLog);
|
||||
CaptureResolution captureResolution = resolveCaptures(move.targetCell(), move.participant(), playedCard, battleLog);
|
||||
activeParticipant = otherParticipant(move.participant());
|
||||
return MoveResult.success(capturedCells, battleLog, playedCard.definition().name().getString(), playedCard.definition().id(), move.targetCell());
|
||||
return MoveResult.success(captureResolution.captureWaves(), battleLog, playedCard.definition().name().getString(), playedCard.definition().id(), move.targetCell());
|
||||
}
|
||||
|
||||
private List<BoardCell> resolveCaptures(BoardCell origin, MatchParticipant owner, GameCard playedCard, List<String> battleLog) {
|
||||
List<BoardCell> captured = new ArrayList<>();
|
||||
private CaptureResolution resolveCaptures(BoardCell origin, MatchParticipant owner, GameCard playedCard, List<String> battleLog) {
|
||||
List<SideComparison> comparisons = buildComparisons(origin, owner, playedCard, battleLog);
|
||||
LinkedHashSet<BoardCell> firstWave = new LinkedHashSet<>();
|
||||
LinkedHashSet<BoardCell> comboSeeds = new LinkedHashSet<>();
|
||||
|
||||
for (SideComparison comparison : comparisons) {
|
||||
if (!comparison.neighbor().owner().equals(owner) && comparison.attackValue() > comparison.defendValue()) {
|
||||
firstWave.add(comparison.cell());
|
||||
}
|
||||
}
|
||||
|
||||
if (ruleSet.sameRule()) {
|
||||
List<SameCandidate> sameCandidates = buildSameCandidates(origin, playedCard, battleLog);
|
||||
long sameMatches = sameCandidates.stream().filter(SameCandidate::matches).count();
|
||||
if (sameMatches >= 2) {
|
||||
List<BoardCell> sameCaptures = sameCandidates.stream()
|
||||
.filter(SameCandidate::matches)
|
||||
.map(SameCandidate::cell)
|
||||
.filter(cell -> cell != null)
|
||||
.distinct()
|
||||
.filter(cell -> {
|
||||
PlacedCard placedCard = cardAt(cell);
|
||||
return placedCard != null && !placedCard.owner().equals(owner);
|
||||
})
|
||||
.toList();
|
||||
if (!sameCaptures.isEmpty()) {
|
||||
battleLog.add("Same triggers.");
|
||||
firstWave.addAll(sameCaptures);
|
||||
comboSeeds.addAll(sameCaptures);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ruleSet.plusRule()) {
|
||||
Map<Integer, List<SideComparison>> plusGroups = new LinkedHashMap<>();
|
||||
for (SideComparison comparison : comparisons) {
|
||||
int sum = comparison.attackValue() + comparison.defendValue();
|
||||
plusGroups.computeIfAbsent(sum, ignored -> new ArrayList<>()).add(comparison);
|
||||
}
|
||||
for (Map.Entry<Integer, List<SideComparison>> entry : plusGroups.entrySet()) {
|
||||
if (entry.getValue().size() < 2) {
|
||||
continue;
|
||||
}
|
||||
List<BoardCell> plusCaptures = entry.getValue().stream()
|
||||
.map(SideComparison::cell)
|
||||
.distinct()
|
||||
.filter(cell -> {
|
||||
PlacedCard placedCard = cardAt(cell);
|
||||
return placedCard != null && !placedCard.owner().equals(owner);
|
||||
})
|
||||
.toList();
|
||||
if (!plusCaptures.isEmpty()) {
|
||||
battleLog.add("Plus triggers on sum " + entry.getKey() + ".");
|
||||
firstWave.addAll(plusCaptures);
|
||||
comboSeeds.addAll(plusCaptures);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
List<List<BoardCell>> waves = new ArrayList<>();
|
||||
if (!firstWave.isEmpty()) {
|
||||
applyOwnership(owner, firstWave);
|
||||
waves.add(List.copyOf(firstWave));
|
||||
}
|
||||
|
||||
if (!comboSeeds.isEmpty()) {
|
||||
LinkedHashSet<BoardCell> comboWave = new LinkedHashSet<>();
|
||||
for (BoardCell comboCell : comboSeeds) {
|
||||
PlacedCard comboCard = cardAt(comboCell);
|
||||
if (comboCard == null || !comboCard.owner().equals(owner)) {
|
||||
continue;
|
||||
}
|
||||
for (CardSide side : CardSide.values()) {
|
||||
BoardCell neighborCell = neighbor(comboCell, side);
|
||||
if (neighborCell == null) {
|
||||
continue;
|
||||
}
|
||||
PlacedCard neighbor = cardAt(neighborCell);
|
||||
if (neighbor == null || neighbor.owner().equals(owner)) {
|
||||
continue;
|
||||
}
|
||||
int attackValue = comboCard.card().value(side);
|
||||
int defendValue = neighbor.card().value(side.opposite());
|
||||
if (attackValue > defendValue) {
|
||||
comboWave.add(neighborCell);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!comboWave.isEmpty()) {
|
||||
battleLog.add("Combo triggers.");
|
||||
applyOwnership(owner, comboWave);
|
||||
waves.add(List.copyOf(comboWave));
|
||||
}
|
||||
}
|
||||
|
||||
return new CaptureResolution(waves);
|
||||
}
|
||||
|
||||
private List<SideComparison> buildComparisons(BoardCell origin, MatchParticipant owner, GameCard playedCard, List<String> battleLog) {
|
||||
List<SideComparison> comparisons = new ArrayList<>();
|
||||
for (CardSide side : CardSide.values()) {
|
||||
BoardCell neighborCell = neighbor(origin, side);
|
||||
if (neighborCell == null) {
|
||||
@@ -90,13 +191,12 @@ public final class TriadMatch {
|
||||
}
|
||||
|
||||
PlacedCard neighbor = cardAt(neighborCell);
|
||||
if (neighbor == null || neighbor.owner().equals(owner)) {
|
||||
if (neighbor == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int attackValue = playedCard.value(side);
|
||||
int defendValue = neighbor.card().value(side.opposite());
|
||||
boolean capturedNeighbor = attackValue > defendValue;
|
||||
battleLog.add(String.format(
|
||||
"%s %s=%d vs %s %s=%d at [%d,%d] -> %s",
|
||||
playedCard.definition().name().getString(),
|
||||
@@ -107,14 +207,47 @@ public final class TriadMatch {
|
||||
defendValue,
|
||||
neighborCell.row(),
|
||||
neighborCell.column(),
|
||||
capturedNeighbor ? "flip" : "hold"));
|
||||
if (capturedNeighbor) {
|
||||
board[neighborCell.index()] = new PlacedCard(owner, neighbor.card());
|
||||
captured.add(neighborCell);
|
||||
!neighbor.owner().equals(owner) && attackValue > defendValue ? "flip" : "hold"));
|
||||
comparisons.add(new SideComparison(side, neighborCell, neighbor, attackValue, defendValue));
|
||||
}
|
||||
return comparisons;
|
||||
}
|
||||
|
||||
return captured;
|
||||
private List<SameCandidate> buildSameCandidates(BoardCell origin, GameCard playedCard, List<String> battleLog) {
|
||||
List<SameCandidate> candidates = new ArrayList<>();
|
||||
for (CardSide side : CardSide.values()) {
|
||||
BoardCell neighborCell = neighbor(origin, side);
|
||||
if (neighborCell == null) {
|
||||
boolean wallMatch = ruleSet.sameWallRule() && playedCard.value(side) == 10;
|
||||
battleLog.add(String.format(
|
||||
"%s %s=%d vs WALL=%d -> %s",
|
||||
playedCard.definition().name().getString(),
|
||||
side.name(),
|
||||
playedCard.value(side),
|
||||
10,
|
||||
wallMatch ? "same" : "ignore"));
|
||||
candidates.add(new SameCandidate(side, null, wallMatch));
|
||||
continue;
|
||||
}
|
||||
|
||||
PlacedCard neighbor = cardAt(neighborCell);
|
||||
if (neighbor == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
boolean sameMatch = playedCard.value(side) == neighbor.card().value(side.opposite());
|
||||
candidates.add(new SameCandidate(side, neighborCell, sameMatch));
|
||||
}
|
||||
return candidates;
|
||||
}
|
||||
|
||||
private void applyOwnership(MatchParticipant owner, Set<BoardCell> capturedCells) {
|
||||
for (BoardCell cell : capturedCells) {
|
||||
PlacedCard placedCard = cardAt(cell);
|
||||
if (placedCard != null) {
|
||||
board[cell.index()] = new PlacedCard(owner, placedCard.card());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private BoardCell neighbor(BoardCell cell, CardSide side) {
|
||||
@@ -167,4 +300,13 @@ public final class TriadMatch {
|
||||
void forceActiveParticipant(MatchParticipant participant) {
|
||||
this.activeParticipant = participant;
|
||||
}
|
||||
|
||||
private record SideComparison(CardSide side, BoardCell cell, PlacedCard neighbor, int attackValue, int defendValue) {
|
||||
}
|
||||
|
||||
private record SameCandidate(CardSide side, BoardCell cell, boolean matches) {
|
||||
}
|
||||
|
||||
private record CaptureResolution(List<List<BoardCell>> captureWaves) {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,8 @@ package com.trunksbomb.minetriad.game;
|
||||
public record TriadRuleSet(
|
||||
boolean openHands,
|
||||
boolean sameRule,
|
||||
boolean sameWallRule,
|
||||
boolean plusRule,
|
||||
boolean elementalRule) {
|
||||
public static final TriadRuleSet CLASSIC_OPEN = new TriadRuleSet(true, false, false, false);
|
||||
public static final TriadRuleSet CLASSIC_OPEN = new TriadRuleSet(true, true, true, true, false);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user