Skip to content

Commit

Permalink
Validation
Browse files Browse the repository at this point in the history
  • Loading branch information
Kkobarii committed Jan 30, 2024
2 parents be9b1f5 + 53a7890 commit aa3c513
Show file tree
Hide file tree
Showing 16 changed files with 451 additions and 304 deletions.
1 change: 0 additions & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,6 @@
<artifactId>dotenv-java</artifactId>
<version>3.0.0</version>
</dependency>

</dependencies>

<build>
Expand Down
455 changes: 227 additions & 228 deletions src/main/java/cz/trailsthroughshadows/algorithm/Dungeon.java

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,8 @@ public abstract class Entity {
public Hex hex;
@Transient
public List<Effect> activeEffects = new ArrayList<>();
@Column(nullable = false)
public CombatStyle combatStyle = CombatStyle.MELEE;

public abstract String getName();

// public abstract List<Action> getActions();

enum CombatStyle {
MELEE,
RANGED,
}
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
package cz.trailsthroughshadows.algorithm.location;

import cz.trailsthroughshadows.algorithm.utils.Vec3;
import cz.trailsthroughshadows.api.table.schematic.hex.Hex;
import cz.trailsthroughshadows.api.table.schematic.location.ILocation;
import cz.trailsthroughshadows.api.table.schematic.part.Part;

import java.util.List;

public abstract class LocationImpl implements ILocation {

public Part getPart(Hex hex) {
Expand All @@ -15,21 +12,4 @@ public Part getPart(Hex hex) {
.findFirst()
.orElse(null);
}

public int getDistance(Hex hex1, Hex hex2) {
Vec3<Integer> vec = new Vec3<>(hex1.getQ() - hex2.getQ(), hex1.getR() - hex2.getR(), hex1.getS() - hex2.getS());
return (Math.abs(vec.x()) + Math.abs(vec.y()) + Math.abs(vec.z())) / 2;
}

public List<Hex> getNeighbors(Hex hex) {
return getNeighbors(hex, 1);
}

public List<Hex> getNeighbors(Hex hex, int range) {
List<Hex> neighbors = getPart(hex).getHexes().stream()
.filter(neighbor -> hex != neighbor && getDistance(hex, neighbor) <= range)
.toList();

return neighbors;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
package cz.trailsthroughshadows.algorithm.location;

import cz.trailsthroughshadows.algorithm.util.Vec3;
import cz.trailsthroughshadows.api.table.schematic.hex.Hex;
import cz.trailsthroughshadows.api.table.schematic.part.Part;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

import java.util.*;

@RequiredArgsConstructor
@Slf4j
public class Navigation {

private final List<Part> parts;

public Navigation(Part... parts) {
this.parts = Arrays.asList(parts);
}

public Part getPart(Hex hex) {
return parts.stream()
.filter(part -> part.getId() == hex.getKey().getIdPart())
.findFirst()
.orElse(null);
}

public int getDistance(Hex hex1, Hex hex2) {
Vec3<Integer> vec = new Vec3<>(hex1.getQ() - hex2.getQ(), hex1.getR() - hex2.getR(), hex1.getS() - hex2.getS());
return (Math.abs(vec.x()) + Math.abs(vec.y()) + Math.abs(vec.z())) / 2;
}

public List<Hex> getNeighbors(Hex hex) {
return getNeighbors(hex, 1);
}

public List<Hex> getNeighbors(Hex hex, int range) {
return getPart(hex).getHexes().stream()
.filter(neighbor -> hex != neighbor && getDistance(hex, neighbor) <= range)
.toList();
}

public List<Hex> getPath(Hex hex1, Hex hex2) {
Map<Hex, Integer> distances = new HashMap<>();
Queue<Hex> queue = new LinkedList<>();
List<Hex> path = new ArrayList<>();

distances.put(hex1, 0);
queue.add(hex1);

// calculate distance from hex1
while (!queue.isEmpty()) {
Hex current = queue.poll();
int distance = distances.get(current);

List<Hex> neighbors = getNeighbors(current);
for (Hex neighbor : neighbors) {
if (distances.containsKey(neighbor))
continue;

distances.put(neighbor, distance + 1);
queue.add(neighbor);
}
}

// hex2 is not reachable from hex1
if (!distances.containsKey(hex2))
return null;

// create path to hex2
Hex currentHex = hex2;
int currentDistance = distances.get(hex2);

while (!currentHex.equals(hex1)) {
path.add(currentHex);

List<Hex> neighbors = getNeighbors(currentHex);
for (Hex neighbor : neighbors) {
int neighborDistance = distances.get(neighbor);

if (neighborDistance < currentDistance) {
currentHex = neighbor;
currentDistance = neighborDistance;
break;
}
}
}
path.add(hex1);

Collections.reverse(path);
return path;
}
}
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
package cz.trailsthroughshadows.algorithm.util;

import java.util.ArrayList;
import java.util.List;

public class ListUtil {
public class List {
public static <T> T getRandom(T[] array) {
return array[(int) (Math.random() * array.length)];
}

@SafeVarargs
public static <T> List<T> union(List<? extends T>... lists) {
public static <T> java.util.List<T> union(java.util.List<? extends T>... lists) {
ArrayList<T> union = new ArrayList<>();
for (List<? extends T> list : lists) {
for (java.util.List<? extends T> list : lists) {
union.addAll(list);
}
return union;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package cz.trailsthroughshadows.algorithm.util;

public record Vec3<T>(T x, T y, T z) {
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -5,50 +5,46 @@
import cz.trailsthroughshadows.api.rest.model.error.RestError;
import cz.trailsthroughshadows.api.rest.model.error.type.MessageError;
import cz.trailsthroughshadows.api.table.schematic.part.Part;
import lombok.extern.slf4j.Slf4j;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

import java.util.ArrayList;
import java.util.List;

@Slf4j
@Log4j2
@Component
@RestController(value = "validation")
public class ValidationController {

private ValidationService validationService;

@PostMapping("/validate/part")
public ResponseEntity<RestResponse> validatePart(@RequestBody Part part) {
List<String> errors = new ArrayList<>();

// TODO: Implement Part validation @Bačkorče
// Validations?:
// 1. Part must have at least 5 hexes
// 2. Part must have at most 50 hexes
// 3. Part is maximum 8 hexes wide and 8 hexes tall
// 4. All hexes must be connected

if (part.getHexes().size() < 5) {
errors.add("Part must have at least 5 hexes!");
}

if (part.getHexes().size() > 50) {
errors.add("Part must have at most 50 hexes!");
}
log.debug("Validating part " + part.getTag());
List<String> errors = validationService.validatePart(part);

if (errors.isEmpty()) {
return RestResponse.of(HttpStatus.OK,"Part is valid.");
log.debug("Part is valid!");
return new ResponseEntity<>(new RestResponse(HttpStatus.OK, "Part is valid!"), HttpStatus.OK);
}

log.debug("Part is not valid!");
RestError error = new RestError(HttpStatus.NOT_ACCEPTABLE, "Part is not valid!");
for (var e : errors) {
log.debug(" > " + e);
error.addSubError(new MessageError(e));
}

throw new RestException(error);
}

@Autowired
public void setValidationService(ValidationService validationService) {
this.validationService = validationService;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
package cz.trailsthroughshadows.api.rest.endpoints;

import cz.trailsthroughshadows.algorithm.location.Navigation;
import cz.trailsthroughshadows.api.table.schematic.hex.Hex;
import cz.trailsthroughshadows.api.table.schematic.part.Part;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

@Service
@Slf4j
public class ValidationService {

// TODO: Implement Part validation @Bačkorče
// Validations?:
// 1. Part must have at least 5 hexes
// 2. Part must have at most 50 hexes
// 3. Part is maximum 8 hexes wide and 8 hexes tall
// 4. All hexes must be connected
public List<String> validatePart(Part part) {
List<String> errors = new ArrayList<>();

int minHexes = 5;
int maxHexes = 50;
int maxHexesWide = 8;

// min 5 hexes
if (part.getHexes().size() < minHexes) {
errors.add("Part must have at least %d hexes!".formatted(minHexes));
}

// max 50 hexes
if (part.getHexes().size() > maxHexes) {
errors.add("Part must have at most 50 hexes!");
}

// max 8 hexes wide
int diffQ = part.getHexes().stream().mapToInt(Hex::getQ).max().getAsInt() - part.getHexes().stream().mapToInt(Hex::getQ).min().getAsInt();
int diffR = part.getHexes().stream().mapToInt(Hex::getR).max().getAsInt() - part.getHexes().stream().mapToInt(Hex::getR).min().getAsInt();
int diffS = part.getHexes().stream().mapToInt(Hex::getS).max().getAsInt() - part.getHexes().stream().mapToInt(Hex::getS).min().getAsInt();
if (diffQ > maxHexesWide || diffR > maxHexesWide || diffS > maxHexesWide) {
errors.add("Part must not be wider than %d hexes!".formatted(maxHexesWide));
}

// no hexes can be on the same position
int duplicates = 0;
for (Hex hex1 : part.getHexes()) {
for (Hex hex2 : part.getHexes()) {
if (hex1 == hex2)
continue;

if (hex1.getQ() == hex2.getQ() && hex1.getR() == hex2.getR() && hex1.getS() == hex2.getS()) {
duplicates++;
break;
}
}
}
if (duplicates > 0)
errors.add("Part must not have duplicate hexes!");

// every hex has to have correct coordinates
for (Hex hex : part.getHexes()) {
if (hex.getQ() + hex.getR() + hex.getS() != 0) {
errors.add("Every hex has to have correct coordinates!");
break;
}
}

// must include center hex
Optional<Hex> centerHex = part.getHexes().stream().filter(hex -> hex.getQ() == 0 && hex.getR() == 0 && hex.getS() == 0).findFirst();
if (centerHex.isEmpty()) {
errors.add("Part must include a center hex!");
return errors;
}

// all hexes must be connected
Navigation navigation = new Navigation(part);

for (Hex hex : part.getHexes()) {
if (hex == centerHex.get())
continue;

if (navigation.getPath(centerHex.get(), hex) == null) {
errors.add("All hexes must be connected!");
break;
}
}

return errors;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,6 @@ public class Hex {
@Column(name = "sCord", nullable = false)
private int s;

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Hex hex)) return false;
return q == hex.q && r == hex.r && s == hex.s && key.equals(hex.key);
}

@Data
@Embeddable
public static class HexId implements Serializable {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,21 +1,18 @@
package cz.trailsthroughshadows.api.table.schematic.part;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import cz.trailsthroughshadows.api.table.schematic.hex.Hex;
import cz.trailsthroughshadows.api.table.schematic.hex.LocationDoor;
import jakarta.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.Set;
import java.util.List;

@Data
@Entity
@NoArgsConstructor
@AllArgsConstructor
@Table(name = "Part")
@JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
public class Part {

@Id
Expand All @@ -25,11 +22,11 @@ public class Part {
@Column(name = "tag")
private String tag;

@OneToMany(mappedBy = "key.idPart", cascade = CascadeType.ALL)
private Set<Hex> hexes;
@OneToMany(mappedBy = "key.idPart", cascade = CascadeType.ALL, orphanRemoval = true)
private List<Hex> hexes;

@OneToMany(mappedBy = "key.fromPart", cascade = CascadeType.ALL)
private Set<LocationDoor> doors;
// @OneToMany(mappedBy = "key.fromPart", cascade = CascadeType.ALL)
// private Set<LocationDoor> doors;

@Column(name = "usages", columnDefinition = "INT default 0")
private int usages = 0;
Expand Down
Loading

0 comments on commit aa3c513

Please sign in to comment.