add Same/Same Wall/Plus rules logic.
This commit is contained in:
@@ -372,11 +372,13 @@ public class DuelTableBlockEntity extends BlockEntity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pendingAnimations.addLast(new AnimationStep(AnimationType.PLACEMENT, new int[] {moveResult.playedCell().index()}, owner));
|
pendingAnimations.addLast(new AnimationStep(AnimationType.PLACEMENT, new int[] {moveResult.playedCell().index()}, owner));
|
||||||
if (!moveResult.capturedCells().isEmpty()) {
|
for (List<BoardCell> captureWave : moveResult.captureWaves()) {
|
||||||
int[] captureSlots = moveResult.capturedCells().stream()
|
int[] captureSlots = captureWave.stream()
|
||||||
.mapToInt(BoardCell::index)
|
.mapToInt(BoardCell::index)
|
||||||
.toArray();
|
.toArray();
|
||||||
pendingAnimations.addLast(new AnimationStep(AnimationType.CAPTURE, captureSlots, owner));
|
if (captureSlots.length > 0) {
|
||||||
|
pendingAnimations.addLast(new AnimationStep(AnimationType.CAPTURE, captureSlots, owner));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
advanceAnimationStep();
|
advanceAnimationStep();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,21 +7,29 @@ import net.minecraft.resources.ResourceLocation;
|
|||||||
public record MoveResult(
|
public record MoveResult(
|
||||||
boolean valid,
|
boolean valid,
|
||||||
String errorMessage,
|
String errorMessage,
|
||||||
|
List<List<BoardCell>> captureWaves,
|
||||||
List<BoardCell> capturedCells,
|
List<BoardCell> capturedCells,
|
||||||
List<String> battleLog,
|
List<String> battleLog,
|
||||||
String playedCardName,
|
String playedCardName,
|
||||||
ResourceLocation playedCardId,
|
ResourceLocation playedCardId,
|
||||||
BoardCell playedCell) {
|
BoardCell playedCell) {
|
||||||
public static MoveResult success(
|
public static MoveResult success(
|
||||||
List<BoardCell> capturedCells,
|
List<List<BoardCell>> captureWaves,
|
||||||
List<String> battleLog,
|
List<String> battleLog,
|
||||||
String playedCardName,
|
String playedCardName,
|
||||||
ResourceLocation playedCardId,
|
ResourceLocation playedCardId,
|
||||||
BoardCell playedCell) {
|
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) {
|
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.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.LinkedHashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
public final class TriadMatch {
|
public final class TriadMatch {
|
||||||
private final MatchParticipant firstParticipant;
|
private final MatchParticipant firstParticipant;
|
||||||
@@ -75,14 +79,111 @@ public final class TriadMatch {
|
|||||||
board[move.targetCell().index()] = new PlacedCard(move.participant(), playedCard);
|
board[move.targetCell().index()] = new PlacedCard(move.participant(), playedCard);
|
||||||
|
|
||||||
List<String> battleLog = new ArrayList<>();
|
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());
|
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) {
|
private CaptureResolution resolveCaptures(BoardCell origin, MatchParticipant owner, GameCard playedCard, List<String> battleLog) {
|
||||||
List<BoardCell> captured = new ArrayList<>();
|
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()) {
|
for (CardSide side : CardSide.values()) {
|
||||||
BoardCell neighborCell = neighbor(origin, side);
|
BoardCell neighborCell = neighbor(origin, side);
|
||||||
if (neighborCell == null) {
|
if (neighborCell == null) {
|
||||||
@@ -90,13 +191,12 @@ public final class TriadMatch {
|
|||||||
}
|
}
|
||||||
|
|
||||||
PlacedCard neighbor = cardAt(neighborCell);
|
PlacedCard neighbor = cardAt(neighborCell);
|
||||||
if (neighbor == null || neighbor.owner().equals(owner)) {
|
if (neighbor == null) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
int attackValue = playedCard.value(side);
|
int attackValue = playedCard.value(side);
|
||||||
int defendValue = neighbor.card().value(side.opposite());
|
int defendValue = neighbor.card().value(side.opposite());
|
||||||
boolean capturedNeighbor = attackValue > defendValue;
|
|
||||||
battleLog.add(String.format(
|
battleLog.add(String.format(
|
||||||
"%s %s=%d vs %s %s=%d at [%d,%d] -> %s",
|
"%s %s=%d vs %s %s=%d at [%d,%d] -> %s",
|
||||||
playedCard.definition().name().getString(),
|
playedCard.definition().name().getString(),
|
||||||
@@ -107,14 +207,47 @@ public final class TriadMatch {
|
|||||||
defendValue,
|
defendValue,
|
||||||
neighborCell.row(),
|
neighborCell.row(),
|
||||||
neighborCell.column(),
|
neighborCell.column(),
|
||||||
capturedNeighbor ? "flip" : "hold"));
|
!neighbor.owner().equals(owner) && attackValue > defendValue ? "flip" : "hold"));
|
||||||
if (capturedNeighbor) {
|
comparisons.add(new SideComparison(side, neighborCell, neighbor, attackValue, defendValue));
|
||||||
board[neighborCell.index()] = new PlacedCard(owner, neighbor.card());
|
}
|
||||||
captured.add(neighborCell);
|
return comparisons;
|
||||||
|
}
|
||||||
|
|
||||||
|
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());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return captured;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private BoardCell neighbor(BoardCell cell, CardSide side) {
|
private BoardCell neighbor(BoardCell cell, CardSide side) {
|
||||||
@@ -167,4 +300,13 @@ public final class TriadMatch {
|
|||||||
void forceActiveParticipant(MatchParticipant participant) {
|
void forceActiveParticipant(MatchParticipant participant) {
|
||||||
this.activeParticipant = 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(
|
public record TriadRuleSet(
|
||||||
boolean openHands,
|
boolean openHands,
|
||||||
boolean sameRule,
|
boolean sameRule,
|
||||||
|
boolean sameWallRule,
|
||||||
boolean plusRule,
|
boolean plusRule,
|
||||||
boolean elementalRule) {
|
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