it is now a website

This commit is contained in:
Shautvast 2024-11-09 12:49:45 +01:00
parent a3cf7b6a27
commit 5b1584e300
18 changed files with 939 additions and 55 deletions

View file

@ -22,6 +22,11 @@
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>

View 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);
}
}

View file

@ -1,4 +1,4 @@
package assessment;
package assessment.algorithm;
import java.io.BufferedReader;
import java.io.IOException;
@ -7,6 +7,7 @@ import java.util.ArrayList;
import java.util.List;
public record Grid(List<List<Integer>> grid) {
private static final double timeValueFactor = .2;
public static Grid fromFile(String resource) {
try {
@ -27,12 +28,30 @@ public record Grid(List<List<Integer>> grid) {
}
}
public int get(int r, int c){
return grid.get(r).get(c);
public int getInitialValue(int x, int y){
return grid.get(y).get(x);
}
//assumes square
public int getWidth(){
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;
}
}
}

View file

@ -1,10 +1,16 @@
package assessment;
package assessment.algorithm;
import java.util.*;
//TODO make non static (CH)
public class BestPathFinder {
private static final double timeValueFactor = .2;
/**
* Finds most valuable path given distance/time covered (t) and elapsed compute time (T)
* 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
private final PriorityQueue<Path> paths = new PriorityQueue<>();
@ -20,8 +26,8 @@ public class BestPathFinder {
* @param y startpositie Y
* @return het meest waardevolle pad
*/
public Path findMaxValPath(Grid g, int N, int t, int T, int x, int y) {
Path path = Path.newPath(g, x, y);
public Path findOptimalPath(Grid g, int N, int t, long T, int x, int y) {
Path path = Path.newPath(g, Point.create(g, x, y));
paths.add(path);
// overall best path
Path max = path;
@ -56,27 +62,27 @@ public class BestPathFinder {
// find best new directions
List<Point> newDirections = new ArrayList<>();
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) {
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) {
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) {
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) {
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) {
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) {
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)
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()) {
@ -98,7 +104,7 @@ public class BestPathFinder {
}
}
if (!pointsAdded) {
//evict
// dead end, evict
Path ended = paths.poll();
if (ended != null && ended.value() > max.value()) {
max = ended;
@ -108,22 +114,4 @@ public class BestPathFinder {
}
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;
}
}
}

View file

@ -1,4 +1,4 @@
package assessment;
package assessment.algorithm;
import java.util.ArrayList;
import java.util.HashSet;
@ -8,7 +8,7 @@ public class Path implements Comparable<Path> {
// beide bevatten de al afgelegde punten
// 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
private final HashSet<Integer> trodden = new HashSet<>();
@ -17,9 +17,9 @@ public class Path implements Comparable<Path> {
}
// 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();
p.add(g, new Point(x, y, g.get(y, x)));
p.add(g, start);
return p;
}
@ -28,14 +28,14 @@ public class Path implements Comparable<Path> {
trodden.add(point.y * g.getWidth() + point.x);
}
public Double value() {
public double value() {
return points.stream().mapToDouble(point -> point.value).sum();
}
// compare descending, highest value first
@Override
public int compareTo(Path o) {
return -this.value().compareTo(o.value());
return -Double.compare(this.value(), o.value());
}
public int length() {

View file

@ -1,17 +1,28 @@
package assessment;
package assessment.algorithm;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.util.Objects;
public class Point implements Comparable<Point> {
final int x;
final int y;
final double value;
public final int x;
public final int y;
public double value;
public Point(int x, int y, double value) {
private Point(int x, int y, double initialValue) {
this.x = x;
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
@ -37,4 +48,5 @@ public class Point implements Comparable<Point> {
public int hashCode() {
return Objects.hash(x, y);
}
}

View 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);
}
}

View 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;
}
}

View 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;
}
}

View 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;
}
}

View 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;
}

View 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">&gt;
<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>

View 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];
}
}
}

View 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);
});

View file

View 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());
}
}

View file

@ -1,4 +1,6 @@
import assessment.Grid;
package assessment.algorithm;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import java.util.List;

View file

@ -1,6 +1,5 @@
import assessment.Grid;
import assessment.Path;
import assessment.BestPathFinder;
package assessment.algorithm;
import org.junit.jupiter.api.Test;
public class PathFinderTest {
@ -8,21 +7,21 @@ public class PathFinderTest {
@Test
public void testBestPath20() {
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);
}
@Test
public void testBestPath100() {
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);
}
@Test
public void testBestPath1000() {
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);
}
}