fix: improve rasterization and tests
This commit is contained in:
parent
0572450ced
commit
5984e40384
@ -1,17 +0,0 @@
|
|||||||
package uulm.teamname.marvelous.gamelibrary.gamelogic;
|
|
||||||
|
|
||||||
import uulm.teamname.marvelous.gamelibrary.events.Event;
|
|
||||||
|
|
||||||
import java.util.Observable;
|
|
||||||
|
|
||||||
/** Represents an event emitter for game events fired by a game instance. */
|
|
||||||
class EventEmitter extends Observable {
|
|
||||||
/**
|
|
||||||
* Emits an array of {@link Event}s. This method is necessary because {@link Observable#setChanged} is protected.
|
|
||||||
* @param events The events to emit
|
|
||||||
*/
|
|
||||||
public void update(Event... events) {
|
|
||||||
setChanged();
|
|
||||||
notifyObservers(events);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,22 +0,0 @@
|
|||||||
package uulm.teamname.marvelous.gamelibrary.gamelogic;
|
|
||||||
|
|
||||||
import uulm.teamname.marvelous.gamelibrary.events.Event;
|
|
||||||
|
|
||||||
import java.util.Observable;
|
|
||||||
import java.util.Observer;
|
|
||||||
|
|
||||||
/** Represents an event observer for game events emitted by an {@link EventEmitter}. */
|
|
||||||
public class EventObserver implements Observer {
|
|
||||||
@Override
|
|
||||||
public void update(Observable o, Object arg) {
|
|
||||||
handle((Event[])arg);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets called with incoming {@link Event}s. Override this method to handle events.
|
|
||||||
* @param events The events that got emitted
|
|
||||||
*/
|
|
||||||
protected void handle(Event[] events) {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,6 +1,7 @@
|
|||||||
package uulm.teamname.marvelous.gamelibrary.gamelogic;
|
package uulm.teamname.marvelous.gamelibrary.gamelogic;
|
||||||
|
|
||||||
import uulm.teamname.marvelous.gamelibrary.IntVector2;
|
import uulm.teamname.marvelous.gamelibrary.IntVector2;
|
||||||
|
import uulm.teamname.marvelous.gamelibrary.Tuple;
|
||||||
import uulm.teamname.marvelous.gamelibrary.entities.*;
|
import uulm.teamname.marvelous.gamelibrary.entities.*;
|
||||||
import uulm.teamname.marvelous.gamelibrary.entities.Character;
|
import uulm.teamname.marvelous.gamelibrary.entities.Character;
|
||||||
import uulm.teamname.marvelous.gamelibrary.events.*;
|
import uulm.teamname.marvelous.gamelibrary.events.*;
|
||||||
@ -11,7 +12,9 @@ import uulm.teamname.marvelous.gamelibrary.requests.Request;
|
|||||||
import uulm.teamname.marvelous.gamelibrary.requests.RequestType;
|
import uulm.teamname.marvelous.gamelibrary.requests.RequestType;
|
||||||
|
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
|
import java.awt.geom.Area;
|
||||||
import java.awt.geom.Line2D;
|
import java.awt.geom.Line2D;
|
||||||
|
import java.awt.geom.Point2D;
|
||||||
import java.awt.geom.Rectangle2D;
|
import java.awt.geom.Rectangle2D;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
@ -98,7 +101,7 @@ class GameLogic {
|
|||||||
}
|
}
|
||||||
case MindStone -> {
|
case MindStone -> {
|
||||||
EntityType target = data.originEntity.type == EntityType.P1 ? EntityType.P2 : EntityType.P1;
|
EntityType target = data.originEntity.type == EntityType.P1 ? EntityType.P2 : EntityType.P1;
|
||||||
for(IntVector2 pos: rasterize(data.originField, data.targetField)) {
|
for(IntVector2 pos: rasterize(data.originField, data.targetField, false, true)) {
|
||||||
for(Entity entity: state.entities.findByPosition(pos)) {
|
for(Entity entity: state.entities.findByPosition(pos)) {
|
||||||
if(entity.id.isSameType(target)) {
|
if(entity.id.isSameType(target)) {
|
||||||
result.add(new EventBuilder(EventType.TakenDamageEvent)
|
result.add(new EventBuilder(EventType.TakenDamageEvent)
|
||||||
@ -436,7 +439,7 @@ class GameLogic {
|
|||||||
* @return Whether or not the light of sight exists
|
* @return Whether or not the light of sight exists
|
||||||
*/
|
*/
|
||||||
private static boolean checkLineOfSight(GameState state, IntVector2 start, IntVector2 end) {
|
private static boolean checkLineOfSight(GameState state, IntVector2 start, IntVector2 end) {
|
||||||
for(IntVector2 pos: rasterize(start, end)) {
|
for(IntVector2 pos: rasterize(start, end, false, false)) {
|
||||||
if(state.entities.blocksVision(pos)) {
|
if(state.entities.blocksVision(pos)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -949,28 +952,84 @@ class GameLogic {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Computes all fields which intersect the line between the center points of the given two fields.
|
* Computes all fields which intersect the line between the center points of the given two fields including the two defining points.
|
||||||
* @return The list of intersecting positions
|
* @return The list of intersecting positions
|
||||||
*/
|
*/
|
||||||
public static ArrayList<IntVector2> rasterize(IntVector2 a, IntVector2 b) {
|
public static ArrayList<IntVector2> rasterize(IntVector2 a, IntVector2 b) {
|
||||||
ArrayList<IntVector2> result = new ArrayList<>();
|
return rasterize(a, b, true, true);
|
||||||
|
}
|
||||||
|
|
||||||
//TODO: implement proper line rasterization algorithm in GameLogic.rasterize
|
/**
|
||||||
|
* Computes all fields which intersect the line between the center points of the given two fields.
|
||||||
|
* @param includeA Whether to include point a in the result
|
||||||
|
* @param includeB Whether to include point b in the result
|
||||||
|
* @return The list of intersecting positions
|
||||||
|
*/
|
||||||
|
public static ArrayList<IntVector2> rasterize(IntVector2 a, IntVector2 b, boolean includeA, boolean includeB) {
|
||||||
|
ArrayList<IntVector2> result = new ArrayList<>();
|
||||||
|
|
||||||
int x1 = Math.min(a.getX(), b.getX());
|
int x1 = Math.min(a.getX(), b.getX());
|
||||||
int x2 = Math.max(a.getX(), b.getX());
|
int x2 = Math.max(a.getX(), b.getX());
|
||||||
int y1 = Math.min(a.getY(), b.getY());
|
int y1 = Math.min(a.getY(), b.getY());
|
||||||
int y2 = Math.max(a.getY(), b.getY());
|
int y2 = Math.max(a.getY(), b.getY());
|
||||||
|
|
||||||
Line2D line = new Line2D.Float(x1 + 0.5f, y1 + 0.5f, x2 + 0.5f, y2 + 0.5f);
|
Line2D line = new Line2D.Float(x1 + 0.5f, y1 + 0.5f, x2 + 0.5f, y2 + 0.5f);
|
||||||
for(int i = x1; i <= x2; i++) {
|
for(int i = x1; i <= x2; i++) {
|
||||||
for(int j = y1; j <= y2; j++) {
|
for(int j = y1; j <= y2; j++) {
|
||||||
Rectangle2D cell = new Rectangle.Float(i, j, 1, 1);
|
HashSet<Point2D.Float> intersections = new HashSet<>();
|
||||||
if(line.intersects(cell)) {
|
for(Line2D.Float part: getWireframe(new Rectangle.Float(i, j, 1, 1))) {
|
||||||
|
if(part.intersectsLine(line)) {
|
||||||
|
Point2D.Float intersection = calculateInterceptionPoint(line.getP1(), line.getP2(), part.getP1(), part.getP2());
|
||||||
|
intersections.add(intersection);
|
||||||
|
if(intersections.size() > 1) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(intersections.size() > 1) {
|
||||||
result.add(new IntVector2(i, j));
|
result.add(new IntVector2(i, j));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(includeA) {
|
||||||
|
result.add(a);
|
||||||
|
}
|
||||||
|
if(includeB) {
|
||||||
|
result.add(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
//https://rosettacode.org/wiki/Find_the_intersection_of_two_lines#Java
|
||||||
|
public static Point2D.Float calculateInterceptionPoint(Point2D s1, Point2D s2, Point2D d1, Point2D d2) {
|
||||||
|
double a1 = s2.getY() - s1.getY();
|
||||||
|
double b1 = s1.getX() - s2.getX();
|
||||||
|
double c1 = a1 * s1.getX() + b1 * s1.getY();
|
||||||
|
|
||||||
|
double a2 = d2.getY() - d1.getY();
|
||||||
|
double b2 = d1.getX() - d2.getX();
|
||||||
|
double c2 = a2 * d1.getX() + b2 * d1.getY();
|
||||||
|
|
||||||
|
double delta = a1 * b2 - a2 * b1;
|
||||||
|
return new Point2D.Float((float) ((b2 * c1 - b1 * c2) / delta), (float) ((a1 * c2 - a2 * c1) / delta));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Computes all wireframe lines for the given rectangle. */
|
||||||
|
public static ArrayList<Line2D.Float> getWireframe(Rectangle.Float rect) {
|
||||||
|
ArrayList<Line2D.Float> result = new ArrayList<>();
|
||||||
|
|
||||||
|
Point2D.Float lt = new Point2D.Float(rect.x, rect.y);
|
||||||
|
Point2D.Float rt = new Point2D.Float(rect.x + rect.width, rect.y);
|
||||||
|
Point2D.Float rb = new Point2D.Float(rect.x + rect.width, rect.y + rect.height);
|
||||||
|
Point2D.Float lb = new Point2D.Float(rect.x, rect.y + rect.height);
|
||||||
|
|
||||||
|
result.add(new Line2D.Float(lt, rt));
|
||||||
|
result.add(new Line2D.Float(rt, rb));
|
||||||
|
result.add(new Line2D.Float(lb, rb));
|
||||||
|
result.add(new Line2D.Float(lt, lb));
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,32 +7,27 @@ import uulm.teamname.marvelous.gamelibrary.IntVector2;
|
|||||||
import uulm.teamname.marvelous.gamelibrary.entities.*;
|
import uulm.teamname.marvelous.gamelibrary.entities.*;
|
||||||
import uulm.teamname.marvelous.gamelibrary.entities.Character;
|
import uulm.teamname.marvelous.gamelibrary.entities.Character;
|
||||||
import uulm.teamname.marvelous.gamelibrary.events.Event;
|
import uulm.teamname.marvelous.gamelibrary.events.Event;
|
||||||
import uulm.teamname.marvelous.gamelibrary.events.EventBuilder;
|
|
||||||
import uulm.teamname.marvelous.gamelibrary.events.EventType;
|
import uulm.teamname.marvelous.gamelibrary.events.EventType;
|
||||||
import uulm.teamname.marvelous.gamelibrary.json.config.*;
|
import uulm.teamname.marvelous.gamelibrary.json.config.*;
|
||||||
import uulm.teamname.marvelous.gamelibrary.requests.*;
|
import uulm.teamname.marvelous.gamelibrary.requests.*;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.*;
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.concurrent.ThreadLocalRandom;
|
import java.util.concurrent.ThreadLocalRandom;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.*;
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
import static org.mockito.Mockito.*;
|
|
||||||
|
|
||||||
class GameLogicTest {
|
class GameLogicTest {
|
||||||
private static final Iterator<Integer> randomIntegers = ThreadLocalRandom.current().ints().iterator();
|
private static final Iterator<Integer> randomIntegers = ThreadLocalRandom.current().ints().iterator();
|
||||||
|
|
||||||
private static PartyConfig partyConfig;
|
private static final PartyConfig partyConfig = new PartyConfig();
|
||||||
private static CharacterConfig characterConfig;
|
private static final CharacterConfig characterConfig = new CharacterConfig();
|
||||||
private static ScenarioConfig scenarioConfig;
|
private static final ScenarioConfig scenarioConfig = new ScenarioConfig();
|
||||||
|
|
||||||
private static ArrayList<Integer> player1Selection = new ArrayList<>();
|
private static final ArrayList<Integer> player1Selection = new ArrayList<>();
|
||||||
private static ArrayList<Integer> player2Selection = new ArrayList<>();
|
private static final ArrayList<Integer> player2Selection = new ArrayList<>();
|
||||||
|
|
||||||
@BeforeAll
|
@BeforeAll
|
||||||
static void setUp() {
|
static void setUp() {
|
||||||
partyConfig = new PartyConfig();
|
|
||||||
partyConfig.maxRounds = 100;
|
partyConfig.maxRounds = 100;
|
||||||
partyConfig.mindStoneCD = 2;
|
partyConfig.mindStoneCD = 2;
|
||||||
partyConfig.powerStoneCD = 3;
|
partyConfig.powerStoneCD = 3;
|
||||||
@ -42,13 +37,11 @@ class GameLogicTest {
|
|||||||
partyConfig.timeStoneCD = 7;
|
partyConfig.timeStoneCD = 7;
|
||||||
partyConfig.mindStoneDMG = 3;
|
partyConfig.mindStoneDMG = 3;
|
||||||
|
|
||||||
characterConfig = new CharacterConfig();
|
|
||||||
characterConfig.characters = new CharacterProperties[] {
|
characterConfig.characters = new CharacterProperties[] {
|
||||||
generateCharacter(), generateCharacter(), generateCharacter(), generateCharacter(), generateCharacter(), generateCharacter(),
|
generateCharacter(), generateCharacter(), generateCharacter(), generateCharacter(), generateCharacter(), generateCharacter(),
|
||||||
generateCharacter(), generateCharacter(), generateCharacter(), generateCharacter(), generateCharacter(), generateCharacter()
|
generateCharacter(), generateCharacter(), generateCharacter(), generateCharacter(), generateCharacter(), generateCharacter()
|
||||||
};
|
};
|
||||||
|
|
||||||
scenarioConfig = new ScenarioConfig();
|
|
||||||
scenarioConfig.name = generateName(20);
|
scenarioConfig.name = generateName(20);
|
||||||
scenarioConfig.author = generateName(20);
|
scenarioConfig.author = generateName(20);
|
||||||
scenarioConfig.scenario = new FieldType[30][30];
|
scenarioConfig.scenario = new FieldType[30][30];
|
||||||
@ -179,6 +172,29 @@ class GameLogicTest {
|
|||||||
assertEquals(EventType.GamestateEvent, actual.type, "First event should be a GameStateEvent");
|
assertEquals(EventType.GamestateEvent, actual.type, "First event should be a GameStateEvent");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testRasterize() {
|
||||||
|
ArrayList<IntVector2> result = GameLogic.rasterize(new IntVector2(0, 0), new IntVector2(1, 1), false, false);
|
||||||
|
|
||||||
|
assertEquals(0, result.size(), "Diagonals are not included");
|
||||||
|
|
||||||
|
|
||||||
|
IntVector2[] result2 = GameLogic.rasterize(new IntVector2(0, 0), new IntVector2(4, 0)).toArray(new IntVector2[0]);
|
||||||
|
|
||||||
|
assertEquals(new HashSet<>(Arrays.asList(
|
||||||
|
new IntVector2(0, 0), new IntVector2(1, 0), new IntVector2(2, 0), new IntVector2(3, 0), new IntVector2(4, 0)
|
||||||
|
)), new HashSet<>(Arrays.asList(result2)), "Straight lines work correctly");
|
||||||
|
|
||||||
|
|
||||||
|
IntVector2[] result3 = GameLogic.rasterize(new IntVector2(0, 0), new IntVector2(2, 1)).toArray(new IntVector2[0]);
|
||||||
|
|
||||||
|
assertEquals(new HashSet<>(Arrays.asList(
|
||||||
|
new IntVector2(0, 0), new IntVector2(1, 0), new IntVector2(1, 1), new IntVector2(2, 1))
|
||||||
|
), new HashSet<>(Arrays.asList(result3)), "Tilted lines work correctly");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// @Provide("gamestate")
|
// @Provide("gamestate")
|
||||||
// Arbitrary<GameState> gamestate() {
|
// Arbitrary<GameState> gamestate() {
|
||||||
// var states = Arbitraries.integers()
|
// var states = Arbitraries.integers()
|
||||||
|
Loading…
Reference in New Issue
Block a user