commit
b58d52529b
17 changed files with 1093 additions and 0 deletions
@ -0,0 +1,2 @@ |
||||
.idea |
||||
|
@ -0,0 +1,6 @@ |
||||
{ |
||||
"display_name": "Pathfinder", |
||||
"info_text": "A graphical demonstration of some pathfinder algorithms", |
||||
"visible": true, |
||||
"tags": ["Simulation", "Tool"] |
||||
} |
After Width: | Height: | Size: 318 B |
@ -0,0 +1,321 @@ |
||||
class Algorithm{ |
||||
|
||||
constructor(){ |
||||
this.startNode = game.startNode; |
||||
this.targetNode = game.targetNode; |
||||
|
||||
this.grid = game.grid; |
||||
|
||||
this.startDate = new Date(); |
||||
this.timeDiff = 0; |
||||
} |
||||
|
||||
get running(){ |
||||
return !this.hasSucceeded && !this.hasFailed; |
||||
} |
||||
|
||||
display(){ |
||||
push(); |
||||
scale(1 / this.grid.nodeSize.x, 1 / this.grid.nodeSize.y); |
||||
fill(0); |
||||
textSize(20); |
||||
textAlign(CENTER, TOP); |
||||
text('Time: ' + this.timeDiff / 1000 + 's', this.grid.size / 2, -this.grid.nodeSize.y / 2 - 50); |
||||
pop(); |
||||
} |
||||
|
||||
update(){ |
||||
this.timeDiff = new Date().getTime() - this.startDate.getTime(); |
||||
} |
||||
|
||||
success(){ |
||||
console.log('Success!'); |
||||
this.hasSucceeded = true; |
||||
} |
||||
|
||||
failed(){ |
||||
console.log('Failed!'); |
||||
this.hasFailed = true; |
||||
} |
||||
|
||||
} |
||||
|
||||
function startAlgorithm(){ |
||||
switch ($('#algorithm_type').val()){ |
||||
case 'astar': |
||||
game.start(new AStar()); |
||||
break; |
||||
case 'dijkstra': |
||||
game.start(new Dijkstra()); |
||||
break; |
||||
case 'dstar': |
||||
game.start(new DStar()); |
||||
break; |
||||
} |
||||
} |
||||
|
||||
|
||||
class DStar extends Algorithm{ |
||||
|
||||
constructor() { |
||||
super(); |
||||
|
||||
this.openList = [] |
||||
} |
||||
|
||||
|
||||
} |
||||
|
||||
|
||||
class AStar extends Algorithm{ |
||||
|
||||
constructor(){ |
||||
super(); |
||||
|
||||
this.startNode.f = 0; |
||||
this.startNode.g = 0; |
||||
|
||||
this.openList = [this.startNode]; |
||||
this.closedList = []; |
||||
} |
||||
|
||||
get minFNodeIndex(){ |
||||
let f = this.openList[0].f, index = 0; |
||||
this.openList.forEach((n, i) => { |
||||
if (n.f < f){ |
||||
f = n.f; |
||||
index = i; |
||||
} |
||||
}); |
||||
return index; |
||||
} |
||||
|
||||
update(){ |
||||
if (!this.running){ |
||||
return; |
||||
} |
||||
|
||||
super.update(); |
||||
|
||||
if (!this.openList.length){ |
||||
this.failed(); |
||||
} else { |
||||
let index = this.minFNodeIndex; |
||||
let currentNode = this.openList[index]; |
||||
this.openList.splice(index, 1); |
||||
|
||||
if (currentNode === this.targetNode){ |
||||
this.success(); |
||||
} else { |
||||
this.closedList.push(currentNode); |
||||
|
||||
currentNode.successors.forEach(successor => { |
||||
|
||||
if (this.closedList.find(n => n === successor)){ |
||||
return; |
||||
} |
||||
|
||||
let provisionalG = currentNode.g + currentNode.pos.dist(successor.pos); |
||||
if (this.openList.find(n => n === successor) && provisionalG >= successor.g){ |
||||
return; |
||||
} |
||||
|
||||
successor.predecessor = currentNode |
||||
successor.g = provisionalG; |
||||
|
||||
successor.f = provisionalG + successor.distanceToTarget; |
||||
if (!this.openList.find(n => n === successor)){ |
||||
this.openList.push(successor); |
||||
} |
||||
}); |
||||
} |
||||
} |
||||
} |
||||
|
||||
display(appearance, isInformationVisible, pathSmoothness){ |
||||
super.display(); |
||||
|
||||
if (isInformationVisible){ |
||||
|
||||
if (appearance === 'Circles'){ |
||||
let r = 0.5; |
||||
|
||||
fill(200); |
||||
this.openList.forEach(n => { |
||||
ellipse(n.pos.x, n.pos.y, r * 2); |
||||
}); |
||||
|
||||
fill(100); |
||||
this.closedList.forEach(n => { |
||||
if (n === this.startNode) |
||||
return; |
||||
ellipse(n.pos.x, n.pos.y, r * 2); |
||||
}); |
||||
} |
||||
|
||||
if (appearance === 'Rectangles'){ |
||||
fill(200); |
||||
this.openList.forEach(n => { |
||||
rect(n.pos.x - 0.5, n.pos.y - 0.5, 1, 1); |
||||
}); |
||||
|
||||
fill(100); |
||||
this.closedList.forEach(n => { |
||||
if (n === this.startNode) |
||||
return; |
||||
rect(n.pos.x - 0.5, n.pos.y - 0.5, 1, 1); |
||||
}); |
||||
} |
||||
} |
||||
|
||||
|
||||
noFill(); |
||||
stroke(50, 255, 50); |
||||
strokeWeight(0.2); |
||||
|
||||
let node; |
||||
if (!this.openList.length){ |
||||
node = this.closedList[this.closedList.length - 1]; |
||||
} else { |
||||
node = this.openList[this.minFNodeIndex]; |
||||
} |
||||
if (this.hasSucceeded){ |
||||
node = this.targetNode; |
||||
} |
||||
if (node){ |
||||
|
||||
if (pathSmoothness){ |
||||
beginShape(); |
||||
vertex(node.pos.x, node.pos.y); |
||||
while (node.predecessor){ |
||||
node = node.predecessor; |
||||
|
||||
if (!node.predecessor){ |
||||
quadraticVertex(node.pos.x, node.pos.y, node.pos.x, node.pos.y); |
||||
endShape(); |
||||
} else { |
||||
let cx = (node.pos.x + node.predecessor.pos.x) / 2; |
||||
let cy = (node.pos.y + node.predecessor.pos.y) / 2; |
||||
quadraticVertex(node.pos.x, node.pos.y, cx, cy); |
||||
} |
||||
} |
||||
|
||||
} else { |
||||
while (node.predecessor){ |
||||
line(node.pos.x, node.pos.y, node.predecessor.pos.x, node.predecessor.pos.y); |
||||
node = node.predecessor; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
} |
||||
|
||||
class Dijkstra extends Algorithm{ |
||||
|
||||
constructor(){ |
||||
super(); |
||||
|
||||
this.nodes = this.grid.allowedNodes.slice(); |
||||
this.nodes.forEach(n => n.distance = Infinity); |
||||
this.startNode.distance = 0; |
||||
} |
||||
|
||||
get minDistanceNodeIndex(){ |
||||
let index = 0, distance = this.nodes[0].distance; |
||||
this.nodes.forEach((n, i) => { |
||||
if (n.distance < distance){ |
||||
index = i; |
||||
distance = n.distance; |
||||
} |
||||
}); |
||||
return index; |
||||
} |
||||
|
||||
update(){ |
||||
if (!this.running){ |
||||
return; |
||||
} |
||||
super.update(); |
||||
|
||||
let index = this.minDistanceNodeIndex; |
||||
let currentNode = this.nodes[index]; |
||||
|
||||
this.nodes.splice(index, 1); |
||||
|
||||
if (currentNode === this.targetNode){ |
||||
this.success(); |
||||
} |
||||
|
||||
currentNode.successors.forEach(s => { |
||||
if (this.nodes.find(n => n === s)){ |
||||
let alternativeDistance = currentNode.distance + currentNode.pos.dist(s.pos); |
||||
if (alternativeDistance < s.distance){ |
||||
s.distance = alternativeDistance; |
||||
s.predecessor = currentNode; |
||||
} |
||||
} |
||||
}) |
||||
} |
||||
|
||||
display(appearance, isInformationVisible, pathSmoothness){ |
||||
super.display(); |
||||
|
||||
if (isInformationVisible){ |
||||
if (appearance === 'Circles'){ |
||||
let r = 0.5; |
||||
fill(100); |
||||
this.nodes.forEach(n => { |
||||
if (n === this.startNode || n === this.targetNode) |
||||
return; |
||||
ellipse(n.pos.x, n.pos.y, r * 2); |
||||
}); |
||||
} |
||||
|
||||
if (appearance === 'Rectangles'){ |
||||
fill(100); |
||||
this.nodes.forEach(n => { |
||||
if (n === this.startNode || n === this.targetNode) |
||||
return; |
||||
rect(n.pos.x - 0.5, n.pos.y - 0.5, 1, 1); |
||||
}); |
||||
} |
||||
} |
||||
|
||||
|
||||
noFill(); |
||||
stroke(50, 255, 50); |
||||
strokeWeight(0.2); |
||||
|
||||
let node = this.nodes[this.minDistanceNodeIndex]; |
||||
if (this.hasSucceeded){ |
||||
node = this.targetNode; |
||||
} |
||||
if (node){ |
||||
|
||||
if (pathSmoothness){ |
||||
beginShape(); |
||||
vertex(node.pos.x, node.pos.y); |
||||
while (node.predecessor){ |
||||
node = node.predecessor; |
||||
|
||||
if (!node.predecessor){ |
||||
quadraticVertex(node.pos.x, node.pos.y, node.pos.x, node.pos.y); |
||||
endShape(); |
||||
} else { |
||||
let cx = (node.pos.x + node.predecessor.pos.x) / 2; |
||||
let cy = (node.pos.y + node.predecessor.pos.y) / 2; |
||||
quadraticVertex(node.pos.x, node.pos.y, cx, cy); |
||||
} |
||||
} |
||||
|
||||
} else { |
||||
while (node.predecessor){ |
||||
line(node.pos.x, node.pos.y, node.predecessor.pos.x, node.predecessor.pos.y); |
||||
node = node.predecessor; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
} |
@ -0,0 +1,29 @@ |
||||
'use strict';
|
||||
|
||||
function keyPressed(){
|
||||
|
||||
} |
||||
|
||||
function keyReleased(){ |
||||
|
||||
} |
||||
|
||||
function mouseMoved(){ |
||||
|
||||
} |
||||
|
||||
function mouseDragged(){ |
||||
|
||||
} |
||||
|
||||
function mousePressed(){ |
||||
|
||||
} |
||||
|
||||
function mouseReleased(){ |
||||
|
||||
} |
||||
|
||||
function windowResized(){ |
||||
resizeCanvas($('#canvas-holder').width(), $('#canvas-holder').height()); |
||||
} |
@ -0,0 +1,111 @@ |
||||
class Game{ |
||||
|
||||
constructor(){ |
||||
this.generateGrid(); |
||||
this.updateVelocity(); |
||||
this.updateDifficulty(); |
||||
this.updateVisibilities(); |
||||
} |
||||
|
||||
generateGrid(){ |
||||
this.algorithm = null; |
||||
|
||||
let count = parseInt($('#cell_count').val()); |
||||
let movement = $('#movement').val(); |
||||
let difficulty = parseInt($('#difficulty').val()); |
||||
let hasRandomDirection = $('#direction').is(':checked'); |
||||
|
||||
this.grid = new Grid(localSettings.app.grid, count, difficulty, hasRandomDirection); |
||||
$('#cell_count_text').text(count + 'x' + count + ' nodes'); |
||||
|
||||
//Two main nodes, start and target
|
||||
if (hasRandomDirection){ |
||||
this.startNode = random(this.grid.allowedNodes); |
||||
this.targetNode = random(this.grid.getAllowedNodesDistantTo(this.startNode) |
||||
.filter(n => { |
||||
if (movement === '1'){ |
||||
return n.pos.x % 2 === this.startNode.pos.x % 2 |
||||
&& n.pos.y % 2 === this.startNode.pos.y % 2; |
||||
} |
||||
return true; |
||||
}) |
||||
); |
||||
} else { |
||||
this.startNode = this.grid.nodes[0][0]; |
||||
this.targetNode = this.grid.nodes[count - 1][count - 1]; |
||||
} |
||||
|
||||
this.updateTester = 0; |
||||
} |
||||
|
||||
update(){ |
||||
if (this.algorithm){ |
||||
this.updateTester += this.movesPerSecond / frameRate(); |
||||
let i = 0; |
||||
for (; i < floor(this.updateTester); i++){ |
||||
this.algorithm.update(); |
||||
} |
||||
this.updateTester -= i; |
||||
}
|
||||
} |
||||
|
||||
display(){ |
||||
this.grid.display(this.appearance, this.linesAreVisible); |
||||
|
||||
if (this.appearance === 'Circles'){ |
||||
let r = 0.5; |
||||
fill(50, 50, 255); |
||||
ellipse(this.startNode.pos.x, this.startNode.pos.y, r * 2); |
||||
fill(50, 255, 50); |
||||
ellipse(this.targetNode.pos.x, this.targetNode.pos.y, r * 2); |
||||
} |
||||
if (this.appearance === 'Rectangles'){ |
||||
fill(50, 50, 255); |
||||
rect(this.startNode.pos.x - 0.5, this.startNode.pos.y - 0.5, 1, 1); |
||||
fill(50, 255, 50); |
||||
rect(this.targetNode.pos.x - 0.5, this.targetNode.pos.y - 0.5, 1, 1); |
||||
} |
||||
|
||||
|
||||
if (this.algorithm) |
||||
this.algorithm.display(this.appearance, this.isInformationVisible, this.pathSmoothness); |
||||
} |
||||
|
||||
start(algorithm){ |
||||
this.algorithm = algorithm; |
||||
|
||||
let movement = $('#movement').val(); |
||||
this.grid.nodes.forEach(x => x.forEach(n => { |
||||
n.setMovement(movement); |
||||
n.connectToSuccessors(this.grid.nodes); |
||||
})); |
||||
} |
||||
|
||||
updateVelocity(){ |
||||
let value = parseInt($('#velocity').val()); |
||||
$('#velocity_text').text(value + ' moves per second'); |
||||
this.movesPerSecond = value; |
||||
} |
||||
|
||||
updateDifficulty(){ |
||||
let value = parseInt($('#difficulty').val()); |
||||
$('#difficulty_text').text('Difficulty: ' + value); |
||||
this.generateGrid(); |
||||
} |
||||
|
||||
updateVisibilities(){ |
||||
let appearance = $('input[name=appearance]:checked').val(); |
||||
let displayGrid = $('#gridVisibility').is(':checked'); |
||||
let displayInformation = $('#information').is(':checked'); |
||||
let pathSmoothness = $('#pathSmoothness').is(':checked'); |
||||
|
||||
if (!appearance) |
||||
appearance = $('input[name=appearance]:eq(0)').val(); |
||||
|
||||
this.appearance = appearance; |
||||
this.linesAreVisible = displayGrid; |
||||
this.isInformationVisible = displayInformation; |
||||
this.pathSmoothness = pathSmoothness; |
||||
} |
||||
|
||||
} |
@ -0,0 +1,160 @@ |
||||
class Grid{ |
||||
|
||||
constructor(settings, cellCount, difficulty, randomDirection){ |
||||
|
||||
this.margin = settings.margin; |
||||
|
||||
this.cellCount = createVector(cellCount, cellCount); |
||||
|
||||
this.nodes = []; |
||||
for (let x = 0; x < this.cellCount.x; x++){ |
||||
let column = []; |
||||
for (let y = 0; y < this.cellCount.y; y++){ |
||||
let forbidden = ranBool(7 - difficulty); |
||||
if (!randomDirection){ |
||||
let allowed = [ |
||||
{x: 0, y: 0}, |
||||
{x: 1, y: 0}, |
||||
{x: 1, y: 1}, |
||||
{x: 0, y: 1}, |
||||
{x: this.cellCount.x - 1, y: this.cellCount.y - 1}, |
||||
{x: this.cellCount.x - 1, y: this.cellCount.y - 2}, |
||||
{x: this.cellCount.x - 2, y: this.cellCount.y - 2}, |
||||
{x: this.cellCount.x - 2, y: this.cellCount.y - 1}, |
||||
]; |
||||
allowed.forEach(a => { |
||||
if (x === a.x && y === a.y){ |
||||
forbidden = false; |
||||
} |
||||
}); |
||||
} |
||||
column.push(new PathNode(x, y, forbidden)); |
||||
} |
||||
this.nodes.push(column); |
||||
} |
||||
|
||||
this.nodes.forEach(x => x.forEach(n => n.connectToSuccessors(this.nodes))); |
||||
|
||||
let nodes = []; |
||||
this.nodes.forEach(x => nodes.push(...x.filter(n => n.isForbidden))); |
||||
this.forbiddenNodes = nodes; |
||||
|
||||
nodes = []; |
||||
this.nodes.forEach(x => nodes.push(...x.filter(n => !n.isForbidden))); |
||||
this.allowedNodes = nodes; |
||||
} |
||||
|
||||
get size(){ |
||||
return Math.min(width - this.margin * 2, height - this.margin * 2); |
||||
} |
||||
|
||||
get nodeSize(){ |
||||
return { |
||||
x: this.size / this.cellCount.x, |
||||
y: this.size / this.cellCount.y |
||||
}; |
||||
} |
||||
|
||||
display(appearance, linesAreVisible){ |
||||
translate(this.margin, this.margin); |
||||
|
||||
if (linesAreVisible){ |
||||
let size = this.size; |
||||
stroke(100); |
||||
strokeWeight(1); |
||||
for (let x = 0; x <= size + 1; x += size / this.cellCount.x){ |
||||
line(x, 0, x, size); |
||||
} |
||||
for (let y = 0; y <= size + 1; y += size / this.cellCount.y){ |
||||
line(0, y, size, y); |
||||
} |
||||
} |
||||
|
||||
translate(this.nodeSize.x / 2, this.nodeSize.y / 2); |
||||
scale(this.nodeSize.x, this.nodeSize.y); |
||||
|
||||
noStroke(); |
||||
fill(200, 50, 50); |
||||
for (let n of this.forbiddenNodes){ |
||||
let x = n.pos.x; |
||||
let y = n.pos.y; |
||||
|
||||
if (appearance === 'Circles'){ |
||||
let r = 0.5; |
||||
ellipse(x, y, r * 2); |
||||
} |
||||
if (appearance === 'Rectangles'){ |
||||
rect(x - 0.5, y - 0.5, 1, 1); |
||||
} |
||||
} |
||||
} |
||||
|
||||
getAllowedNodesDistantTo(otherNode){ |
||||
return this.allowedNodes.filter(n => n.pos.dist(otherNode.pos) > Math.max(this.cellCount.x, this.cellCount.y) / 2); |
||||
} |
||||
|
||||
} |
||||
|
||||
class PathNode{ |
||||
|
||||
constructor(x, y, isForbidden){ |
||||
this.pos = createVector(x, y); |
||||
this.isForbidden = isForbidden; |
||||
} |
||||
|
||||
get distanceToTarget(){ |
||||
return this.pos.dist(game.targetNode.pos); |
||||
} |
||||
|
||||
setMovement(movement){ |
||||
this.isDiagonalAllowed = false; |
||||
this.isStraightAllowed = false; |
||||
switch (movement){ |
||||
case "0": |
||||
this.isStraightAllowed = true; |
||||
break; |
||||
case "1": |
||||
this.isDiagonalAllowed = true; |
||||
break; |
||||
case "2": |
||||
this.isStraightAllowed = true; |
||||
this.isDiagonalAllowed = true; |
||||
break; |
||||
} |
||||
} |
||||
|
||||
connectToSuccessors(nodes){ |
||||
let allowed = [], successors = []; |
||||
if (this.isStraightAllowed){ |
||||
allowed.push( |
||||
{x: 1, y: 0}, |
||||
{x: 0, y: 1}, |
||||
{x: -1, y: 0}, |
||||
{x: 0, y: -1} |
||||
); |
||||
} |
||||
if (this.isDiagonalAllowed){ |
||||
allowed.push( |
||||
{x: 1, y: 1}, |
||||
{x: 1, y: -1}, |
||||
{x: -1, y: 1}, |
||||
{x: -1, y: -1} |
||||
); |
||||
} |
||||
|
||||
allowed.forEach(a => { |
||||
let column = nodes[this.pos.x + a.x]; |
||||
if (column){ |
||||
let node = column[this.pos.y + a.y]; |
||||
if (node){ |
||||
if (!node.isForbidden){ |
||||
successors.push(node); |
||||
} |
||||
} |
||||
} |
||||
}); |
||||
|
||||
this.successors = successors; |
||||
} |
||||
|
||||
} |
@ -0,0 +1,87 @@ |
||||
'use strict'; |
||||
|
||||
let debug = false, |
||||
font, |
||||
localSettings, |
||||
loader; |
||||
|
||||
//Only for online games
|
||||
let socket; |
||||
|
||||
let antiCacheQuery = '?_=' + new Date().getTime(); |
||||
|
||||
let game; |
||||
|
||||
function preload(){ |
||||
localSettings = loadJSON('data/settings/settings.json' + antiCacheQuery, json => { |
||||
console.log('Local settings loaded: ', json); |
||||
}, error => { |
||||
console.log('Local settings failed: ', error); |
||||
}); |
||||
|
||||
font = loadFont('data/styles/font.ttf', json => { |
||||
console.log('Local font loaded: ', json); |
||||
}, error => { |
||||
console.log('Local font failed: ', error); |
||||
}); |
||||
} |
||||
|
||||
function setup(){ |
||||
canvasSetup(); |
||||
interfaceSetup(); |
||||
|
||||
loadDynamicScripts().then(() => game = new Game()); |
||||
} |
||||
|
||||
function draw(){ |
||||
|
||||
background(40); |
||||
|
||||
if (game){ |
||||
game.update(); |
||||
game.display(); |
||||
} |
||||
|
||||
if (loader){ |
||||
loader.update(); |
||||
loader.display(); |
||||
} |
||||
|
||||
if (debug) debugInformation(); |
||||
} |
||||
|
||||
function canvasSetup(){ |
||||
setFrameRate(60); |
||||
let w = $('#canvas-holder').width(), |
||||
h = $('#canvas-holder').height(); |
||||
let canvas = createCanvas(w, h); |
||||
canvas.parent('canvas-holder'); |
||||
textFont(font); |
||||
} |
||||
|
||||
function interfaceSetup(){ |
||||
$('#cell_count').attr({ |
||||
'min': localSettings.app.grid.size.min,
|
||||
'max': localSettings.app.grid.size.max |
||||
}); |
||||
} |
||||
|
||||
function loadDynamicScripts(){ |
||||
|
||||
return httpGet('data/settings/libraries.json' + antiCacheQuery, 'json') |
||||
.then(json => { |
||||
let requests = []; |
||||
for (let script in json){ |
||||
if (json[script]){ |
||||
let url = '/lib/benjocraeft/' + script + '.js' |
||||
requests.push($.getScript(url, () => { |
||||
console.log('Successfully loaded script: ', url) |
||||
})); |
||||
} |
||||
} |
||||
return $.when(...requests); |
||||
}) |
||||
.then(() => { |
||||
console.log("All dynamic scripts have been loaded!"); |
||||
}); |
||||
} |
@ -0,0 +1,10 @@ |
||||
'use strict'; |
||||
|
||||
function socketConnect(project, name = "noone"){ |
||||
let urlQueries = '?game=' + project.name + '&name=' + name; |
||||
let port = 3000; |
||||
let url = location.hostname + ":" + port + urlQueries; |
||||
|
||||
socket = io.connect(url); |
||||
socket.on('connect', () => console.log('Connected to ', url)); |
||||
} |
@ -0,0 +1,8 @@ |
||||
{ |
||||
"collision": false, |
||||
"colorPicker": false, |
||||
"cookie": false, |
||||
"loader": false, |
||||
"prototypes": false, |
||||
"technical": true |
||||
} |
@ -0,0 +1,32 @@ |
||||
{ |
||||
"project": { |
||||
"name": "pathfinder", |
||||
"author": "BenjoCraeft", |
||||
"version": "1.0.0", |
||||
"playerCounts": [], |
||||
"online": { |
||||
"iceServers": [ |
||||
{"urls": "stun:stun.l.google.com:19302"}, |
||||
{ |
||||
"urls": "turn:numb.viagenie.ca", |
||||
"credential": "muazkh", |
||||
"username": "webrtc@live.com" |
||||
} |
||||
] |
||||
} |
||||
}, |
||||
"frameWork": { |
||||
"frameRate": 60, |
||||
"width": null, |
||||
"height": null |
||||
}, |
||||
"app": { |
||||
"grid": { |
||||
"margin": 50, |
||||
"size": { |
||||
"min": 20, |
||||
"max": 100 |
||||
} |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,88 @@ |
||||
#color_picker{ |
||||
width: 300px; |
||||
height: 25%; |
||||
margin: 20px; |
||||
margin-top: 50px; |
||||
border: 5px solid #000; |
||||
background-color: #000; |
||||
-webkit-user-select: none; |
||||
-moz-user-select: none; |
||||
-ms-user-select: none; |
||||
user-select: none; |
||||
position: relative; |
||||
} |
||||
#color_picker_numeric{ |
||||
width: 80%; |
||||
padding: 5%; |
||||
margin: 5%; |
||||
background-color: #888; |
||||
border-radius: 10px; |
||||
overflow: hidden; |
||||
} |
||||
.color_picker_rgb{ |
||||
float: left; |
||||
width: 22%; |
||||
height: 35px; |
||||
font-size: 25px; |
||||
color: #000; |
||||
} |
||||
.color_picker_rgb:nth-child(1){ |
||||
margin-right: 10%; |
||||
margin-left: 3%; |
||||
background-color: #F00; |
||||
|
||||
} |
||||
.color_picker_rgb:nth-child(2){ |
||||
background-color: #0F0; |
||||
} |
||||
.color_picker_rgb:nth-child(3){ |
||||
margin-left: 10%; |
||||
background-color: #00F; |
||||
color: #FFF; |
||||
} |
||||
#color_picker_hex{ |
||||
width: 50%; |
||||
height: 30px; |
||||
font-size: 25px; |
||||
margin: 10% 25% 0 25%; |
||||
} |
||||
#saturation{ |
||||
position: relative; |
||||
width: calc(100% - 33px); |
||||
height: 100%; |
||||
background: linear-gradient(to right, #FFF 0%, #F00 100%); |
||||
float: left; |
||||
margin-right: 6px; |
||||
} |
||||
#value { |
||||
width: 100%; |
||||
height: 100%; |
||||
background: linear-gradient(to top, #000 0%, rgba(255,255,255,0) 100%); |
||||
} |
||||
#sb_picker{ |
||||
border: 2px solid; |
||||
border-color: #FFF; |
||||
position: absolute; |
||||
width: 14px; |
||||
height: 14px; |
||||
border-radius: 10px; |
||||
bottom: 50px; |
||||
left: 50px; |
||||
box-sizing: border-box; |
||||
z-index: 10; |
||||
} |
||||
#hue { |
||||
width: 27px; |
||||
height: 100%; |
||||
position: relative; |
||||
float: left; |
||||
background: linear-gradient(to bottom, #F00 0%, #F0F 17%, #00F 34%, #0FF 50%, #0F0 67%, #FF0 84%, #F00 100%); |
||||
} |
||||
#hue_picker { |
||||
position: absolute; |
||||
background: #000; |
||||
border-bottom: 1px solid #000; |
||||
top: 0; |
||||
width: 27px; |
||||
height: 2px; |
||||
} |
Binary file not shown.
@ -0,0 +1,88 @@ |
||||
input[type=range] { |
||||
-webkit-appearance: none; |
||||
margin: 18px 0; |
||||
width: 100%; |
||||
background: none; |
||||
} |
||||
input[type=range]:focus { |
||||
outline: none; |
||||
} |
||||
input[type=range]::-webkit-slider-runnable-track { |
||||
width: 100%; |
||||
height: 8.4px; |
||||
cursor: pointer; |
||||
animate: 0.2s; |
||||
box-shadow: 1px 1px 1px #000000, 0px 0px 1px #0d0d0d; |
||||
background: #3071a9; |
||||
border-radius: 1.3px; |
||||
border: 0.2px solid #010101; |
||||
} |
||||
input[type=range]::-webkit-slider-thumb { |
||||
box-shadow: 1px 1px 1px #000000, 0px 0px 1px #0d0d0d; |
||||
border: 1px solid #000000; |
||||
height: 36px; |
||||
width: 16px; |
||||
border-radius: 3px; |
||||
background: #ffffff; |
||||
cursor: pointer; |
||||
-webkit-appearance: none; |
||||
margin-top: -14px; |
||||
} |
||||
input[type=range]:focus::-webkit-slider-runnable-track { |
||||
background: #367ebd; |
||||
} |
||||
input[type=range]::-moz-range-track { |
||||
width: 100%; |
||||
height: 8.4px; |
||||
cursor: pointer; |
||||
box-shadow: 1px 1px 1px #000000, 0px 0px 1px #0d0d0d; |
||||
background: #3071a9; |
||||
border-radius: 1.3px; |
||||
border: 0.2px solid #010101; |
||||
} |
||||
input[type=range]::-moz-range-thumb { |
||||
box-shadow: 1px 1px 1px #000000, 0px 0px 1px #0d0d0d; |
||||
border: 1px solid #000000; |
||||
height: 36px; |
||||
width: 16px; |
||||
border-radius: 3px; |
||||
background: #302a86; |
||||
cursor: pointer; |
||||
} |
||||
input[type=range]::-ms-track { |
||||
width: 100%; |
||||
height: 8.4px; |
||||
cursor: pointer; |
||||
animate: 0.2s; |
||||
background: transparent; |
||||
border-color: transparent; |
||||
border-width: 16px 0; |
||||
color: transparent; |
||||
} |
||||
input[type=range]::-ms-fill-lower { |
||||
background: #2a6495; |
||||
border: 0.2px solid #010101; |
||||
border-radius: 2.6px; |
||||
box-shadow: 1px 1px 1px #000000, 0px 0px 1px #0d0d0d; |
||||
} |
||||
input[type=range]::-ms-fill-upper { |
||||
background: #3071a9; |
||||
border: 0.2px solid #010101; |
||||
border-radius: 2.6px; |
||||
box-shadow: 1px 1px 1px #000000, 0px 0px 1px #0d0d0d; |
||||
} |
||||
input[type=range]::-ms-thumb { |
||||
box-shadow: 1px 1px 1px #000000, 0px 0px 1px #0d0d0d; |
||||
border: 1px solid #000000; |
||||
height: 36px; |
||||
width: 16px; |
||||
border-radius: 3px; |
||||
background: #ffffff; |
||||
cursor: pointer; |
||||
} |
||||
input[type=range]:focus::-ms-fill-lower { |
||||
background: #3071a9; |
||||
} |
||||
input[type=range]:focus::-ms-fill-upper { |
||||
background: #367ebd; |
||||
} |
@ -0,0 +1,66 @@ |
||||
<!DOCTYPE html> |
||||
<html lang="en"> |
||||
<head> |
||||
<meta charset="utf-8"> |
||||
<script src="https://code.jquery.com/jquery-3.6.4.min.js" type="text/javascript"></script> |
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.6.0/p5.min.js" type="text/javascript"></script> |
||||
<script src="data/scripts/init.js" type="text/javascript"></script> |
||||
<script src="data/scripts/events.js" type="text/javascript"></script> |
||||
<script src="data/scripts/online.js" type="text/javascript"></script> |
||||
<script src="data/scripts/game.js" type="text/javascript"></script> |
||||
<script src="data/scripts/algorithm.js" type="text/javascript"></script> |
||||
<script src="data/scripts/grid.js" type="text/javascript"></script> |
||||
<link href="styles.css" rel="stylesheet"> |
||||
<link href="data/styles/color_picker.css" rel="stylesheet"> |
||||
<link href="data/styles/range_input.css" rel="stylesheet"> |
||||
<title>Pathfinding Algorithms</title> |
||||
</head> |
||||
<body> |
||||
<div id="p5_loading"></div> |
||||
<div id="content"> |
||||
<div id="canvas-holder"></div> |
||||
<div id="interface"> |
||||
<form onchange="game.updateVisibilities();"> |
||||
<label for="circles">Circles</label> |
||||
<input id="circles" name="appearance" type="radio" value="Circles"> |
||||
<br> |
||||
<label for="rectangles">Rectangles</label> |
||||
<input id="rectangles" name="appearance" type="radio" value="Rectangles"> |
||||
</form> |
||||
<div onchange="game.updateVisibilities();"> |
||||
<label for="pathSmoothness">Smooth path: </label> |
||||
<input id="pathSmoothness" type="checkbox"> |
||||
</div> |
||||
<div onchange="game.updateVisibilities();"> |
||||
<label for="gridVisibility">Grid: </label> |
||||
<input id="gridVisibility" type="checkbox"> |
||||
</div> |
||||
<div onchange="game.updateVisibilities();"> |
||||
<label for="information">Additional information: </label> |
||||
<input id="information" type="checkbox"> |
||||
</div> |
||||
<div onchange="game.generateGrid();"> |
||||
<label for="direction">Random direction: </label> |
||||
<input id="direction" type="checkbox"> |
||||
</div> |
||||
<select id="movement"> |
||||
<option value="0">Only Straight</option> |
||||
<option value="1">Only Diagonal</option> |
||||
<option value="2">Straight and Diagonal</option> |
||||
</select> |
||||
<input id="difficulty" max="5" min="1" oninput="game.updateDifficulty()" type="range"> |
||||
<span id="difficulty_text"></span> |
||||
<input id="velocity" max="60" min="1" oninput="game.updateVelocity()" type="range"> |
||||
<span id="velocity_text"></span> |
||||
<input id="cell_count" onchange="game.generateGrid();" type="range"> |
||||
<span id="cell_count_text"></span> |
||||
<select id="algorithm_type"> |
||||
<option value="astar">A* Algorithm</option> |
||||
<option value="dijkstra">Dijkstra Algorithm</option> |
||||
</select> |
||||
<button onclick="startAlgorithm();">Start</button> |
||||
<button onclick="game.generateGrid();">Generate</button> |
||||
</div> |
||||
</div> |
||||
</body> |
||||
</html> |
@ -0,0 +1,85 @@ |
||||
a:link, a:hover, a:active, a:visited{color: #000;} |
||||
|
||||
html, body{margin: 0; padding: 0; height: 100%; width: 100%;} |
||||
|
||||
canvas{margin: 0; padding: 0; border: none; display: block;} |
||||
|
||||
button:hover{cursor: pointer;} |
||||
|
||||
@font-face{ |
||||
font-family: "Rametto"; |
||||
src: url("data/styles/font.ttf"); |
||||
} |
||||
|
||||
*{ |
||||
font-family: "Rametto", serif; |
||||
color: #000; |
||||
font-size: 17px; |
||||
} |
||||
|
||||
:root{ |
||||
--width: 100vw; |
||||
--height: 100vh; |
||||
} |
||||
|
||||
/** |
||||
* Standard styles |
||||
*/ |
||||
|
||||
#canvas-holder{ |
||||
position: relative; |
||||
width: 100vh; |
||||
height: 100vh; |
||||
float: left; |
||||
} |
||||
#canvas-holder canvas{ |
||||
position: absolute; |
||||
top: 0; |
||||
left: 0; |
||||
right: 0; |
||||
bottom: 0; |
||||
border-radius: inherit; |
||||
} |
||||
#p5_loading{ |
||||
display: none; |
||||
} |
||||
|
||||
|
||||
|
||||
/** |
||||
* Interface |
||||
*/ |
||||
|
||||
#interface{ |
||||
width: calc(100% - 100vh); |
||||
height: 80vh; |
||||
padding: 10vh 0; |
||||
display: flex; |
||||
flex-direction: column; |
||||
justify-content: space-between; |
||||
align-items: center; |
||||
|
||||
float: left; |
||||
background-color: rgb(40, 40, 40); |
||||
} |
||||
|
||||
#interface > *{ |
||||
margin: 10px; |
||||
width: 300px; |
||||
text-align: center; |
||||
} |
||||
|
||||
span, label, select{ |
||||
color: rgb(0, 0, 0); |
||||
} |
||||
|
||||
button{ |
||||
background-color: rgb(50, 200, 50); |
||||
border: 2px solid #000; |
||||
} |
||||
button:hover{ |
||||
background-color: rgb(20, 100, 20); |
||||
} |
||||
|
||||
|
||||
|
After Width: | Height: | Size: 174 KiB |
Loading…
Reference in new issue