Skip to content
Closed
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
12 changes: 12 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,18 @@
<version>5.7.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>3.6.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-junit-jupiter</artifactId>
<version>3.6.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.esotericsoftware</groupId>
<artifactId>kryo</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ public class DeckEditingContext extends GameContext {

private DeckList deckList1 = BeginnerDecks.RUNNING_AND_WALKING.deckList();
private DeckList deckList2 = BeginnerDecks.AGRICULTURE_AND_LABOUR.deckList();
private DeckList deckList3 = BeginnerDecks.CYCLE_AND_SEARCH.deckList();
private DeckList deckList3 = BeginnerDecks.FIRE_AND_ICE.deckList();
private DeckList deckList4 = BeginnerDecks.PUNCH_AND_GRAPPLE.deckList();

@Override
Expand Down
9 changes: 8 additions & 1 deletion src/main/java/nomadrealms/context/game/card/GameCard.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,11 @@
import nomadrealms.context.game.card.expression.SurfaceCardExpression;
import nomadrealms.context.game.card.expression.TeleportExpression;
import nomadrealms.context.game.card.expression.TeleportNoTargetExpression;
import nomadrealms.context.game.card.query.actor.ActorsOnTilesQuery;
import nomadrealms.context.game.card.query.actor.SelfQuery;
import nomadrealms.context.game.card.query.card.LastResolvedCardQuery;
import nomadrealms.context.game.card.query.tile.PreviousTileQuery;
import nomadrealms.context.game.card.query.tile.TilesInRadiusQuery;
import nomadrealms.context.game.card.target.TargetingInfo;
import nomadrealms.context.game.world.map.tile.factory.TileType;

Expand Down Expand Up @@ -100,7 +102,12 @@ public enum GameCard implements Card {
"Wooden Chest",
"Create a chest on target tile",
new CreateStructureExpression(StructureType.CHEST),
new TargetingInfo(HEXAGON, 1));
new TargetingInfo(HEXAGON, 1)),
FLAME_CIRCLE(
"Flame Circle",
"Deal 4 damage to all other actors within radius 3",
new DamageExpression(new ActorsOnTilesQuery(new TilesInRadiusQuery(new SelfQuery(), 3)), 4),
new TargetingInfo(NONE, 0));

private final String title;
private final String description;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,37 @@
import static java.util.Collections.singletonList;

import java.util.List;
import java.util.stream.Collectors;

import nomadrealms.context.game.actor.cardplayer.CardPlayer;
import nomadrealms.context.game.card.intent.DamageIntent;
import nomadrealms.context.game.card.intent.Intent;
import nomadrealms.context.game.card.query.Query;
import nomadrealms.context.game.event.Target;
import nomadrealms.context.game.actor.cardplayer.CardPlayer;
import nomadrealms.context.game.world.World;

public class DamageExpression implements CardExpression {

private final int amount;
private final Query<? extends Target> query;

public DamageExpression(int amount) {
this.amount = amount;
this.query = null;
}

public DamageExpression(Query<? extends Target> query, int amount) {
this.amount = amount;
this.query = query;
}

@Override
public List<Intent> intents(World world, Target target, CardPlayer source) {
if (query != null) {
return query.find(world, source).stream()
.map(t -> new DamageIntent(t, source, amount))
.collect(Collectors.toList());
}
return singletonList(new DamageIntent(target, source, amount));
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package nomadrealms.context.game.card.query.actor;

import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

import nomadrealms.context.game.actor.Actor;
import nomadrealms.context.game.actor.cardplayer.CardPlayer;
import nomadrealms.context.game.card.query.Query;
import nomadrealms.context.game.world.World;
import nomadrealms.context.game.world.map.area.Tile;

public class ActorsOnTilesQuery implements Query<Actor> {

private final Query<Tile> tilesQuery;

public ActorsOnTilesQuery(Query<Tile> tilesQuery) {
this.tilesQuery = tilesQuery;
}

@Override
public List<Actor> find(World world, CardPlayer source) {
return tilesQuery.find(world, source).stream()
.map(tile -> world.getTargetOnTile(tile))
.filter(Objects::nonNull)
.filter(entity -> entity instanceof Actor)
.map(entity -> (Actor) entity)
.filter(actor -> actor != source)
.collect(Collectors.toList());
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package nomadrealms.context.game.card.query.tile;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;

import nomadrealms.context.game.actor.HasPosition;
import nomadrealms.context.game.actor.cardplayer.CardPlayer;
import nomadrealms.context.game.card.query.Query;
import nomadrealms.context.game.world.World;
import nomadrealms.context.game.world.map.area.Tile;
import nomadrealms.context.game.world.map.area.coordinate.TileCoordinate;

public class TilesInRadiusQuery implements Query<Tile> {

private final Query<? extends HasPosition> centerQuery;
private final int radius;

public TilesInRadiusQuery(Query<? extends HasPosition> centerQuery, int radius) {
this.centerQuery = centerQuery;
this.radius = radius;
}

@Override
public List<Tile> find(World world, CardPlayer source) {
List<? extends HasPosition> centers = centerQuery.find(world, source);
if (centers.isEmpty()) {
return Collections.emptyList();
}

Tile centerTile = centers.get(0).tile();
if (centerTile == null) {
return Collections.emptyList();
}

Set<Tile> visited = new HashSet<>();
List<Tile> frontier = new ArrayList<>();
List<Tile> tilesInRadius = new ArrayList<>();

frontier.add(centerTile);
visited.add(centerTile);
tilesInRadius.add(centerTile);

for (int i = 0; i < radius; i++) {
List<Tile> nextFrontier = new ArrayList<>();
for (Tile tile : frontier) {
addNeighbor(world, nextFrontier, visited, tilesInRadius, tile.coord().ul());
addNeighbor(world, nextFrontier, visited, tilesInRadius, tile.coord().um());
addNeighbor(world, nextFrontier, visited, tilesInRadius, tile.coord().ur());
addNeighbor(world, nextFrontier, visited, tilesInRadius, tile.coord().dl());
addNeighbor(world, nextFrontier, visited, tilesInRadius, tile.coord().dm());
addNeighbor(world, nextFrontier, visited, tilesInRadius, tile.coord().dr());
}
frontier = nextFrontier;
}

return tilesInRadius;
}

private void addNeighbor(World world, List<Tile> nextFrontier, Set<Tile> visited, List<Tile> tilesInRadius, TileCoordinate neighborCoord) {
Tile neighbor = world.getTile(neighborCoord);
if (neighbor != null && !visited.contains(neighbor)) {
visited.add(neighbor);
nextFrontier.add(neighbor);
tilesInRadius.add(neighbor);
}
}
}
10 changes: 8 additions & 2 deletions src/main/java/nomadrealms/context/game/zone/BeginnerDecks.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import static nomadrealms.context.game.card.GameCard.ATTACK;
import static nomadrealms.context.game.card.GameCard.CREATE_ROCK;
import static nomadrealms.context.game.card.GameCard.FLAME_CIRCLE;
import static nomadrealms.context.game.card.GameCard.GATHER;
import static nomadrealms.context.game.card.GameCard.HEAL;
import static nomadrealms.context.game.card.GameCard.MELEE_ATTACK;
Expand All @@ -22,9 +23,14 @@ public enum BeginnerDecks {
UNSTABLE_TELEPORT,
REWIND
)),
PUNCH_AND_GRAPPLE("Punch & Grapple", new DeckList(HEAL, ATTACK, HEAL, MELEE_ATTACK)),
PUNCH_AND_GRAPPLE("Punch & Grapple", new DeckList(HEAL, ATTACK, HEAL, MELEE_ATTACK, FLAME_CIRCLE)),
CYCLE_AND_SEARCH("Cycle & Search ", new DeckList()),
AGRICULTURE_AND_LABOUR("Agriculture & Labour", new DeckList(GATHER, CREATE_ROCK, WOODEN_CHEST, TILL_SOIL, PLANT_SEED));
AGRICULTURE_AND_LABOUR("Agriculture & Labour", new DeckList(GATHER, CREATE_ROCK, WOODEN_CHEST, TILL_SOIL,
PLANT_SEED)),
FIRE_AND_ICE("Fire & Ice", new DeckList(
FLAME_CIRCLE
)),
;

private final String name;
private final DeckList deckList;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package nomadrealms.context.game.card.expression;

import static java.util.Arrays.asList;
import static nomadrealms.context.game.card.GameCard.FLAME_CIRCLE;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.mock;

import java.util.List;

import nomadrealms.context.game.actor.Actor;
import nomadrealms.context.game.actor.cardplayer.CardPlayer;
import nomadrealms.context.game.card.intent.Intent;
import nomadrealms.context.game.card.query.Query;
import nomadrealms.context.game.world.World;
import org.junit.jupiter.api.Test;

class DamageExpressionTest {

@Test
void testQueryBasedDamage() {
World world = mock(World.class);
CardPlayer source = mock(CardPlayer.class);
Actor target1 = mock(Actor.class);
Actor target2 = mock(Actor.class);

Query<Actor> query = (w, s) -> asList(target1, target2);

DamageExpression expression = new DamageExpression(query, 5);
List<Intent> intents = expression.intents(world, null, source);

assertEquals(2, intents.size());
}

@Test
void testFlameCircleCard() {
World world = mock(World.class);
CardPlayer source = mock(CardPlayer.class);
Actor target1 = mock(Actor.class);
Actor target2 = mock(Actor.class);

// This is a simplified test. The query logic is tested separately.
// Here we just need to make sure the card uses the expression correctly.
CardExpression expression = FLAME_CIRCLE.expression();
List<Intent> intents = expression.intents(world, null, source);

// We can't easily mock the chained queries, so we'll just check that
// the expression is a DamageExpression.
assertEquals(DamageExpression.class, expression.getClass());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package nomadrealms.context.game.card.query.actor;

import static java.util.Arrays.asList;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

import java.util.List;

import nomadrealms.context.game.actor.Actor;
import nomadrealms.context.game.actor.cardplayer.CardPlayer;
import nomadrealms.context.game.card.query.Query;
import nomadrealms.context.game.world.World;
import nomadrealms.context.game.world.map.area.Tile;
import org.junit.jupiter.api.Test;

class ActorsOnTilesQueryTest {

@Test
void testFind() {
World world = mock(World.class);
CardPlayer source = mock(CardPlayer.class);
Tile tile1 = mock(Tile.class);
Tile tile2 = mock(Tile.class);
Actor actor1 = mock(Actor.class);
Actor actor2 = mock(Actor.class);

Query<Tile> tilesQuery = (w, s) -> asList(tile1, tile2);

when(world.getTargetOnTile(tile1)).thenReturn(actor1);
when(world.getTargetOnTile(tile2)).thenReturn(actor2);

ActorsOnTilesQuery query = new ActorsOnTilesQuery(tilesQuery);
List<Actor> result = query.find(world, source);

assertEquals(2, result.size());
assertEquals(actor1, result.get(0));
assertEquals(actor2, result.get(1));
}

@Test
void testFind_excludesSource() {
World world = mock(World.class);
CardPlayer source = mock(CardPlayer.class);
Tile tile1 = mock(Tile.class);
Tile tile2 = mock(Tile.class);
Actor actor1 = mock(Actor.class);

Query<Tile> tilesQuery = (w, s) -> List.of(tile1, tile2);

when(world.getTargetOnTile(tile1)).thenReturn(actor1);
when(world.getTargetOnTile(tile2)).thenReturn(source);

ActorsOnTilesQuery query = new ActorsOnTilesQuery(tilesQuery);
List<Actor> result = query.find(world, source);

assertEquals(1, result.size());
assertEquals(actor1, result.get(0));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package nomadrealms.context.game.card.query.tile;

import org.junit.jupiter.api.Test;
import nomadrealms.context.game.actor.cardplayer.CardPlayer;
import nomadrealms.context.game.card.query.actor.SelfQuery;
import nomadrealms.context.game.world.World;
import nomadrealms.context.game.world.map.area.Tile;
import nomadrealms.context.game.world.map.area.coordinate.TileCoordinate;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

import java.util.List;

class TilesInRadiusQueryTest {

@Test
void testFind() {
World world = mock(World.class);
CardPlayer source = mock(CardPlayer.class);
Tile centerTile = mock(Tile.class);
TileCoordinate centerCoord = mock(TileCoordinate.class);

when(source.tile()).thenReturn(centerTile);
when(centerTile.coord()).thenReturn(centerCoord);

Tile ul = mock(Tile.class);
Tile um = mock(Tile.class);
Tile ur = mock(Tile.class);
Tile dl = mock(Tile.class);
Tile dm = mock(Tile.class);
Tile dr = mock(Tile.class);

TileCoordinate ulCoord = mock(TileCoordinate.class);
TileCoordinate umCoord = mock(TileCoordinate.class);
TileCoordinate urCoord = mock(TileCoordinate.class);
TileCoordinate dlCoord = mock(TileCoordinate.class);
TileCoordinate dmCoord = mock(TileCoordinate.class);
TileCoordinate drCoord = mock(TileCoordinate.class);

when(centerCoord.ul()).thenReturn(ulCoord);
when(centerCoord.um()).thenReturn(umCoord);
when(centerCoord.ur()).thenReturn(urCoord);
when(centerCoord.dl()).thenReturn(dlCoord);
when(centerCoord.dm()).thenReturn(dmCoord);
when(centerCoord.dr()).thenReturn(drCoord);

when(world.getTile(ulCoord)).thenReturn(ul);
when(world.getTile(umCoord)).thenReturn(um);
when(world.getTile(urCoord)).thenReturn(ur);
when(world.getTile(dlCoord)).thenReturn(dl);
when(world.getTile(dmCoord)).thenReturn(dm);
when(world.getTile(drCoord)).thenReturn(dr);

TilesInRadiusQuery query = new TilesInRadiusQuery(new SelfQuery(), 1);
List<Tile> result = query.find(world, source);

assertEquals(7, result.size());
}
}