it is now a website
This commit is contained in:
parent
a3cf7b6a27
commit
5b1584e300
18 changed files with 939 additions and 55 deletions
5
pom.xml
5
pom.xml
|
|
@ -22,6 +22,11 @@
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-web</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.junit.jupiter</groupId>
|
<groupId>org.junit.jupiter</groupId>
|
||||||
<artifactId>junit-jupiter</artifactId>
|
<artifactId>junit-jupiter</artifactId>
|
||||||
|
|
|
||||||
12
src/main/java/assessment/Application.java
Normal file
12
src/main/java/assessment/Application.java
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
package assessment;
|
||||||
|
|
||||||
|
import org.springframework.boot.SpringApplication;
|
||||||
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
|
|
||||||
|
@SpringBootApplication
|
||||||
|
public class Application {
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
SpringApplication.run(Application.class, args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
package assessment;
|
package assessment.algorithm;
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
import java.io.BufferedReader;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
@ -7,6 +7,7 @@ import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public record Grid(List<List<Integer>> grid) {
|
public record Grid(List<List<Integer>> grid) {
|
||||||
|
private static final double timeValueFactor = .2;
|
||||||
|
|
||||||
public static Grid fromFile(String resource) {
|
public static Grid fromFile(String resource) {
|
||||||
try {
|
try {
|
||||||
|
|
@ -27,12 +28,30 @@ public record Grid(List<List<Integer>> grid) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public int get(int r, int c){
|
public int getInitialValue(int x, int y){
|
||||||
return grid.get(r).get(c);
|
return grid.get(y).get(x);
|
||||||
}
|
}
|
||||||
|
|
||||||
//assumes square
|
//assumes square
|
||||||
public int getWidth(){
|
public int getWidth(){
|
||||||
return grid.size();
|
return grid.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* de waarde van een punt x,y wordt bepaald door de beginwaarde, tenzij we er al geweest zijn
|
||||||
|
* dan telt de tijd sinds we er geweest zijn = afstand in sindsdien afgelegd pad
|
||||||
|
* de waarde is rechtevenredig met de afstand
|
||||||
|
*/
|
||||||
|
public double getCurrentValue(Path path, int x, int y) {
|
||||||
|
int gridValue = getInitialValue(x, y);
|
||||||
|
if (path.hasPoint(this, x, y)) {
|
||||||
|
// been there
|
||||||
|
int distanceInPath = path.getDistanceInPath(x, y) - 1;
|
||||||
|
double increment = gridValue * timeValueFactor;
|
||||||
|
|
||||||
|
return Math.min((distanceInPath - 1) * increment, gridValue);
|
||||||
|
} else {
|
||||||
|
return gridValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,10 +1,16 @@
|
||||||
package assessment;
|
package assessment.algorithm;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
//TODO make non static (CH)
|
/**
|
||||||
public class BestPathFinder {
|
* Finds most valuable path given distance/time covered (t) and elapsed compute time (T)
|
||||||
private static final double timeValueFactor = .2;
|
* for a drone moving through a square grid of size N that has cells that have varying value
|
||||||
|
* <p/>
|
||||||
|
* outstanding questions:
|
||||||
|
* * is a cell of (initial) value 0 equally valuable as a cell just occupied?
|
||||||
|
* -> probably, what is the chance that you find a 'treasure' behind it, that you would have not found via an alternative path?
|
||||||
|
*/
|
||||||
|
public class OptimalPathFinder {
|
||||||
|
|
||||||
// paths to be considered
|
// paths to be considered
|
||||||
private final PriorityQueue<Path> paths = new PriorityQueue<>();
|
private final PriorityQueue<Path> paths = new PriorityQueue<>();
|
||||||
|
|
@ -20,8 +26,8 @@ public class BestPathFinder {
|
||||||
* @param y startpositie Y
|
* @param y startpositie Y
|
||||||
* @return het meest waardevolle pad
|
* @return het meest waardevolle pad
|
||||||
*/
|
*/
|
||||||
public Path findMaxValPath(Grid g, int N, int t, int T, int x, int y) {
|
public Path findOptimalPath(Grid g, int N, int t, long T, int x, int y) {
|
||||||
Path path = Path.newPath(g, x, y);
|
Path path = Path.newPath(g, Point.create(g, x, y));
|
||||||
paths.add(path);
|
paths.add(path);
|
||||||
// overall best path
|
// overall best path
|
||||||
Path max = path;
|
Path max = path;
|
||||||
|
|
@ -56,27 +62,27 @@ public class BestPathFinder {
|
||||||
// find best new directions
|
// find best new directions
|
||||||
List<Point> newDirections = new ArrayList<>();
|
List<Point> newDirections = new ArrayList<>();
|
||||||
if (y > 0) {
|
if (y > 0) {
|
||||||
newDirections.add(new Point(x, y - 1, getValueFromGrid(g, path, x, y - 1)));
|
newDirections.add(Point.create(g, path, x, y - 1));
|
||||||
if (x < N - 1) {
|
if (x < N - 1) {
|
||||||
newDirections.add(new Point(x + 1, y - 1, getValueFromGrid(g, path, x + 1, y - 1)));
|
newDirections.add(Point.create(g, path, x + 1, y - 1));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (x > 0) {
|
if (x > 0) {
|
||||||
newDirections.add(new Point(x - 1, y, getValueFromGrid(g, path, x - 1, y)));
|
newDirections.add(Point.create(g, path, x - 1, y));
|
||||||
if (y > 0) {
|
if (y > 0) {
|
||||||
newDirections.add(new Point(x - 1, y - 1, getValueFromGrid(g, path, x - 1, y - 1)));
|
newDirections.add(Point.create(g, path, x - 1, y - 1));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (x < N - 1) {
|
if (x < N - 1) {
|
||||||
newDirections.add(new Point(x + 1, y, getValueFromGrid(g, path, x + 1, y)));
|
newDirections.add(Point.create(g, path, x + 1, y));
|
||||||
if (y < N - 1) {
|
if (y < N - 1) {
|
||||||
newDirections.add(new Point(x + 1, y + 1, getValueFromGrid(g, path, x + 1, y + 1)));
|
newDirections.add(Point.create(g, path, x + 1, y + 1));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (y < N - 1) {
|
if (y < N - 1) {
|
||||||
newDirections.add(new Point(x, y + 1, getValueFromGrid(g, path, x, y + 1)));
|
newDirections.add(Point.create(g, path, x, y + 1));
|
||||||
if (x > 0)
|
if (x > 0)
|
||||||
newDirections.add(new Point(x - 1, y + 1, getValueFromGrid(g, path, x - 1, y + 1)));
|
newDirections.add(Point.create(g, path, x - 1, y + 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!newDirections.isEmpty()) {
|
if (!newDirections.isEmpty()) {
|
||||||
|
|
@ -98,7 +104,7 @@ public class BestPathFinder {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!pointsAdded) {
|
if (!pointsAdded) {
|
||||||
//evict
|
// dead end, evict
|
||||||
Path ended = paths.poll();
|
Path ended = paths.poll();
|
||||||
if (ended != null && ended.value() > max.value()) {
|
if (ended != null && ended.value() > max.value()) {
|
||||||
max = ended;
|
max = ended;
|
||||||
|
|
@ -108,22 +114,4 @@ public class BestPathFinder {
|
||||||
}
|
}
|
||||||
return max;
|
return max;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* de waarde van een punt x,y wordt bepaald door de beginwaarde, tenzij we er al geweest zijn
|
|
||||||
* dan telt de tijd sinds we er geweest zijn = afstand in sindsdien afgelegd pad
|
|
||||||
* de waarde is rechtevenredig met de afstand
|
|
||||||
*/
|
|
||||||
private double getValueFromGrid(Grid grid, Path path, int x, int y) {
|
|
||||||
int gridValue = grid.get(x, y);
|
|
||||||
if (path.hasPoint(grid, x, y)) {
|
|
||||||
// been there
|
|
||||||
int distanceInPath = path.getDistanceInPath(x, y);
|
|
||||||
double increment = gridValue * timeValueFactor;
|
|
||||||
|
|
||||||
return Math.min((distanceInPath - 1) * increment, gridValue);
|
|
||||||
} else {
|
|
||||||
return gridValue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
package assessment;
|
package assessment.algorithm;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
|
@ -8,7 +8,7 @@ public class Path implements Comparable<Path> {
|
||||||
// beide bevatten de al afgelegde punten
|
// beide bevatten de al afgelegde punten
|
||||||
|
|
||||||
// points is bedoeld om door de punten te lopen
|
// points is bedoeld om door de punten te lopen
|
||||||
private final ArrayList<Point> points = new ArrayList<>();
|
public final ArrayList<Point> points = new ArrayList<>();
|
||||||
|
|
||||||
// trodden is bedoeld om zo snel mogelijk vast te stellen of we al op het punt geweest zijn
|
// trodden is bedoeld om zo snel mogelijk vast te stellen of we al op het punt geweest zijn
|
||||||
private final HashSet<Integer> trodden = new HashSet<>();
|
private final HashSet<Integer> trodden = new HashSet<>();
|
||||||
|
|
@ -17,9 +17,9 @@ public class Path implements Comparable<Path> {
|
||||||
}
|
}
|
||||||
|
|
||||||
// meh row/col vs x/y
|
// meh row/col vs x/y
|
||||||
static Path newPath(Grid g, int x, int y) {
|
public static Path newPath(Grid g, Point start) {
|
||||||
Path p = new Path();
|
Path p = new Path();
|
||||||
p.add(g, new Point(x, y, g.get(y, x)));
|
p.add(g, start);
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -28,14 +28,14 @@ public class Path implements Comparable<Path> {
|
||||||
trodden.add(point.y * g.getWidth() + point.x);
|
trodden.add(point.y * g.getWidth() + point.x);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Double value() {
|
public double value() {
|
||||||
return points.stream().mapToDouble(point -> point.value).sum();
|
return points.stream().mapToDouble(point -> point.value).sum();
|
||||||
}
|
}
|
||||||
|
|
||||||
// compare descending, highest value first
|
// compare descending, highest value first
|
||||||
@Override
|
@Override
|
||||||
public int compareTo(Path o) {
|
public int compareTo(Path o) {
|
||||||
return -this.value().compareTo(o.value());
|
return -Double.compare(this.value(), o.value());
|
||||||
}
|
}
|
||||||
|
|
||||||
public int length() {
|
public int length() {
|
||||||
|
|
@ -1,17 +1,28 @@
|
||||||
package assessment;
|
package assessment.algorithm;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
|
|
||||||
public class Point implements Comparable<Point> {
|
public class Point implements Comparable<Point> {
|
||||||
|
|
||||||
final int x;
|
public final int x;
|
||||||
final int y;
|
public final int y;
|
||||||
final double value;
|
public double value;
|
||||||
|
|
||||||
public Point(int x, int y, double value) {
|
private Point(int x, int y, double initialValue) {
|
||||||
this.x = x;
|
this.x = x;
|
||||||
this.y = y;
|
this.y = y;
|
||||||
this.value = value;
|
this.value = initialValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Point create(Grid g, int x, int y) {
|
||||||
|
return new Point(x, y, g.getInitialValue(x, y));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Point create(Grid g, Path p, int x, int y) {
|
||||||
|
return new Point(x, y, g.getCurrentValue(p, x, y));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -37,4 +48,5 @@ public class Point implements Comparable<Point> {
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
return Objects.hash(x, y);
|
return Objects.hash(x, y);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
29
src/main/java/assessment/restapi/FlightPathApi.java
Normal file
29
src/main/java/assessment/restapi/FlightPathApi.java
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
package assessment.restapi;
|
||||||
|
|
||||||
|
import assessment.algorithm.OptimalPathFinder;
|
||||||
|
import assessment.algorithm.Grid;
|
||||||
|
import assessment.algorithm.Path;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.PathVariable;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
public class FlightPathApi {
|
||||||
|
|
||||||
|
@GetMapping(path = "/api/path/{gridname}/{pathlength}/{maxMillis}/{x}/{y}")
|
||||||
|
public PathDto getOptimalPath(@PathVariable String gridname,
|
||||||
|
@PathVariable int pathlength,
|
||||||
|
@PathVariable long maxMillis,
|
||||||
|
@PathVariable int x,
|
||||||
|
@PathVariable int y) {
|
||||||
|
Grid grid = Grid.fromFile("grids/" + gridname + ".txt");
|
||||||
|
Path p = new OptimalPathFinder().findOptimalPath(grid, grid.getWidth(), pathlength, maxMillis, x, y);
|
||||||
|
return new PathDto(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping(path = "/api/grid/{gridname}")
|
||||||
|
public GridDto getGrid(@PathVariable String gridname) {
|
||||||
|
Grid grid = Grid.fromFile("grids/" + gridname + ".txt");
|
||||||
|
return new GridDto(grid);
|
||||||
|
}
|
||||||
|
}
|
||||||
23
src/main/java/assessment/restapi/GridDto.java
Normal file
23
src/main/java/assessment/restapi/GridDto.java
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
package assessment.restapi;
|
||||||
|
|
||||||
|
import assessment.algorithm.Grid;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class GridDto {
|
||||||
|
private List<List<Integer>> grid;
|
||||||
|
private int size;
|
||||||
|
|
||||||
|
public GridDto(Grid grid) {
|
||||||
|
this.grid = grid.grid();
|
||||||
|
this.size = grid.getWidth();
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<List<Integer>> getGrid() {
|
||||||
|
return grid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getSize() {
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
}
|
||||||
25
src/main/java/assessment/restapi/PathDto.java
Normal file
25
src/main/java/assessment/restapi/PathDto.java
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
package assessment.restapi;
|
||||||
|
|
||||||
|
import assessment.algorithm.Path;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
public class PathDto {
|
||||||
|
private final List<PointDto> points;
|
||||||
|
private final double value;
|
||||||
|
|
||||||
|
public PathDto(Path p){
|
||||||
|
this.points = p.points.stream().map(PointDto::new).collect(Collectors.toList());
|
||||||
|
this.value = p.value();
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<PointDto> getPoints() {
|
||||||
|
return points;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getValue() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
22
src/main/java/assessment/restapi/PointDto.java
Normal file
22
src/main/java/assessment/restapi/PointDto.java
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
package assessment.restapi;
|
||||||
|
|
||||||
|
import assessment.algorithm.Point;
|
||||||
|
|
||||||
|
public class PointDto {
|
||||||
|
private final int x;
|
||||||
|
private final int y;
|
||||||
|
|
||||||
|
public PointDto(Point p) {
|
||||||
|
this.x = p.x;
|
||||||
|
this.y = p.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getX() {
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getY() {
|
||||||
|
return y;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
62
src/main/resources/static/css/style.css
Normal file
62
src/main/resources/static/css/style.css
Normal file
|
|
@ -0,0 +1,62 @@
|
||||||
|
body{
|
||||||
|
background-color: green;
|
||||||
|
}
|
||||||
|
|
||||||
|
canvas {
|
||||||
|
width: 100vw;
|
||||||
|
height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
#console {
|
||||||
|
font: 13px Arial, sans-serif;
|
||||||
|
padding: 5px;
|
||||||
|
color: greenyellow;
|
||||||
|
background: black;
|
||||||
|
position: absolute;
|
||||||
|
right: 10px;
|
||||||
|
bottom: 0;
|
||||||
|
width: 20%;
|
||||||
|
height: 10em;
|
||||||
|
border: 2px solid darkgray;
|
||||||
|
border-radius: 10px;
|
||||||
|
z-index: 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
#prompt {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
width: 90%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#command_input {
|
||||||
|
background: black;
|
||||||
|
color: greenyellow;
|
||||||
|
outline: none;
|
||||||
|
width: 90%;
|
||||||
|
height: 1em;
|
||||||
|
resize: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#command_history {
|
||||||
|
font-size: 12px;
|
||||||
|
color: greenyellow;
|
||||||
|
position: absolute;
|
||||||
|
bottom: 1.5em;
|
||||||
|
max-height: 20em;
|
||||||
|
width: 100%;
|
||||||
|
overflow-y: visible;
|
||||||
|
overflow-x: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.multiline {
|
||||||
|
border-top: 1px slategray solid;
|
||||||
|
border-left: 1px slategray solid;
|
||||||
|
border-right: 1px slategray solid;
|
||||||
|
border-bottom: none transparent;
|
||||||
|
border-top-left-radius: 3px;
|
||||||
|
border-top-right-radius: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.single_line {
|
||||||
|
border: none transparent;
|
||||||
|
}
|
||||||
24
src/main/resources/static/index.html
Normal file
24
src/main/resources/static/index.html
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>OptimalPathFinder</title>
|
||||||
|
<link rel="stylesheet" href="css/style.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<canvas id="myCanvas" width="500px" height="500px"></canvas>
|
||||||
|
|
||||||
|
<div id="console">
|
||||||
|
<div id="command_history">
|
||||||
|
<div id="bottom"></div>
|
||||||
|
</div>
|
||||||
|
<label id="prompt">>
|
||||||
|
<textarea id="command_input" class="single_line" autofocus></textarea>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script src="js/grid.js"></script>
|
||||||
|
<script src="js/console.js"></script>
|
||||||
|
<script src="js/scanner.js"></script>
|
||||||
|
<script src="js/parser.js"></script>
|
||||||
|
|
||||||
|
</body>
|
||||||
596
src/main/resources/static/js/console.js
Normal file
596
src/main/resources/static/js/console.js
Normal file
|
|
@ -0,0 +1,596 @@
|
||||||
|
const command_input_element = document.getElementById('command_input');
|
||||||
|
const command_history_element = document.getElementById('command_history');
|
||||||
|
let command_history = [''];
|
||||||
|
let command_history_index = 0;
|
||||||
|
let token_index = 0;
|
||||||
|
const bindings = {};
|
||||||
|
const state = {};
|
||||||
|
const keywords = {
|
||||||
|
'true': true,
|
||||||
|
'false': false,
|
||||||
|
'pi': Math.PI,
|
||||||
|
'PI': Math.PI,
|
||||||
|
'e': Math.E
|
||||||
|
}
|
||||||
|
|
||||||
|
let tokens;
|
||||||
|
|
||||||
|
const adjust_input_element_height = function () {
|
||||||
|
let num_lines = command_input_element.value.split(/\n/).length;
|
||||||
|
command_input_element.setAttribute('style', 'height: ' + num_lines + 'em');
|
||||||
|
if (num_lines > 1) {
|
||||||
|
command_input_element.setAttribute('class', 'multiline');
|
||||||
|
} else {
|
||||||
|
command_input_element.setAttribute('class', 'single_line');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// command_input_element.onkeypress = function handle_key_input(event) {
|
||||||
|
// if (event.key === 'Enter') {
|
||||||
|
// event.preventDefault();
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
command_input_element.onkeyup = function handle_key_input(event) {
|
||||||
|
adjust_input_element_height();
|
||||||
|
if (event.key === 'c' && event.ctrlKey) {
|
||||||
|
command_input_element.value = '';
|
||||||
|
}
|
||||||
|
if (event.key === 'ArrowUp' && !event.shiftKey) {
|
||||||
|
if (command_history_index > -1) {
|
||||||
|
command_input_element.value = command_history[command_history_index];
|
||||||
|
if (command_history_index > 0) {
|
||||||
|
command_history_index -= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (event.key === 'ArrowDown' && !event.shiftKey) {
|
||||||
|
if (command_history_index < command_history.length - 1) {
|
||||||
|
command_history_index += 1;
|
||||||
|
command_input_element.value = command_history[command_history_index];
|
||||||
|
} else {
|
||||||
|
command_input_element.value = '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (event.key === 'Enter') {
|
||||||
|
handle_enter();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handle_enter = function () {
|
||||||
|
let command = command_input_element.value.trim();
|
||||||
|
command_input_element.value = '';
|
||||||
|
adjust_input_element_height();
|
||||||
|
|
||||||
|
if (command.length > 0) {
|
||||||
|
command_history_element.innerText += command + "\n";
|
||||||
|
command_input_element.value = '';
|
||||||
|
command_history_index = command_history.length;
|
||||||
|
|
||||||
|
scan(command);
|
||||||
|
let statement = parse();
|
||||||
|
let value = evaluate(statement);
|
||||||
|
|
||||||
|
if (value !== undefined) {
|
||||||
|
let binding;
|
||||||
|
if (value.is_binding) { // if it's declaration work with the initializer
|
||||||
|
binding = value.name; // but we also need the name of the bound variable
|
||||||
|
value = state[value.name]; // lookup the value for the binding
|
||||||
|
}
|
||||||
|
|
||||||
|
// if (value.is_visual) {
|
||||||
|
//
|
||||||
|
// } else {
|
||||||
|
// if (binding && bindings[binding].previous && bindings[binding].previous.is_visual) {
|
||||||
|
// label(bindings[binding].previous, '@' + bindings[binding].previous.id);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// if (value.description) {
|
||||||
|
// value = value.description;
|
||||||
|
// }
|
||||||
|
command_history_element.innerText += value.toString() + "\n";
|
||||||
|
command_history.push(command);
|
||||||
|
command_history_element.scrollTo(0, command_history_element.scrollHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const evaluate = function (expr) {
|
||||||
|
switch (expr.type) {
|
||||||
|
case 'declaration': {
|
||||||
|
let value = evaluate(expr.initializer);
|
||||||
|
let binding_name = expr.var_name.value;
|
||||||
|
bindings[binding_name] = {
|
||||||
|
is_binding: true,
|
||||||
|
name: binding_name,
|
||||||
|
};
|
||||||
|
state[binding_name] = value; // assign new value to binding
|
||||||
|
|
||||||
|
return bindings[binding_name]; // don't return the value itself, but the binding_object
|
||||||
|
} // with which you can lookup the value
|
||||||
|
|
||||||
|
case 'group': // expression within parentheses
|
||||||
|
return evaluate(expr.expression);
|
||||||
|
case 'unary': {
|
||||||
|
let right_operand = evaluate(expr.right);
|
||||||
|
if (expr.operator === token_types.MINUS) {
|
||||||
|
return -right_operand; //TODO create negate function (because now it only works for numbers)
|
||||||
|
} else if (expr.operator === token_types.NOT) {
|
||||||
|
return !right_operand;
|
||||||
|
} else {
|
||||||
|
throw {message: 'illegal unary operator'};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case 'binary': {
|
||||||
|
switch (expr.operator) {
|
||||||
|
case token_types.MINUS:
|
||||||
|
return subtraction(evaluate(expr.left), evaluate(expr.right));
|
||||||
|
case token_types.PLUS:
|
||||||
|
return add(evaluate(expr.left), evaluate(expr.right));
|
||||||
|
case token_types.STAR:
|
||||||
|
return multiply(evaluate(expr.left), evaluate(expr.right));
|
||||||
|
case token_types.SLASH:
|
||||||
|
return division(evaluate(expr.left), evaluate(expr.right));
|
||||||
|
case token_types.EQUALS_EQUALS:
|
||||||
|
return test_equal(evaluate(expr.left), evaluate(expr.right));
|
||||||
|
case token_types.AND:
|
||||||
|
return logical_and(evaluate(expr.left), evaluate(expr.right));
|
||||||
|
case token_types.OR:
|
||||||
|
return logical_or(evaluate(expr.left), evaluate(expr.right));
|
||||||
|
}
|
||||||
|
throw {message: 'illegal binary operator'};
|
||||||
|
}
|
||||||
|
case 'identifier': {
|
||||||
|
if (expr.name in keywords) {
|
||||||
|
return keywords[expr.name];
|
||||||
|
} else {
|
||||||
|
if (state[expr.name]) {
|
||||||
|
return state[expr.name];
|
||||||
|
} else {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case 'literal': {
|
||||||
|
return expr.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function parse() {
|
||||||
|
token_index = 0;
|
||||||
|
if (check(token_types.IDENTIFIER, token_index) && check(token_types.EQUALS, token_index + 1)) {
|
||||||
|
let var_name = current_token();
|
||||||
|
advance();
|
||||||
|
advance();
|
||||||
|
return {type: 'declaration', var_name: var_name, initializer: expression()};
|
||||||
|
} else {
|
||||||
|
return expression();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function expression() {
|
||||||
|
return equality();
|
||||||
|
}
|
||||||
|
|
||||||
|
function equality() {
|
||||||
|
let expr = comparison()
|
||||||
|
|
||||||
|
while (match([token_types.EQUALS_EQUALS, token_types.NOT_EQUALS])) {
|
||||||
|
let operator = previous_token();
|
||||||
|
let right = unary();
|
||||||
|
expr = {type: 'binary', left: expr, operator: operator, right: right};
|
||||||
|
}
|
||||||
|
|
||||||
|
return expr;
|
||||||
|
}
|
||||||
|
|
||||||
|
function comparison() {
|
||||||
|
let expr = add_sub();
|
||||||
|
|
||||||
|
while (match([token_types.LESS, token_types.LESS_OR_EQUAL, token_types.GREATER, token_types.GREATER_OR_EQUAL])) {
|
||||||
|
let operator = previous_token();
|
||||||
|
let right = add_sub();
|
||||||
|
expr = {type: 'binary', left: expr, operator: operator, right: right};
|
||||||
|
}
|
||||||
|
|
||||||
|
return expr;
|
||||||
|
}
|
||||||
|
|
||||||
|
function add_sub() {
|
||||||
|
let expr = mult_div();
|
||||||
|
|
||||||
|
while (match([token_types.OR, token_types.MINUS, token_types.PLUS])) {
|
||||||
|
let operator = previous_token();
|
||||||
|
let right = mult_div();
|
||||||
|
expr = {type: 'binary', left: expr, operator: operator, right: right};
|
||||||
|
}
|
||||||
|
|
||||||
|
return expr;
|
||||||
|
}
|
||||||
|
|
||||||
|
function mult_div() {
|
||||||
|
let expr = unary();
|
||||||
|
|
||||||
|
while (match([token_types.AND, token_types.SLASH, token_types.STAR, token_types.DOT])) {
|
||||||
|
let operator = previous_token();
|
||||||
|
let right = unary();
|
||||||
|
expr = {type: 'binary', left: expr, operator: operator, right: right};
|
||||||
|
}
|
||||||
|
|
||||||
|
return expr;
|
||||||
|
}
|
||||||
|
|
||||||
|
function unary() {
|
||||||
|
if (match([token_types.NOT, token_types.MINUS])) {
|
||||||
|
let operator = previous_token();
|
||||||
|
let right = unary();
|
||||||
|
return {type: 'unary', operator: operator, right: right};
|
||||||
|
} else {
|
||||||
|
return primary();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function primary() {
|
||||||
|
if (match([token_types.NUMERIC, token_types.STRING])) {
|
||||||
|
return {type: 'literal', value: previous_token().value, value_type: previous_token().type};
|
||||||
|
} else if (match([token_types.LEFT_PAREN])) {
|
||||||
|
let expr = expression();
|
||||||
|
if (expr && match([token_types.RIGHT_PAREN])) {
|
||||||
|
return {
|
||||||
|
type: 'group',
|
||||||
|
expression: expr
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
throw {message: 'expected expression or )'};
|
||||||
|
}
|
||||||
|
} else if (check(token_types.IDENTIFIER, token_index)) {
|
||||||
|
let identifier = {
|
||||||
|
type: 'identifier',
|
||||||
|
name: current_token().value
|
||||||
|
};
|
||||||
|
advance();
|
||||||
|
return identifier;
|
||||||
|
} else if (match([token_types.LEFT_BRACKET])) {
|
||||||
|
let array = [];
|
||||||
|
if (!check(token_types.RIGHT_BRACKET, token_index)) {
|
||||||
|
let result;
|
||||||
|
do {
|
||||||
|
result = expression();
|
||||||
|
if (result) {
|
||||||
|
array.push(result);
|
||||||
|
} else {
|
||||||
|
throw {message: "Expect ']' after array elements."};
|
||||||
|
}
|
||||||
|
match([token_types.COMMA]);
|
||||||
|
} while (!match([token_types.RIGHT_BRACKET]));
|
||||||
|
}
|
||||||
|
|
||||||
|
return {type: 'array', elements: array};
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* matches token against array of tokens to check for equality (matching type)
|
||||||
|
* @param tokens_to_match array of tokens
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
function match(tokens_to_match) {
|
||||||
|
for (let i = 0; i < tokens_to_match.length; i++) {
|
||||||
|
if (are_same(tokens_to_match[i], current_token())) {
|
||||||
|
advance();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if token at position index matches the given
|
||||||
|
* @param token_to_check expected token type
|
||||||
|
* @param index of token to check
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
function check(token_to_check, index) {
|
||||||
|
let token = tokens[index];
|
||||||
|
if (!token) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return are_same(token_to_check, token);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* checks if 2 tokens have same type
|
||||||
|
* @param token_1
|
||||||
|
* @param token_2
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
function are_same(token_1, token_2) {
|
||||||
|
if (is_at_end()) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
return token_1.type === token_2.type;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function is_at_end() {
|
||||||
|
return token_index >= tokens.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
function advance() {
|
||||||
|
token_index += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
function previous_token() {
|
||||||
|
return tokens[token_index - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
function current_token() {
|
||||||
|
return tokens[token_index];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an array of tokens from a line of input.
|
||||||
|
*
|
||||||
|
* @returns {token_types[]}
|
||||||
|
* @param command
|
||||||
|
*/
|
||||||
|
const scan = function (command) {
|
||||||
|
tokens = [];
|
||||||
|
let current_index = 0, // current index of char to look at in the command string
|
||||||
|
word_start_index = 0; // marker for start of a literal or identifier
|
||||||
|
|
||||||
|
while (!is_at_end()) {
|
||||||
|
word_start_index = current_index;
|
||||||
|
let token = scan_token();
|
||||||
|
if (token) { // undefined mostly means whitespace
|
||||||
|
tokens.push(token);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function scan_token() {
|
||||||
|
let next_char = advance();
|
||||||
|
switch (next_char) {
|
||||||
|
case '(':
|
||||||
|
return token_types.LEFT_PAREN;
|
||||||
|
case ')':
|
||||||
|
return token_types.RIGHT_PAREN;
|
||||||
|
case '[':
|
||||||
|
return token_types.LEFT_BRACKET;
|
||||||
|
case ']':
|
||||||
|
return token_types.RIGHT_BRACKET;
|
||||||
|
case ',':
|
||||||
|
return token_types.COMMA;
|
||||||
|
case '.':
|
||||||
|
return token_types.DOT;
|
||||||
|
case '-':
|
||||||
|
return token_types.MINUS;
|
||||||
|
case '+':
|
||||||
|
return token_types.PLUS;
|
||||||
|
case '*':
|
||||||
|
return token_types.STAR;
|
||||||
|
case '/':
|
||||||
|
return token_types.SLASH;
|
||||||
|
case '>':
|
||||||
|
if (expect('=')) {
|
||||||
|
return token_types.GREATER_OR_EQUAL;
|
||||||
|
} else {
|
||||||
|
return token_types.GREATER;
|
||||||
|
}
|
||||||
|
case '<':
|
||||||
|
if (expect('=')) {
|
||||||
|
return token_types.LESS_OR_EQUAL;
|
||||||
|
} else {
|
||||||
|
return token_types.LESS;
|
||||||
|
}
|
||||||
|
case '!':
|
||||||
|
if (expect('=')) {
|
||||||
|
return token_types.NOT_EQUALS;
|
||||||
|
} else {
|
||||||
|
return token_types.NOT;
|
||||||
|
}
|
||||||
|
case '=':
|
||||||
|
if (expect('=')) {
|
||||||
|
return token_types.EQUALS_EQUALS;
|
||||||
|
} else {
|
||||||
|
return token_types.EQUALS;
|
||||||
|
}
|
||||||
|
case '&':
|
||||||
|
return token_types.AND;
|
||||||
|
case '|':
|
||||||
|
return token_types.OR;
|
||||||
|
case '\'':
|
||||||
|
return string();
|
||||||
|
case ' ':
|
||||||
|
case '\t':
|
||||||
|
case '\r':
|
||||||
|
advance();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (is_digit(next_char)) {
|
||||||
|
let token = Object.assign({}, token_types.NUMERIC);
|
||||||
|
token.value = parse_number();
|
||||||
|
return token;
|
||||||
|
} else {
|
||||||
|
if (is_alpha_or_underscore(next_char)) {
|
||||||
|
let token = Object.assign({}, token_types.IDENTIFIER);
|
||||||
|
token.value = parse_identifier();
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function expect(expected_char) {
|
||||||
|
if (is_at_end()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (current_char() === expected_char) {
|
||||||
|
advance();
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function advance() {
|
||||||
|
if (current_index < command.length) {
|
||||||
|
current_index += 1;
|
||||||
|
}
|
||||||
|
return command[current_index - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
function is_at_end() {
|
||||||
|
return current_index >= command.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
function current_char() {
|
||||||
|
return command[current_index];
|
||||||
|
}
|
||||||
|
|
||||||
|
function is_digit(char) {
|
||||||
|
return char >= '0' && char <= '9';
|
||||||
|
}
|
||||||
|
|
||||||
|
function is_part_of_number(char) {
|
||||||
|
return is_digit(char) || char === '.'; // no scientific notation for now
|
||||||
|
}
|
||||||
|
|
||||||
|
function parse_reference() {
|
||||||
|
while (current_char() === '@' || is_digit(current_char())) {
|
||||||
|
advance();
|
||||||
|
}
|
||||||
|
return command.substring(word_start_index, current_index);
|
||||||
|
}
|
||||||
|
|
||||||
|
function parse_number() {
|
||||||
|
while (is_part_of_number(current_char())) {
|
||||||
|
advance();
|
||||||
|
}
|
||||||
|
let number_string = command.substring(word_start_index, current_index);
|
||||||
|
return Number.parseFloat(number_string);
|
||||||
|
}
|
||||||
|
|
||||||
|
function is_alpha_or_underscore(char) {
|
||||||
|
return (char >= 'a' && char <= 'z') || (char >= 'A' && char <= 'Z') || char === '_';
|
||||||
|
}
|
||||||
|
|
||||||
|
function is_alphanumeric_or_underscore(char) {
|
||||||
|
return (char >= 'a' && char <= 'z') || (char >= 'A' && char <= 'Z') || is_digit(char) || char === '_';
|
||||||
|
}
|
||||||
|
|
||||||
|
function parse_identifier() {
|
||||||
|
while (is_alphanumeric_or_underscore(current_char())) {
|
||||||
|
advance();
|
||||||
|
}
|
||||||
|
return command.substring(word_start_index, current_index);
|
||||||
|
}
|
||||||
|
|
||||||
|
function string() { // as of yet strings may not unclude escaped quotes that are also the start/end quote
|
||||||
|
while (current_char() !== '\'' && !is_at_end()) {
|
||||||
|
advance();
|
||||||
|
}
|
||||||
|
if (is_at_end() && current_char() !== '\'') {
|
||||||
|
throw {message: 'unterminated string'}
|
||||||
|
} else {
|
||||||
|
let string_token = Object.assign({}, token_types.STRING);
|
||||||
|
string_token.value = command.substring(word_start_index + 1, current_index);
|
||||||
|
advance();
|
||||||
|
return string_token;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const token_types = {
|
||||||
|
LEFT_PAREN: {type: 'left_paren'},
|
||||||
|
RIGHT_PAREN: {type: 'right_paren'},
|
||||||
|
LEFT_BRACKET: {type: 'left_bracket'},
|
||||||
|
RIGHT_BRACKET: {type: 'right_bracket'},
|
||||||
|
COMMA: {type: 'comma'},
|
||||||
|
DOT: {type: 'dot'},
|
||||||
|
MINUS: {type: 'minus'},
|
||||||
|
PLUS: {type: 'plus'},
|
||||||
|
STAR: {type: 'star'},
|
||||||
|
SLASH: {type: 'slash'},
|
||||||
|
EQUALS: {type: 'equals'},
|
||||||
|
EQUALS_EQUALS: {type: 'equals_equals'},
|
||||||
|
NOT_EQUALS: {type: 'not_equals'},
|
||||||
|
NOT: {type: 'not'},
|
||||||
|
GREATER: {type: 'greater'},
|
||||||
|
GREATER_OR_EQUAL: {type: 'greater_or_equal'},
|
||||||
|
LESS: {type: 'less'},
|
||||||
|
LESS_OR_EQUAL: {type: 'less_or_equal'},
|
||||||
|
NUMERIC: {type: 'number', value: undefined},
|
||||||
|
IDENTIFIER: {type: 'identifier', value: undefined},
|
||||||
|
STRING: {type: 'string', value: undefined},
|
||||||
|
AND: {type: 'logical_and'},
|
||||||
|
OR: {type: 'logical_or'}
|
||||||
|
};
|
||||||
|
|
||||||
|
const multiply = function (left, right) {
|
||||||
|
return left * right;
|
||||||
|
}
|
||||||
|
|
||||||
|
const division = function (left, right) {
|
||||||
|
return left / right;
|
||||||
|
}
|
||||||
|
|
||||||
|
const add = function (left, right) {
|
||||||
|
return left + right;
|
||||||
|
}
|
||||||
|
|
||||||
|
const subtraction = function (left, right) {
|
||||||
|
return left - right;
|
||||||
|
}
|
||||||
|
|
||||||
|
const test_equal = function (left, right) {
|
||||||
|
if (left.is_vector && right.is_vector) {
|
||||||
|
return left.equals(right);
|
||||||
|
} else {
|
||||||
|
return left === right;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const logical_and = function (left, right) {
|
||||||
|
return left && right;
|
||||||
|
}
|
||||||
|
|
||||||
|
const logical_or = function (left, right) {
|
||||||
|
return left || right;
|
||||||
|
}
|
||||||
|
|
||||||
|
const create_2d_id_matrix = function () {
|
||||||
|
return {
|
||||||
|
data: [[1, 0], [0, 1]],
|
||||||
|
id: index_sequence++,
|
||||||
|
is_visual: true,
|
||||||
|
is_vector: false, // for type comparison
|
||||||
|
is_matrix: true,
|
||||||
|
type: 'matrix', // for showing type to user
|
||||||
|
is_new: true, // to determine view action
|
||||||
|
visible: true,
|
||||||
|
toString: function () {
|
||||||
|
return `matrix@${this.id}`;
|
||||||
|
},
|
||||||
|
hide: function () {
|
||||||
|
return hide(this);
|
||||||
|
},
|
||||||
|
label: function (text) {
|
||||||
|
return label(this, text);
|
||||||
|
},
|
||||||
|
show: function () {
|
||||||
|
return show(this);
|
||||||
|
},
|
||||||
|
equals: function (other) {
|
||||||
|
return (this.id === other.id || (this.type === other.type && this.data === other.data)); // TODO
|
||||||
|
},
|
||||||
|
row: function (index) {
|
||||||
|
return this.data[index];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
32
src/main/resources/static/js/grid.js
Normal file
32
src/main/resources/static/js/grid.js
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
let canvas = document.getElementById('myCanvas');
|
||||||
|
let ctx;
|
||||||
|
if (canvas.getContext) {
|
||||||
|
ctx = canvas.getContext('2d');
|
||||||
|
|
||||||
|
// draw here
|
||||||
|
ctx.fillStyle = 'green';
|
||||||
|
ctx.fillRect(0, 0, 500, 500);
|
||||||
|
} else {
|
||||||
|
console.log("Canvas not supported");
|
||||||
|
}
|
||||||
|
|
||||||
|
fetch('/api/grid/100', {
|
||||||
|
method: 'GET' // or 'POST'
|
||||||
|
}).then((response) => {
|
||||||
|
if (response.ok) {
|
||||||
|
return response.json(); // Here we're using JSON but you can use other formats such as blob, text etc.
|
||||||
|
} else {
|
||||||
|
throw new Error('Server response wasn\'t OK');
|
||||||
|
}
|
||||||
|
}).then((grid) => {
|
||||||
|
const cell_factor = 500 / grid.size;
|
||||||
|
ctx.font = `5px Arial`;
|
||||||
|
ctx.fillStyle = "grey"
|
||||||
|
for (let r = 0; r < grid.size; r++) {
|
||||||
|
for (let c = 0; c < grid.size; c++) {
|
||||||
|
ctx.fillText("" + grid.grid[r][c], 5 + c * cell_factor, r * cell_factor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).catch((err) => {
|
||||||
|
console.log('Fetching failed', err);
|
||||||
|
});
|
||||||
0
src/main/resources/static/js/parser.js
Normal file
0
src/main/resources/static/js/parser.js
Normal file
34
src/test/java/assessment/algorithm/CircleTest.java
Normal file
34
src/test/java/assessment/algorithm/CircleTest.java
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
package assessment.algorithm;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Assertions;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
|
||||||
|
public class CircleTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void test_return_to_previous_is_not_worth_it() {
|
||||||
|
Grid grid = Grid.fromFile("grids/20.txt");
|
||||||
|
Path path = Path.newPath(grid, Point.create(grid, 0, 0)); // 0.0
|
||||||
|
path.add(grid, Point.create(grid, path, 0, 1)); // 1.0
|
||||||
|
path.add(grid, Point.create(grid, path, 0, 2)); // 3.0
|
||||||
|
path.add(grid, Point.create(grid, path, 0, 1)); // 3.0
|
||||||
|
Assertions.assertEquals(3.0, path.value());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void test_return_to_previous_is_worth_something() {
|
||||||
|
Grid grid = Grid.fromFile("grids/20.txt");
|
||||||
|
Path path = Path.newPath(grid, Point.create(grid, 0, 0)); // 0.0
|
||||||
|
path.add(grid, Point.create(grid, path, 0, 1)); // 1.0
|
||||||
|
path.add(grid, Point.create(grid, path, 0, 2)); // 3.0
|
||||||
|
path.add(grid, Point.create(grid, path, 1, 1)); // 3.0
|
||||||
|
path.add(grid, Point.create(grid, path, 0, 1)); // 3.2 distance 1, factor .2
|
||||||
|
Assertions.assertEquals(3.2, path.value());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -1,4 +1,6 @@
|
||||||
import assessment.Grid;
|
package assessment.algorithm;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Assertions;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
import assessment.Grid;
|
package assessment.algorithm;
|
||||||
import assessment.Path;
|
|
||||||
import assessment.BestPathFinder;
|
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
public class PathFinderTest {
|
public class PathFinderTest {
|
||||||
|
|
@ -8,21 +7,21 @@ public class PathFinderTest {
|
||||||
@Test
|
@Test
|
||||||
public void testBestPath20() {
|
public void testBestPath20() {
|
||||||
Grid grid = Grid.fromFile("grids/20.txt");
|
Grid grid = Grid.fromFile("grids/20.txt");
|
||||||
Path path = new BestPathFinder().findMaxValPath(grid, 20, 8, 1000, 9, 9);
|
Path path = new OptimalPathFinder().findOptimalPath(grid, 20, 8, 1000, 9, 9);
|
||||||
System.out.println(path);
|
System.out.println(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testBestPath100() {
|
public void testBestPath100() {
|
||||||
Grid grid = Grid.fromFile("grids/100.txt");
|
Grid grid = Grid.fromFile("grids/100.txt");
|
||||||
Path path = new BestPathFinder().findMaxValPath(grid, 100, 8, 10000, 50, 50);
|
Path path = new OptimalPathFinder().findOptimalPath(grid, 100, 8, 10000, 50, 50);
|
||||||
System.out.println(path);
|
System.out.println(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testBestPath1000() {
|
public void testBestPath1000() {
|
||||||
Grid grid = Grid.fromFile("grids/1000.txt");
|
Grid grid = Grid.fromFile("grids/1000.txt");
|
||||||
Path path = new BestPathFinder().findMaxValPath(grid, 1000, 18, 100000, 500, 500);
|
Path path = new OptimalPathFinder().findOptimalPath(grid, 1000, 18, 10000, 500, 500);
|
||||||
System.out.println(path);
|
System.out.println(path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Loading…
Add table
Reference in a new issue