Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Black dragon #436

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
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
1 change: 1 addition & 0 deletions src/main/java/com/jcloisterzone/engine/Engine.java
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,7 @@ private GameSetup createSetupFromMessage(GameSetupMessage setupMsg) {
capabilities = addCapabilities(capabilities, setupMsg,"watchtower", WatchtowerCapability.class);

capabilities = addCapabilities(capabilities, setupMsg,"robbers-son", RobbersSonCapability.class);
capabilities = addCapabilities(capabilities, setupMsg,"black-dragon", BlackDragonCapability.class);

Map<Rule, Object> rules = HashMap.empty();
if (setupMsg.getElements().containsKey("farmers")) {
Expand Down
31 changes: 31 additions & 0 deletions src/main/java/com/jcloisterzone/engine/StateGsonBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,21 @@
import com.jcloisterzone.feature.Tower;
import com.jcloisterzone.figure.Follower;
import com.jcloisterzone.figure.Meeple;
import com.jcloisterzone.figure.neutral.BlackDragon;
import com.jcloisterzone.figure.neutral.Dragon;
import com.jcloisterzone.game.capability.*;
import com.jcloisterzone.game.capability.FerriesCapability.FerryToken;
import com.jcloisterzone.game.capability.GoldminesCapability.GoldToken;
import com.jcloisterzone.game.capability.LittleBuildingsCapability.LittleBuilding;
import com.jcloisterzone.game.capability.SheepToken;
import com.jcloisterzone.game.phase.BlackDragonMovePhase;
import com.jcloisterzone.game.phase.DragonMovePhase;
import com.jcloisterzone.game.phase.Phase;
import com.jcloisterzone.game.phase.RussianPromosTrapPhase;
import com.jcloisterzone.game.state.*;
import com.jcloisterzone.io.MessageParser;
import io.vavr.Tuple2;
import io.vavr.Tuple3;
import io.vavr.collection.*;

import java.lang.reflect.Type;
Expand Down Expand Up @@ -288,6 +291,19 @@ public JsonElement serializeNeutralFigures(GameState root, JsonSerializationCont
data.add("placement", context.serialize(pos));
neutral.add("bigtop", data);
}
pos = state.getBlackDragonDeployment();
if (pos != null) {
BlackDragonCapabilityModel model = root.getCapabilityModel(BlackDragonCapability.class);
JsonObject data = new JsonObject();
data.add("position", context.serialize(pos));
if (root.getPhase() instanceof BlackDragonMovePhase) {
JsonArray visitedData = new JsonArray();
model.getVisited().forEach(p -> visitedData.add(context.serialize(p)));
data.add("visited", visitedData);
data.addProperty("remaining", model.getMoves() - model.getVisited().length());
}
neutral.add("blackdragon", data);
}
return neutral;
}

Expand Down Expand Up @@ -374,6 +390,7 @@ public JsonArray serializePlayEvents(GameState root, JsonSerializationContext co
JsonObject item = null;
JsonArray turnEvents = null;
JsonArray dragonPath = null;
JsonArray blackdragonPath = null;
for (PlayEvent ev : root.getEvents()) {
if (ev instanceof PlayerTurnEvent) {
player = ((PlayerTurnEvent) ev).getPlayer();
Expand All @@ -387,6 +404,7 @@ public JsonArray serializePlayEvents(GameState root, JsonSerializationContext co
events.add(item);
// clean-up
dragonPath = null;
blackdragonPath = null;
continue;
}
if (item == null) {
Expand Down Expand Up @@ -529,6 +547,19 @@ public JsonArray serializePlayEvents(GameState root, JsonSerializationContext co
} else {
dragonPath.add(context.serialize(nev.getTo()));
}
} else if (nev.getNeutralFigure() instanceof BlackDragon) {
if (blackdragonPath == null) {
JsonObject data = new JsonObject();
blackdragonPath = new JsonArray();
blackdragonPath.add(context.serialize(nev.getFrom()));
blackdragonPath.add(context.serialize(nev.getTo()));
data.addProperty("type", "blackdragon-moved");
data.addProperty("figure", nev.getNeutralFigure().getId());
data.add("path", blackdragonPath);
turnEvents.add(data);
} else {
blackdragonPath.add(context.serialize(nev.getTo()));
}
} else {
JsonObject data = new JsonObject();
data.addProperty("type", "neutral-moved");
Expand Down
1 change: 1 addition & 0 deletions src/main/java/com/jcloisterzone/figure/neutral/BigTop.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.jcloisterzone.Immutable;
import com.jcloisterzone.board.Position;
import com.jcloisterzone.game.state.GameState;

@Immutable
public class BigTop extends NeutralFigure<Position> {
Expand Down
15 changes: 15 additions & 0 deletions src/main/java/com/jcloisterzone/figure/neutral/BlackDragon.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.jcloisterzone.figure.neutral;

import com.jcloisterzone.Immutable;
import com.jcloisterzone.board.Position;

@Immutable
public class BlackDragon extends NeutralFigure<Position> {

private static final long serialVersionUID = 1L;

public BlackDragon(String id) {
super(id);
}

}
1 change: 1 addition & 0 deletions src/main/java/com/jcloisterzone/figure/neutral/Count.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.jcloisterzone.Immutable;
import com.jcloisterzone.board.pointer.FeaturePointer;
import com.jcloisterzone.game.state.GameState;

@Immutable
public class Count extends NeutralFigure<FeaturePointer> {
Expand Down
12 changes: 0 additions & 12 deletions src/main/java/com/jcloisterzone/figure/neutral/NeutralFigure.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,16 +28,4 @@ public boolean at(GameState state, Structure feature) {
FeaturePointer fp = ptr.asFeaturePointer();
return feature.getPlaces().contains(fp);
}


// @Override
// public void deploy(T at) {
// T origin = getDeployment();
// game.replaceState(state -> {
// LinkedHashMap<NeutralFigure<?>, BoardPointer> deployedNeutralFigures = state.getDeployedNeutralFigures();
// return state.setDeployedNeutralFigures(deployedNeutralFigures.put(this, at));
// });
// game.post(new NeutralFigureMoveEvent(game.getActivePlayer(), this, origin, at));
// }

}
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ public GameStatePhaseReducer(GameSetup setup, double initialRandom) {
next = cleanUpTurnPartPhase = new CleanUpTurnPartPhase(randomGenerator, next);
if (setup.contains(CornCircleCapability.class)) next = new CornCirclePhase(randomGenerator, next);

if (setup.contains(BlackDragonCapability.class)) next = new BlackDragonPlacePhase(randomGenerator, next);
if (setup.contains(DragonCapability.class) && "after-scoring".equals(setup.getStringRule(Rule.DRAGON_MOVEMENT))) {
next = new DragonPhase(randomGenerator, next);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package com.jcloisterzone.game.capability;

import com.jcloisterzone.Immutable;
import com.jcloisterzone.board.Position;
import com.jcloisterzone.board.pointer.FeaturePointer;
import com.jcloisterzone.feature.Completable;
import com.jcloisterzone.figure.Meeple;
import com.jcloisterzone.figure.neutral.BlackDragon;
import com.jcloisterzone.game.Capability;
import com.jcloisterzone.game.state.GameState;
import com.jcloisterzone.reducers.MoveNeutralFigure;
import com.jcloisterzone.reducers.UndeployMeeple;

import io.vavr.collection.Vector;
import io.vavr.Tuple2;


@Immutable
public class BlackDragonCapability extends Capability<BlackDragonCapabilityModel> {

private static final long serialVersionUID = 1L;

public static final Vector<Position> EMPTY_VISITED = Vector.empty();

@Override
public GameState onStartGame(GameState state) {
state = state.mapNeutralFigures(nf -> nf.setBlackDragon(new BlackDragon("blackdragon.1")));
state = setModel(state, new BlackDragonCapabilityModel(EMPTY_VISITED, 0));
return state;
}

@Override
public boolean isMeepleDeploymentAllowed(GameState state, Position pos) {
return !pos.equals(state.getNeutralFigures().getBlackDragonDeployment());
}

@Override
public GameState beforeCompletableScore(GameState state, java.util.Set<Completable> features) {
if (features.size() > 0) {
state = setModel(state, new BlackDragonCapabilityModel(EMPTY_VISITED, features.size()));
}
return state;
}

public GameState moveBlackDragon(GameState state, Position pos) {
state = (
new MoveNeutralFigure<>(state.getNeutralFigures().getBlackDragon(), pos)
).apply(state);

state = clearTile(state, pos);
return state;
}

public GameState clearTile(GameState state, Position pos) {
for (Tuple2<Meeple, FeaturePointer> t: state.getDeployedMeeples()) {
Meeple m = t._1;
FeaturePointer fp = t._2;
if (pos.equals(fp.getPosition()) && m.canBeEatenByDragon(state)) {
state = (new UndeployMeeple(m, true)).apply(state);
}
}

return state;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package com.jcloisterzone.game.capability;

import com.jcloisterzone.board.Position;
import io.vavr.collection.Vector;

public class BlackDragonCapabilityModel {

private final Vector<Position> visited;
private final int moves;

public BlackDragonCapabilityModel(Vector<Position> visited, int moves) {
this.visited = visited;
this.moves = moves;
}

public Vector<Position> getVisited() {
return visited;
}

public BlackDragonCapabilityModel setVisited(Vector<Position> visited) {
return new BlackDragonCapabilityModel(visited, moves);
}

public int getMoves() {
return moves;
}

public BlackDragonCapabilityModel setMoves(int moves) {
return new BlackDragonCapabilityModel(visited, moves);
}
}
100 changes: 100 additions & 0 deletions src/main/java/com/jcloisterzone/game/phase/BlackDragonMovePhase.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
package com.jcloisterzone.game.phase;

import com.jcloisterzone.action.MoveDragonAction;
import com.jcloisterzone.board.Position;
import com.jcloisterzone.board.pointer.BoardPointer;
import com.jcloisterzone.figure.neutral.BlackDragon;
import com.jcloisterzone.figure.neutral.NeutralFigure;
import com.jcloisterzone.game.capability.BlackDragonCapabilityModel;
import com.jcloisterzone.game.capability.CountCapability;
import com.jcloisterzone.game.capability.BlackDragonCapability;
import com.jcloisterzone.game.state.ActionsState;
import com.jcloisterzone.game.state.Flag;
import com.jcloisterzone.game.state.GameState;
import com.jcloisterzone.game.state.PlacedTile;
import com.jcloisterzone.io.message.MoveNeutralFigureMessage;
import com.jcloisterzone.random.RandomGenerator;
import com.jcloisterzone.reducers.MoveNeutralFigure;
import io.vavr.collection.HashSet;
import io.vavr.collection.Set;
import io.vavr.collection.Vector;

public class BlackDragonMovePhase extends Phase {

public BlackDragonMovePhase(RandomGenerator random, Phase defaultNext) {
super(random, defaultNext);
}

@Override
public StepResult enter(GameState state) {
BlackDragonCapabilityModel model = state.getCapabilityModel(BlackDragonCapability.class);
if (model.getVisited().size() == model.getMoves()) {
return next(endBlackDragonMove(state));
}
Set<Position> availMoves = getAvailBlackDragonMoves(state, model.getVisited());
if (availMoves.isEmpty()) {
return next(endBlackDragonMove(state));
}
BlackDragon blackdragon = state.getNeutralFigures().getBlackDragon();
return promote(state.setPlayerActions(
new ActionsState(state.getTurnPlayer(), new MoveDragonAction(blackdragon.getId(), availMoves), false)
));
}

private GameState endBlackDragonMove(GameState state) {
state = state.addFlag(Flag.BLACK_DRAGON_MOVED);
state = state.setCapabilityModel(BlackDragonCapability.class, new BlackDragonCapabilityModel(BlackDragonCapability.EMPTY_VISITED, 0));
state = clearActions(state);
return state;
}

public Set<Position> getAvailBlackDragonMoves(GameState state, Vector<Position> visited) {
Set<Position> result = HashSet.empty();
Position blackdragonPosition = state.getNeutralFigures().getBlackDragonDeployment();

if (blackdragonPosition != null) {
for (Position offset: Position.ADJACENT.values()) {
Position pos = blackdragonPosition.add(offset);
PlacedTile pt = state.getPlacedTile(pos);

if (pt == null || CountCapability.isTileForbidden(pt.getTile())) continue;
if (visited.contains(pos)) continue;

result = result.add(pos);
}
}
return result;
}

@PhaseMessageHandler
public StepResult handleMoveNeutralFigure(GameState state, MoveNeutralFigureMessage msg) {
BoardPointer ptr = msg.getTo();
NeutralFigure<?> fig = state.getNeutralFigures().getById(msg.getFigureId());

if (!(fig instanceof BlackDragon)) {
throw new IllegalArgumentException("Illegal neutral figure move");
}

BlackDragonCapability cap = state.getCapabilities().get(BlackDragonCapability.class);
BlackDragonCapabilityModel model = state.getCapabilityModel(BlackDragonCapability.class);

Vector<Position> visited = model.getVisited();
Set<Position> availMoves = getAvailBlackDragonMoves(state, visited);

final Position pos = ptr.getPosition();
if (!availMoves.contains(pos)) {
throw new IllegalArgumentException("Invalid black dragon move.");
}

state = (
new MoveNeutralFigure<>((BlackDragon) fig, pos, state.getActivePlayer())
).apply(state);

state = state.mapCapabilityModel(BlackDragonCapability.class, m -> {
return m.setVisited(m.getVisited().append(pos));
});

state = cap.clearTile(state, pos);
return enter(state);
}
}
25 changes: 25 additions & 0 deletions src/main/java/com/jcloisterzone/game/phase/BlackDragonPhase.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package com.jcloisterzone.game.phase;

import com.jcloisterzone.game.capability.BlackDragonCapability;
import com.jcloisterzone.game.capability.BlackDragonCapabilityModel;
import com.jcloisterzone.game.state.GameState;
import com.jcloisterzone.random.RandomGenerator;

public class BlackDragonPhase extends Phase {

private BlackDragonMovePhase blackDragonMovePhase;

public BlackDragonPhase(RandomGenerator random, Phase defaultNext) {
super(random, defaultNext);
blackDragonMovePhase = new BlackDragonMovePhase(random, defaultNext);
}

@Override
public StepResult enter(GameState state) {
BlackDragonCapabilityModel model = state.getCapabilityModel(BlackDragonCapability.class);
if (model.getMoves() > 0) {
return next(state, blackDragonMovePhase);
}
return next(state);
}
}
Loading