How to pause the entire code for 1 second in javascript - javascript

I am trying to code an evolutionary neural network for the game snake. I already coded the neural network part and now I'd like to output the game of the best individual of every generation. For that I'm using the drawing library p5.js (https://p5js.org/).
In my code I am running a loop in where a new generation based on the last generation is created. Each individual of the generation will have to play the game and that is how they are rated. Now I want the best individual to be outputted after I let every individual play once.
Between each outputted turn of the game of the best individual I want the code to wait 500 milliseconds. How do I achieve that?
Here is the code I've already tried but here it only outputed the board after the last turn of each generation:
async function start() {
for (let i = 0; i < 50; i++) {
population.createNewGeneration();
let bestGameTurns = population.bestIndividual.game.turns; //Array of boards
for (let turn = 0; turn < bestGameTurns.length; turn++) {
let board = bestGameTurns[turn];
drawBoard(board);
let p = new Promise(resolve => setTimeout(resolve, 500));
await p;
function drawBoard(board) {
//Draw the board using p5.js rect()'s
}
}
}
}
Another version but the waiting didn't work here:
let i = 0;
setInterval(async () => {
population.createNewGeneration();
console.log(i, population.avgFitness);
let bestGameTurns = population.bestIndividual.game.turns; //Array of boards
for (let turn = 0; turn < bestGameTurns.length; turn++) {
let board = bestGameTurns[turn];
drawBoard(board);
let p = new Promise(resolve => setTimeout(resolve, 500));
await p;
function drawBoard(board) {
//Draw the board using p5.js rect()'s
}
}
i++;
}, 1);

The code you provided should do what you asked for, I could only clean up some parts for you. Explain a bit better what is the problem you are facing.
// The function should be defined only once.
function drawBoard(board) { }
async function start() {
for (let i = 0; i < 50; i++) {
population.createNewGeneration();
const bestGameTurns = population.bestIndividual.game.turns; //Array of boards
for (let turn = 0; turn < bestGameTurns.length; turn++) {
// Don't wait on first iteration
await new Promise(resolve => setTimeout(resolve, 500 * (turn ? 0 : 1 )));
drawBoard(bestGameTurns[turn]);
}
}
}

Original idea (discarded)
You can create a short function like that:
function pause(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
Then in any async function you can call it like that:
async function () {}
// something happening
await pause(500);
// continue
}
The other idea
Now, the code in your question is not complete so this is kind of blind coding but.
So, first of all setInterval will be running the whole function every 1 millisecond (actually 4 ms, as this is the minimum in JS). Which means it will run those loops. I decided to focus on the part that was marked by you.
Instead of loop and trying to pause it, I ask that maybe rewriting the loop into function called each half second until condition is met would do?
Also, I move drawBoard outside
setInterval(async () => {
// ^^^^^^^^ <-- this should probably go
population.createNewGeneration();
console.log(i, population.avgFitness);
let bestGameTurns = population.bestIndividual.game.turns; //Array of boards
function tick(turn = 0) {
let board = bestGameTurns[turn];
function drawBoard(board) {
//Draw the board using p5.js rect()'s
}
drawBoard(board);
// here is "setTimeouted" loop
if (turn < bestGameTurns.length) {
setTimeout(tick, 500, turn + 1);
}
}
tick();
}, 1);

Thanks to everyone, your suggestions brought me to an idea. I found out that the problem was lying somewhere else. Because javascript only runs on the one thread (I think thats how its called), after we run over one generation, we have to stop that function to let another draw function, which runs every frame, draw the board. After it is drawn, the main function can continue. This is how it looks:
let isDrawn = false;
let currentBoard;
async function setup() {
for (let i = 0; i < 50; i++) {
population.createNewGeneration();
const bestGameTurns = population.bestIndividual.game.turns;
for (let turn = 0; turn < bestGameTurns.length; turn++) {
await step(bestGameTurns[turn], turn);
}
}
}
function step(board, turn) {
currentBoard = board;
isDrawn = false;
return new Promise(resolve => setTimeout(() => {
if (isDrawn) resolve();
}, 500));
}
setTimeout(() => {
if (currentBoard) {
drawBoard(currentBoard);
isDrawn = true;
currentBoard = undefined;
}
}, 1);
const nrOfCols = 10;
const nrOfRows = 10;
const fieldWidth = 20;
const nodeNrs = [24, 8, 8, 4];
const populationSize = 200;
const mutationRate = 0.01;
let population;
let game;
let isDrawn = false;
let currentBoard;
async function setup() {
createCanvas(500, 500);
population = new PopulationManager(populationSize);
for (let i = 0; i < 50; i++) {
population.createNewGeneration();
const bestGameTurns = population.bestIndividual.game.turns;
for (let turn = 0; turn < bestGameTurns.length; turn++) {
await step(bestGameTurns[turn]);
}
}
}
function step(board) {
currentBoard = board;
isDrawn = false;
return new Promise(resolve => setTimeout(() => {
if (isDrawn) resolve();
}, 500));
}
function draw() {
if (currentBoard) {
drawBoard(currentBoard);
isDrawn = true;
currentBoard = undefined;
}
}
function drawBoard(board) {
board.forEach((col, colNr) => {
col.forEach((field, rowNr) => {
fill(field.isSnake ? "green" : field.isFruit ? "red" : "black");
stroke(255);
rect(colNr*fieldWidth, rowNr*fieldWidth, fieldWidth, fieldWidth);
});
});
}
function play(game) {
setInterval(() => {
if (!game.lost) {
game.nextTurn();
drawBoard(game.board);
} else {
clearInterval(1);
}
}, 500);
}
class PopulationManager {
constructor(populationSize) {
this.population = createPopulation();
function createPopulation() {
let population = [];
for (let i = 0; i < populationSize; i++) {
let chromosomes = createRandomChromosomes();
let i = new Individual(chromosomes);
population.push(i);
}
return population;
function createRandomChromosomes() {
let arr = [];
let nrOfChromosomes = calcNrOfChromosomes();
for (let i = 0; i < nrOfChromosomes; i++)
arr.push(Math.random()*2-1);
return arr;
function calcNrOfChromosomes() {
let nr = 0;
for (let i = 0; i < nodeNrs.length - 1; i++)
nr += (nodeNrs[i] + 1)*nodeNrs[i + 1];
return nr;
}
}
};
}
createNewGeneration() {
let that = this;
getFitnessOfPop();
this.calcAvgFitness();
this.findBestIndividual();
let parents = selection();
breed(parents);
function getFitnessOfPop() {
that.population.forEach(iv => {
iv.fitness = iv.playGame();
});
that.population.sort((a, b) => a.fitness - b.fitness);
}
function selection() {
let totalFitness = that.population.map(iv => iv.fitness/* + 1 */).reduce((a,b) => a + b);
let allParents = [];
for (let i = 0; i < that.population.length/2; i++) {
allParents.push(selectRandomParents());
}
return allParents;
function selectRandomParents() {
let p1, p2;
do {
p1 = selectRandomParent();
p2 = selectRandomParent();
} while (p1 == p2);
return [p1, p2];
function selectRandomParent() {
let rdm = Math.random()*totalFitness;
return that.population.find((iv, i) => {
let sum = that.population.filter((iv2, i2) => i2 <= i).map(iv => iv.fitness /* + 1 */).reduce((a,b) => a + b);
return rdm <= sum;
});
}
}
}
function breed(allParents) {
that.population = [];
allParents.forEach(ps => {
let childChromosomes = crossOver(ps[0].chromosome, ps[1].chromosome);
childChromosomes = [mutate(childChromosomes[0]), mutate(childChromosomes[1])];
let child1 = new Individual(childChromosomes[0]);
let child2 = new Individual(childChromosomes[1]);
that.population.push(child1);
that.population.push(child2);
});
function crossOver(parent1Chromosome, parent2Chromosome) {
let crossingPoint = Math.round(Math.random()*parent1Chromosome.length);
let divided1 = divideChromosome(parent1Chromosome);
let divided2 = divideChromosome(parent2Chromosome);
let child1Chromosome = divided1[0].concat(divided2[1]);
let child2Chromosome = divided2[0].concat(divided1[1]);
return [child1Chromosome, child2Chromosome];
function divideChromosome(chromosome) {
let part1 = chromosome.filter((g, i) => i <= crossingPoint);
let part2 = chromosome.filter((g, i) => i > crossingPoint);
return [part1, part2];
}
}
function mutate(chromosome) {
chromosome = chromosome.map(g => {
if (Math.random() < mutationRate)
return Math.random()*2-1;
return g;
});
return chromosome;
}
}
}
calcAvgFitness() {
this.avgFitness = this.population.map(iv => iv.fitness).reduce((a, b) => a + b) / this.population.length;
}
findBestIndividual() {
let bestFitness = -1, bestIndividual;
this.population.forEach(i => {
if (i.fitness > bestFitness) {
bestFitness = i.fitness;
bestIndividual = i;
}
});
this.bestIndividual = bestIndividual;
}
}
class Individual {
constructor(chromosome) {
this.chromosome = chromosome;
this.fitness = 0;
this.game = createGame();
function createGame() {
let weights = convertChromosomeToWeights();
let game = new Game(weights);
return game;
function convertChromosomeToWeights() {
let weights = [];
for (let i = 0; i < nodeNrs.length - 1; i++) {
let lArr = [];
for (let j = 0; j < nodeNrs[i] + 1; j++) {
let nArr = [];
lArr.push(nArr);
}
weights.push(lArr);
}
chromosome.forEach((gene, geneIdx) => {
let lIdx = -1, minIdx, maxIdx = 0;
for (let i = 0; i < nodeNrs.length - 1; i++) {
let nr = 0;
for (let j = 0; j <= i; j++)
nr += (nodeNrs[j] + 1)*nodeNrs[j + 1];
if (geneIdx < nr) {
lIdx = i;
break;
}
maxIdx = nr;
minIdx = maxIdx;
}
minIdx = maxIdx;
let nIdx = -1;
for (let i = 0; i < nodeNrs[lIdx] + 1; i++) {
let nr = minIdx + nodeNrs[lIdx + 1];;
if (geneIdx < nr) {
nIdx = i;
break;
}
maxIdx = nr;
minIdx = maxIdx;
}
minIdx = maxIdx;
let wIdx = -1;
for (let i = 0; i < nodeNrs[lIdx + 1]; i++) {
let nr = minIdx + 1;
if (geneIdx < nr) {
wIdx = i;
break;
}
maxIdx = nr;
minIdx = maxIdx;
}
weights[lIdx][nIdx][wIdx] = gene;
});
return weights;
}
}
}
playGame() {
while (!this.game.lost) {
this.game.nextTurn();
}
return this.game.score;
}
}
class Game {
constructor(weights) {
let that = this;
this.chromosome = flattenArray(weights);
this.nn = new NeuralNetwork(weights);
this.turnNr = 0;
this.score = 0;
this.lost = false;
this.board = createBoard();
this.snake = new Snake();
setupSnake();
this.createFruit();
this.turns = [JSON.parse(JSON.stringify(this.board))];
function createBoard() {
let board = [];
for (let colNr = 0; colNr < nrOfCols; colNr++) {
board[colNr] = [];
for (let rowNr = 0; rowNr < nrOfRows; rowNr++) {
let field = new Field(colNr, rowNr);
board[colNr][rowNr] = field;
}
}
return board;
}
function setupSnake() {
for (let i = 0; i < 4; i++)
that.addToTail([floor(nrOfCols/2) - i, floor(nrOfRows/2)]);
that.length = that.snake.body.length;
}
function flattenArray(arr) {
let flattened = [];
flatten(arr);
return flattened;
function flatten(arr) {
arr.forEach(e => {
if (Array.isArray(e))
flatten(e);
else
flattened.push(e);
});
}
}
}
addToTail(pos) {
this.snake.body.push(pos);
this.board[pos[0]][pos[1]].setSnake(true);
}
nextTurn() {
let that = this;
let direction = findDirection();
this.moveSnake(direction);
this.turns.push(JSON.parse(JSON.stringify(this.board)));
this.turnNr++;
checkEat();
function findDirection() {
let inputValues = [];
for (let i = 0; i < 8; i++) {
let distances = that.snake.look(i, that.board);
inputValues.push(distances.distToFruit);
inputValues.push(distances.distToWall);
inputValues.push(distances.distToBody);
}
let output = that.nn.getOutput(inputValues);
let probability = -1;
let direction = -1;
output.forEach((v, vIdx) => {
if (v > probability) {
probability = v;
direction = vIdx;
}
});
return direction;
}
function checkEat() {
let head = that.snake.body[0];
let headField = that.board[head[0]][head[1]];
if (headField.isFruit) {
that.snake.eat();
that.score++;
headField.setFruit(false);
that.createFruit();
}
}
}
createFruit() {
let field;
do {
let colNr = floor(random()*nrOfCols);
let rowNr = floor(random()*nrOfRows);
field = this.board[colNr][rowNr];
} while(field.isSnake);
field.setFruit(true);
}
moveSnake(newDirection) {
let that = this;
let oldBody = JSON.parse(JSON.stringify(that.snake.body));
moveTail();
makeSnakeLonger();
moveHead();
function moveTail() {
for (let i = oldBody.length - 1; i > 0; i--)
that.snake.body[i] = oldBody[i - 1];
}
function moveHead() {
let newHeadPosition = findNewHeadPos();
if (
newHeadPosition[0] >= nrOfCols || newHeadPosition[0] < 0 ||
newHeadPosition[1] >= nrOfRows || newHeadPosition[1] < 0
) {
that.lose();
return;
}
let newHeadField = that.board[newHeadPosition[0]][newHeadPosition[1]];
if (newHeadField.isSnake) {
that.lose();
return;
}
addNewHeadPos(newHeadPosition);
}
function findNewHeadPos() {
if (newDirection == 0) { //up
return [oldBody[0][0], oldBody[0][1] - 1];
} else if (newDirection == 1) { //right
return [oldBody[0][0] + 1, oldBody[0][1]];
} else if (newDirection == 2) { //down
return [oldBody[0][0], oldBody[0][1] + 1];
} else if (newDirection == 3) { //left
return [oldBody[0][0] - 1, oldBody[0][1]];
}
}
function makeSnakeLonger() {
if (that.snake.length > that.snake.body.length) {
that.addToTail(oldBody[oldBody.length - 1]);
} else {
removeFromTail(oldBody[oldBody.length - 1]);
}
}
function removeFromTail(pos) {
that.board[pos[0]][pos[1]].setSnake(false);
}
function addNewHeadPos(pos) {
that.snake.body[0] = pos;
that.board[pos[0]][pos[1]].setSnake(true);
}
}
lose() {
this.lost = true;
}
}
class Field {
constructor(col, row) {
this.col = col;
this.row = row;
this.isFruit = false;
this.isSnake = false;
}
setFruit(bool) {
this.isFruit = bool;
}
setSnake(bool) {
this.isSnake = bool;
}
}
class Snake {
constructor() {
this.body = [];
this.length = 4;
}
eat() {
this.length++;
}
look(direction, board) {
let distances = {distToFruit: 0, distToWall: 0, distToBody: 0};
let xDiff = getXDiff(direction), yDiff = getYDiff(direction);
let pos = [this.body[0][0] + xDiff, this.body[0][1] + yDiff];
let dist = 1;
while (pos[0] > 0 && pos[0] < nrOfRows && pos[1] > 0 && pos[1] < nrOfCols) {
if (board[pos[0]][pos[1]].isFruit && distances.distToFruit == 0) distances.distToFruit = dist;
if (board[pos[0]][pos[1]].isSnake && distances.distToBody == 0) distances.distToBody = dist;
pos[0] += xDiff, pos[1] += yDiff;
dist++;
}
distances.distToWall = dist;
return distances;
function getXDiff(direction) {
if (direction == 5 || direction == 6 || direction == 7) return -1;
else if (direction == 1 || direction == 2 || direction == 3) return 1;
return 0;
}
function getYDiff(direction) {
if (direction == 7 || direction == 0 || direction == 1) return -1;
else if (direction == 3 || direction == 4 || direction == 5) return 1;
return 0;
}
}
}
class NeuralNetwork {
constructor(weights) {
this.layers = createLayers();
this.layers = addWeights(this.layers, weights);
function createLayers() {
let layers = [];
let nrOfNodesGlobal;
nodeNrs.forEach((nrOfNodes, lNr) => {
nrOfNodesGlobal = nrOfNodes;
layers[lNr] = [];
for (let i = 0; i < nrOfNodes; i++) {
let node = createNode(lNr);
layers[lNr][i] = node;
}
if (lNr != nodeNrs.length - 1)
layers[lNr].push(new Bias());
});
return layers;
function createNode(lNr) {
if (lNr == 0) return new InputLayerNode();
else if (lNr == nrOfNodesGlobal - 1) return new OutputLayerNode();
else return new HiddenLayerNode();
}
}
function addWeights(layers, weights) {
for (let lNr = 0; lNr < layers.length - 1; lNr++) {
let l = layers[lNr];
l.forEach((n1, nNr) => {
for (let n2Nr = 0; n2Nr < layers[lNr+1].length - 1; n2Nr++) { //not including bias of next layer
let n2 = layers[lNr+1][n2Nr];
let weight = weights[lNr][nNr][n2Nr];
let w = new Weight(n1, n2, weight);
n1.addWeight(w);
}
});
}
return layers;
}
}
getOutput(inputValues) {
let output = [];
this.layers[0].forEach((inputNeuron, nNr) => {
if (nNr != this.layers[0].length - 1)
inputNeuron.addToInput(inputValues[nNr]);
});
this.layers.forEach((l, lNr) => {
calcOutputs(l);
if (lNr != this.layers.length - 1) {
l.forEach(n => {
n.feedForward();
});
} else {
output = l.map(n => n.output);
}
});
return output;
function calcOutputs(layer) {
layer.forEach(n => n.output = n.activationFunction(n.summedInput, layer.map(N => N.summedInput)));
}
}
log() {
console.log(this.weights, this.nodes);
}
}
class Node {
constructor() {
this.weights = [];
this.summedInput = 0;
}
addWeight(w) {
this.weights.push(w);
}
addToInput(input) {
if (input == NaN)
console.log("A");
this.summedInput += input;
}
feedForward() {
this.weights.forEach((w, wNr) => {
let input = w.weight*this.output;
w.to.addToInput(input);
});
}
}
class Bias extends Node {
constructor() {
super();
this.output = 1;
}
activationFunction(x, allXs) {
return x;
}
}
class InputLayerNode extends Node {
constructor() {
super();
}
activationFunction(x, allXs) {
return x;
}
}
class HiddenLayerNode extends Node {
constructor() {
super();
}
activationFunction(x, allXs) {
return leakyReLU(x);
}
}
class OutputLayerNode extends Node {
constructor() {
super();
}
activationFunction(x, allXs) {
return softmax(x, allXs);
}
}
class Weight {
constructor(from, to, weight) {
this.from = from;
this.to = to;
this.weight = weight;
}
setWeight(newWeight) {
this.weight = weight;
}
}
function leakyReLU(x) {
if (x >= 0) return x;
else return 0.01*x;
}
function softmax(x, allXs) {
return Math.exp(x) / allXs.map(X => Math.exp(X)).reduce((a, b) => a+b);
}
<!DOCTYPE html>
<html>
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.8.0/p5.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.8.0/addons/p5.dom.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.8.0/addons/p5.sound.min.js"></script>
<link rel="stylesheet" type="text/css" href="style.css">
<meta charset="utf-8" />
</head>
<body>
<script src="sketch.js"></script>
</body>
</html>
It's not working that well but a few improvements should make it better...
If you have any suggestions for improvements of the code, please let me know!

I tried to fix it into steps as I said in comments,I hope I have no mistake :
let i = 0;
async function step(bestGameTurns, turn)
{
if (turn == bestGameTurns.length)
return;
let board = bestGameTurns[turn];
drawBoard(board);
let p = new Promise(resolve => setTimeout(() => step(bestGameTurns, turn+1), 500));
await p;
}
function drawBoard(board) {
//Draw the board using p5.js rect()'s
}
setInterval(async () => {
population.createNewGeneration();
console.log(i, population.avgFitness);
let bestGameTurns = population.bestIndividual.game.turns; //Array of boards
step(bestGameTurns, 0);
i++;
}, 1);

Related

Click event listener is being repeatedly triggered without any clicks?

I am developing a "Battleship" game with two grids made up of divs and am currently attempting to add a click event listener to all of the divs.
The issue that I am having is that the event listener is being repeatedly triggered (until every single div has been clicked) when I refresh my page and I can't understand why...
Here's the event listener in question:
let aiGridCells = document.querySelectorAll(".ai-grid__game-cell");
aiGridCells.forEach(cell => {
cell.addEventListener("click", humanPlayer.humanAttack(cell.getAttribute('data-ai'),aiPlayer))
});
Where humanPlayer is an object that has been generated by a factory function:
const humanPlayer = playerFactory('human');
import gameboardFactory from './gameboardFactory';
const playerFactory = (name) => {
const playerBoard = gameboardFactory();
const humanAttack = (cell, player) => { // Where player is opponent
if (player.playerBoard.gameBoard[cell].id !== 'miss') {
player.playerBoard.receiveAttack(cell);
};
};
const aiAttack = (player) => { // Where player is opponent
const availableCells = [];
for (let i = 0; i < player.playerBoard.gameBoard.length; i++) {
if (player.playerBoard.gameBoard[i].id === null) {
availableCells.push(i);
};
};
const attackCell = Math.floor(Math.random() * availableCells.length);
player.playerBoard.receiveAttack(attackCell);
};
return {
name,
playerBoard,
humanAttack,
aiAttack
}
};
export default playerFactory;
and gameboardFactory is:
import shipFactory from './shipFactory';
const gameboardFactory = () => {
const gameBoard = [];
const shipYard = [];
const init = () => {
for (let i = 0; i<100; i++) {
gameBoard.push({id: null})
};
};
const checkValidCoordinates = (direction, start, end) => {
if (direction === 'horizontal') {
if ((start <= 9) && (end <= 9)) {
return true;
} else {
let newStart = (start/10).toString(10);
let newEnd = (end/10).toString(10);
if ((newStart.charAt(0)) === (newEnd.charAt(0))) {
return true;
};
};
} else {
if ((start <= 9) && (end <= 9)) {
return false
} else if (start <= 9) {
let newStart = start.toString(10);
let newEnd = end.toString(10);
if ((newStart.charAt(0)) === (newEnd.charAt(1))) {
return true;
};
} else {
let newStart = start.toString(10);
let newEnd = end.toString(10);
if ((newStart.charAt(1)) === (newEnd.charAt(1))) {
return true;
};
};
};
return false
};
const checkIfShipPresent = (direction, start, end) => {
if (direction === 'horizontal') {
for (let i = start; i <= end; i++) {
if (gameBoard[i].id !== null) {
return true;
}
};
return false;
} else {
for (let i = start; i <= end; i += 10) {
if (gameBoard[i].id !== null) {
return true;
}
};
return false;
};
};
const placeShip = (id, direction, start, end) => {
if (!checkValidCoordinates(direction, start, end)) {
return;
};
if (checkIfShipPresent(direction, start, end)) {
return;
};
const newShip = [];
if (direction === 'horizontal') {
for (let i = start; i <= end; i++) {
gameBoard[i].id = id;
newShip.push(i);
};
} else {
for (let i = start; i <= end; i += 10) {
gameBoard[i].id = id;
newShip.push(i);
};
};
shipYard.push(shipFactory(id, newShip));
};
const receiveAttack = (cell) => {
console.log(cell)
if (gameBoard[cell].id !== null) {
const attackedShip = shipYard.filter((ship) => {
return ship.id === gameBoard[cell].id;
})[0];
if (!attackedShip.hits.includes(cell)) {
attackedShip.hits.push(cell);
};
} else {
gameBoard[cell].id = 'miss';
};
};
const checkIfAllShipsSunk = () => {
let allShipsSunk = true;
shipYard.forEach((ship) => {
if (ship.isSunk() === false) {
allShipsSunk = false;
};
});
return allShipsSunk;
};
if (gameBoard.length === 0) {
init();
};
return {
gameBoard,
placeShip,
receiveAttack,
shipYard,
checkIfAllShipsSunk
};
};
export default gameboardFactory;
I'm completely lost to what the issue could be and have tried countless things to rectify it. Any suggestions would be hugely appreciated.
Thank you!
You trying to add actual function call as listener here:
let aiGridCells = document.querySelectorAll(".ai-grid__game-cell");
aiGridCells.forEach(cell => {
cell.addEventListener("click", humanPlayer.humanAttack(cell.getAttribute('data-ai'),aiPlayer))
});
So on your event listener initialization you actually call your function instead of passing it as a listener.
You can pass it like this instead:
aiGridCells.forEach(cell => {
cell.addEventListener("click", () => humanPlayer.humanAttack(cell.getAttribute('data-ai'),aiPlayer))
});

update priority of queue in javascript

in hasValue class, why return is not working? when i try with console.log and alert, it worked.
want to implement function like priorityQueue.changePriority("Sheru", 1); changePriority class is not working.
commented code is code i tried to implement the changes i.e. i want to change the priority of existing item present in queue. Could anyone please help?
class QElement {
constructor(element, priority) {
this.element = element;
this.priority = priority;
}
}
class PriorityQueue {
constructor() {
this.items = [];
}
isEmpty() {
return this.items.length == 0;
}
add(element, priority) {
var qElement = new QElement(element, priority);
var contain = false;
for (var i = 0; i < this.items.length; i++) {
if (this.items[i].priority > qElement.priority) {
this.items.splice(i, 0, qElement);
contain = true;
break;
}
}
if (!contain) {
this.items.push(qElement);
}
}
peek() {
if (this.isEmpty())
return "No elements in Queue";
return this.items[0];
}
poll() {
if (this.isEmpty())
return "Underflow";
return this.items.shift();
}
/*changePriority(firstTerm, secondTerm)
{
let xxx = new QElement(firstTerm, secondTerm);
for (let i = 0; i < this.items.length; i++){
if (this.items[i].element === firstTerm){
this.items[i].priority = secondTerm;
this.items.splice(i, 0, xxx);
}
}
this.items.push(xxx);
}*/
hasValue(args) {
let status = false;
for (let i = 0; i < this.items.length; i++) {
if (this.items[i].element === args) {
status = true;
}
}
console.log(status);
}
size() {
if (this.isEmpty())
return "Underflow";
return this.items.length;
}
printPQueue() {
var str = "";
for (var i = 0; i < this.items.length; i++)
str += this.items[i].element + " ";
return str;
}
}
var priorityQueue = new PriorityQueue();
console.log(priorityQueue.isEmpty());
console.log(priorityQueue.peek());
priorityQueue.add("Sumit", 2);
priorityQueue.add("Gourav", 1);
priorityQueue.add("Piyush", 1);
priorityQueue.add("Sunny", 2);
priorityQueue.add("Sheru", 3);
console.log(priorityQueue.printPQueue());
console.log(priorityQueue.peek().element);
console.log(priorityQueue.poll().element);
priorityQueue.add("Sunil", 2);
console.log(priorityQueue.size());
priorityQueue.hasValue('Sumit');
console.log(priorityQueue.printPQueue());
priorityQueue.changePriority("Sheru", 1);
console.log(priorityQueue.printPQueue());
You missing return keyword. This just works:
hasValue(args) {
for (let i = 0; i < this.items.length; i++) {
if (this.items[i].element === args) {
return true;
}
}
return false;
}
I did not understand the idea how your changePriority function should work. Just find the element and move it up or down based on priority change:
swap(a, b) {
let tmp = this.items[a];
this.items[a] = this.items[b];
this.items[b] = tmp;
}
changePriority(firstTerm, secondTerm) {
let i = 0;
while (i < this.items.length) {
if (this.items[i].element === firstTerm) {
if (secondTerm < this.items[i].priority) {
// move up
this.items[i].priority = secondTerm;
while (i > 0 && this.items[i - 1].priority > secondTerm) {
this.swap(i - 1, i);
i--;
}
} else if (secondTerm > this.items[i].priority) {
// move down
this.items[i].priority = secondTerm;
while (i < this.items.length - 1 && this.items[i + 1].priority < secondTerm) {
this.swap(i + 1, i);
i++;
}
}
break;
}
i++;
}
}

A* Algorithm - Javascript: Why does my algorithm not find the shortest path

I am working on visualizing A*. I have a problem where the algorithm finds a path, but it is not the shortest. If I remove the part of the A* function code which is commented as 'tie-breaking', the algorithm finds the shortest path, but it searches the whole grid just like Dijkstra's algorithm, which I don't think A* is supposed to do. These are the pictures of the results with and without tie-breaking:
With Tie-Breaking
Without Tie-Breaking
What is wrong? Here is my A* function:
async a_star_search() {
this.clearSearchNotWalls();
let openSet = [];
let closedSet = [];
let start, end;
let path = [];
this.findNeighbors();
//shapes is a 2d array of squares... a grid
for (let i = 0; i < this.shapes.length; i++) {
for (let j = 0; j < this.shapes[0].length; j++) {
if (this.shapes[i][j].type == "Start") {
start = this.shapes[i][j];
}
if (this.shapes[i][j].type == "End") {
end = this.shapes[i][j];
}
}
}
openSet.push(start);
while (openSet.length > 0) {
let lowestIndex = 0;
//find lowest index
for (let i = 0; i < openSet.length; i++) {
if (openSet[i].F < openSet[lowestIndex].F)
lowestIndex = i;
}
//current node
let current = openSet[lowestIndex];
//if reached the end
if (openSet[lowestIndex] === end) {
path = [];
let temp = current;
path.push(temp);
while (temp.cameFrom) {
path.push(temp.cameFrom);
temp = temp.cameFrom;
}
console.log("Done!");
for (let i = path.length - 1; i >= 0; i--) {
this.ctxGrid.fillStyle = "#ffff00";
this.ctxGrid.fillRect(path[i].x, path[i].y, 14, 14);
await new Promise(resolve =>
setTimeout(() => {
resolve();
}, this.animDelay / 2)
);
}
break;
}
this.removeFromArray(openSet, current);
closedSet.push(current);
let my_neighbors = current.neighbors;
for (let i = 0; i < my_neighbors.length; i++) {
var neighbor = my_neighbors[i];
if (!closedSet.includes(neighbor) && neighbor.type != "Wall") {
let tempG = current.G + 1;
let newPath = false;
if (openSet.includes(neighbor)) {
if (tempG < neighbor.G) {
neighbor.G = tempG;
newPath = true;
}
} else {
neighbor.G = tempG;
newPath = true;
openSet.push(neighbor);
}
if (newPath) {
neighbor.H = this.heuristic(neighbor, end);
neighbor.G = neighbor.F + neighbor.H;
neighbor.cameFrom = current;
}
}
}
//draw
for (let i = 0; i < closedSet.length; i++) { //BLUE
this.ctxGrid.fillStyle = "#4287f5";
this.ctxGrid.fillRect(closedSet[i].x, closedSet[i].y, 14, 14);
}
for (let i = 0; i < openSet.length; i++) { //GREEN
this.ctxGrid.fillStyle = "#00ff00";
this.ctxGrid.fillRect(openSet[i].x, openSet[i].y, 14, 14);
}
await new Promise(resolve =>
setTimeout(() => {
resolve();
}, 10)
);
}
if (openSet.length <= 0) {
//no solution
}
}
Here is my heuristic function:
heuristic(a, b) {
//let d = Math.sqrt(Math.pow(b.I - a.I, 2) + Math.pow(b.J - a.J, 2));
let d = Math.sqrt(Math.pow(b.x - a.x, 2) + Math.pow(b.y - a.y, 2));
return d;
}

How to fix similar predictions in tenserflow.js

I'm doing a coding challenge from the coding train, and I'm trying to improve on his code. The idea is that the cars are driving around a race track. When I went back to check something, I noticed that I misspelled "activation: sigmoid", as in activation function. When I fixed it, the cars seemed to be driving in circles.
I'm a very new coder (as I am 12 years old), so many things in my code are broken, hard to understand, or just not finished. I'm also pretty new to stack overflow, so I might be breaking a lot of rules.
The link to download my project is here: https://1drv.ms/u/s!ApmY_SAko19ChzCKe5uNT7I9EZAX?e=YUg2ff
The misspelled words are at lines 29 and 34 in the nn.js file.
car.js
function pldistance(p1, p2, x, y) {
const num = abs((p2.y - p1.y) * x - (p2.x - p1.x) * y + p2.x * p1.y - p2.y * p1.x);
const den = p5.Vector.dist(p1, p2);
return num / den;
}
class Car {
constructor(brain, color = [random(255), random(255), random(255)]) {
this.colorGene = color;
this.dead = false;
this.finished = false;
this.fitness = 0;
this.rays = [];
this.wallRays = [];
this.degreeOfSight = degreeOfSight;
this.degreeOfRays = degreeOfSight / (numOfRays - 1);
if (this.degreeOfSight == 360) {
this.degreeOfRays = degreeOfSight / numOfRays;
}
this.pos = createVector(start.x, start.y);
this.vel = createVector();
this.acc = createVector();
this.sight = sight;
this.maxspeed = maxspeed;
this.maxforce = maxTurningSpeed;
this.currentGoal = 0;
this.timeTillDeadC = timeTillDead;
this.timeTillDead = this.timeTillDeadC;
this.goal;
this.rate = mutationRate;
if (degreeOfSight != 360) {
for (let a = -(this.degreeOfSight / 2); a <= this.degreeOfSight / 2; a += this.degreeOfRays) {
this.rays.push(new Ray(this.pos, radians(a)));
}
} else {
for (let a = -(this.degreeOfSight / 2); a < this.degreeOfSight / 2; a += this.degreeOfRays) {
this.rays.push(new Ray(this.pos, radians(a)));
}
}
for (let a = 0; a < 360; a += 45) {
this.wallRays.push(new Ray(this.pos, radians(a)));
}
if (brain) {
this.brain = brain.copy();
} else {
this.brain = new NeuralNetwork(this.rays.length + 2, 16, 2);
}
}
applyForce(force) {
this.acc.add(force);
}
update(x, y) {
this.timeTillDead--;
if (this.timeTillDead <= 0) {
this.dead = true;
}
if (!this.dead || this.finished) {
this.pos.add(this.vel);
this.vel.add(this.acc);
this.vel.limit(this.maxspeed);
this.acc.set(0, 0);
}
for (let i = 0; i < this.rays.length; i++) {
this.rays[i].rotate(this.vel.heading());
}
for (let i = 0; i < this.wallRays.length; i++) {
this.wallRays[i].rotate(this.vel.heading());
}
}
show(walls) {
push();
translate(this.pos.x, this.pos.y);
if (visualization) {
fill(this.colorGene[0], this.colorGene[1], this.colorGene[1]);
} else {
fill(0);
}
stroke(255);
const heading = this.vel.heading();
rotate(heading);
rectMode(CENTER);
rect(0, 0, 10, 5);
pop();
if (!this.dead) {
checkpoints[this.currentGoal].show();
}
for (let i = 0; i < this.rays.length; i++) {
let closest = null;
let record = this.sight;
for (let wall of walls) {
const pt = this.rays[i].cast(wall);
if (pt) {
const d = p5.Vector.dist(this.pos, pt);
if (d < record && d < this.sight) {
record = d;
closest = pt;
}
}
}
if (closest) {
if (showLines) {
ellipse(closest.x, closest.y, 4)
stroke(255, 100)
line(this.pos.x, this.pos.y, closest.x, closest.y);
}
}
}
}
check(checkpoints, walls) {
if (!this.dead) {
this.goal = checkpoints[this.currentGoal];
const d = pldistance(this.goal.a, this.goal.b, this.pos.x, this.pos.y);
if (d < 5) {
this.fitness++;
this.currentGoal++;
this.timeTillDead = this.timeTillDeadC;
if (this.currentGoal == checkpoints.length) {
this.finished = true;
this.fitness = this.fitness * 1.5;
if (endBarrier) {
this.dead = true;
} else {
this.currentGoal = 0;
}
}
}
}
for (let i = 0; i < this.wallRays.length; i++) {
let closest = null;
let record = this.sight;
for (let wall of walls) {
const pt = this.wallRays[i].cast(wall);
if (pt) {
const d = p5.Vector.dist(this.pos, pt);
if (d < record) {
record = d;
closest = pt;
}
}
}
if (record < 4) {
this.dead = true;
}
}
}
look(walls) {
const inputs = [];
for (let i = 0; i < this.wallRays.length; i++) {
let closest = null;
let record = this.sight;
for (let wall of walls) {
const pt = this.rays[i].cast(wall);
if (pt) {
const d = p5.Vector.dist(this.pos, pt);
if (d < record && d < this.sight) {
record = d;
closest = pt;
}
}
}
inputs[i] = map(record, 0, 50, 1, 0);
}
inputs.push(end.x);
inputs.push(end.y);
const output = this.brain.predict(inputs);
let angle = map(output[0], 0, 1, -PI, PI);
let speed = map(output[1], 0, 1, -this.maxspeed, this.maxspeed);
angle += this.vel.heading();
const steering = p5.Vector.fromAngle(angle);
steering.setMag(speed);
steering.limit(this.maxforce);
this.applyForce(steering);
}
mutateDemBabies() {
if (this.finished) {
this.rate = finishingMutationRate;
}
this.brain.mutate(this.rate);
let changeColor = this.brain.mutated();
if (changeColor) {
for (let color of this.colorGene) {
let r = map(random(20), 0, 20, -25, 25);
color += r;
}
}
this.rate = mutationRate;
}
dispose() {
this.brain.dispose();
}
}
nn.js
//<script src="https://cdn.jsdelivr.net/npm/#tensorflow/tfjs#1.1.0/dist/tf.min.js"></script>
class NeuralNetwork {
//this how many inputs, hidden, and output nodes there are. modelC is the brain that we want to copy to give to the new bird
constructor(inputNumber, hiddenNumber, outputNumber, modelC) {
if (modelC instanceof tf.Sequential) {
//this is the making a copy of the neural network
this.input_nodes = inputNumber;
this.hidden_nodes = hiddenNumber;
this.output_nodes = outputNumber;
this.model = modelC;
} else {
//this is the creating a random brain
this.input_nodes = inputNumber;
this.hidden_nodes = hiddenNumber;
this.output_nodes = outputNumber;
this.model = this.createBrain();
}
this.changeColor = false;
}
createBrain() {
//the model is the neural network
const model = tf.sequential();
//configuring the hidden layer
const hiddenLayer = tf.layers.dense({
units: this.hidden_nodes,
inputShape: [this.input_nodes],
activaation: "sigmoid"
});
//configuring the output layer
const outputLayer = tf.layers.dense({
units: this.output_nodes,
activaation: "sigmoid"
});
//adding the hidden layer to the model
model.add(hiddenLayer);
//adding the output layer to the model
model.add(outputLayer);
//returning the model
return model;
}
predict(inputs) {
//clearing the tensors after using them
//then returning the output
return tf.tidy(() => {
//creating a tensor with the inputs
const xs = tf.tensor2d([inputs]);
//running the inputs through the neural network
const ys = this.model.predict(xs);
//getting the raw numbers from the tensor object
const outputs = ys.dataSync();
//returning the outputs
return outputs;
});
}
copy() {
//clearing the tensors after using them
//then returning the output
return tf.tidy(() => {
//creating a new neural network
const modelCopy = this.createBrain();
//getting the weights from the old neural network
const weights = this.model.getWeights();
//setting the new weights
modelCopy.setWeights(weights);
//making a new network but this time with all the weights then returning it
return new NeuralNetwork(
this.input_nodes,
this.hidden_nodes,
this.output_nodes,
modelCopy
);
});
}
mutate(rate, colorGene) {
//clearing the tensors after using them
tf.tidy(() => {
this.changeColor = false;
//getting the weights so that we can change them later
const weights = this.model.getWeights();
//the variable that will be holding the mutated weights
const mutatedWeights = [];
for (let i = 0; i < weights.length; i++) {
//getting the shape of the current weights
let shape = weights[i].shape;
//making a copy of the raw numbers from the object tensor
//dataSync gets the numbers, but doesn't make a copy, so slice will make the copy
let values = weights[i].dataSync().slice();
for (let j = 0; j < values.length; j++) {
//if the random number is less than mutation rate the it runs the code
if (random(1) < rate) {
this.changeColor = true;
//mutating the value
//randomGaussianis returns a float from a series of numbers with a mean of 0
values[j] = values[j] + randomGaussian();
}
}
//holding the new value of each weight
mutatedWeights[i] = tf.tensor(values, shape);
}
//setting the mutated weights as the new weights
this.model.setWeights(mutatedWeights);
});
}
mutated() {
if (this.changeColor) {
this.changeColor = false;
return true;
} else {
this.changeColor = false;
return false;
}
}
dispose() {
//disposing the brain so that memory doesn't leak
this.model.dispose();
}
}

Why don't all web workers send back data with a postMessage?

I call postMessage on 4 different web workers in a loop. I have tried making different files for each of them and using the same file for each, same results. My main thread webworker onmessage function only gets called once. I need my other three web workers to return their chunks!
html
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>The HTML5 Herald</title>
<meta name="description" content="The HTML5 Herald">
<meta name="author" content="SitePoint">
<link rel="stylesheet" href="./index.css">
</head>
<body>
<input type="text" id='input'><div style="word-wrap: break-word;" id='output'></div>
<script src="./m1worker.js"></script>
<script src="./m2worker.js"></script>
<script src="./m3worker.js"></script>
<script src="./m4worker.js"></script>
<script src="./IntToRomanNumeral.js"></script>
</body>
</html>
worker.js
self.onmessage = (e) => {
console.log('worker file', e.data);
let marr;
if (e.data[0]) {
marr = new Array(e.data[0]);
for (let i = 0; i < e.data[0]; i++) {
marr[i] = 'M';
}
}
self.postMessage([marr.join(""), e.data[1]]);
self.close();
};
main thread
const THREADCOUNT = 4;
const goodmap = Array(THREADCOUNT);
const workernames = [];
const setupGoodMapAndNamesArr = () => {
for (let i = 0; i < THREADCOUNT; i++) {
goodmap[i] = false;
workernames.push('./m'+(i+1)+'worker.js');
}
}
const stalltillgood = (cb) => {
console.log('stalling');
const realgood = goodmap.every((thread) => {
return thread;
});
if (realgood) {
cb();
}
else {
setTimeout(stalltillgood.bind(this, cb), 1000);
}
}
const workerReturn = (e) => {
console.log(e.data);
output.appendChild(document.createTextNode(e.data[0]));
goodmap[e.data[1]] = true;
}
const ITRN = (str) => {
setupGoodMapAndNamesArr();
const workers = [];
for (let i = 0; i < THREADCOUNT; i++) {
workers.push(new Worker(workernames[i]));
}
var cNode = output.cloneNode(false);
output.parentNode.replaceChild(cNode ,output);
console.time("Start!");
if (/^\d+$/.test(str)) {
//generate the intermediate (invalid - doesn't follow rule)
let inputnum = +str;
if (inputnum > 100000000000) {
return 'YOU MIGHT NOT HAVE ENOUGH MEMORY FOR THAT!'
}
let romannumeral = [];
let numM = Math.floor(inputnum / 1000);
if (numM) {
inputnum -= numM * 1000;
}
const numD = Math.floor(inputnum / 500);
if (numD) {
inputnum -= numD * 500;
}
const numC = Math.floor(inputnum / 100);
if (numC) {
inputnum -= numC * 100;
}
const numL = Math.floor(inputnum / 50);
if (numL) {
inputnum -= numL * 50;
}
const numX = Math.floor(inputnum / 10);
if (numX) {
inputnum -= numX * 10;
}
const numV = Math.floor(inputnum / 5);
if (numV) {
inputnum -= numV * 5;
}
console.time('Start pushing intermediate');
console.time('PUSH MS!');
if (numM >= 4) {
const remain = numM % 4;
numM = Math.floor(numM / 4);
for (let i = 0; i < THREADCOUNT; i++) {
workers[0].onmessage = workerReturn;
}
workers[0].postMessage([numM+remain, 0]);
for (let i = 1; i < THREADCOUNT; i++) {
workers[i].postMessage([numM, i]);
}
}
else {
for (let i = 0; i < THREADCOUNT; i++) {
goodmap[i] = true;
}
}
stalltillgood(() => {
//appendChild the text chunks after =() and see
console.log('we are good');
console.timeEnd('PUSH MS!');
for (let i = 0; i < numD; i++) {
romannumeral.push('D');
}
for (let i = 0; i < numC; i++) {
romannumeral.push('C');
}
for (let i = 0; i < numL; i++) {
romannumeral.push('L');
}
for (let i = 0; i < numX; i++) {
romannumeral.push('X');
}
for (let i = 0; i < numV; i++) {
romannumeral.push('V');
}
for (let i = 0; i < inputnum; i++) {
romannumeral.push('I');
}
console.timeEnd('Start pushing intermediate');
//find invalid strings in the intermediate
//generate an array of new str segments to be joined
//replace the invalid strings with their valid forms
const invalidtovalidmap = {
I: 'IV',
V: 'VX',
X: 'XL',
C: 'CD',
D: 'DM',
};
const nextturtleupmap = {
I: 'V',
X: 'L',
C: 'D',
};
const turtlingmap = {
I: 'IX',
C: 'CM',
X: 'XC',
C: 'CM',
};
let validromannumeral;
let count = 0;
let i;
let m = romannumeral.indexOf('M');
if (m !== -1) {
let lastm = romannumeral.lastIndexOf('M');
validromannumeral = romannumeral.slice(m, lastm);
i = lastm++;
}
else {
validromannumeral = [];
i = 0;
}
let lastnumeral = romannumeral[i];
validromannumeral.push(lastnumeral);
i++;
console.time('compilering');
for (;i < romannumeral.length; i++) {
if (lastnumeral === romannumeral[i]) {
if (count === 2) {
count = 0;
validromannumeral.pop();
validromannumeral.pop();
validromannumeral.pop();
if (nextturtleupmap[romannumeral[i]]) {
if (validromannumeral[validromannumeral.length-1] === nextturtleupmap[romannumeral[i]]) {
validromannumeral.pop();
validromannumeral.push(turtlingmap[romannumeral[i]]);
}
else {
validromannumeral.push(invalidtovalidmap[romannumeral[i]]);
}
}
else {
validromannumeral.push(invalidtovalidmap[romannumeral[i]]);
}
}
else {
++count;
validromannumeral.push(romannumeral[i]);
}
}
else {
lastnumeral = romannumeral[i];
count = 0;
validromannumeral.push(romannumeral[i]);
}
}
console.timeEnd('compilering');
console.timeEnd('Start!');
console.time('OUTPUT!');
output.appendChild(document.createTextNode(validromannumeral.join("")));
});
}
else {
return 'INVALID INTEGER';
}
}
window.onload = () => {
const input = document.getElementById('input');
const output = document.getElementById('output');
input.oninput = () => {
ITRN(input.value);
console.timeEnd('OUTPUT!');
}
}
Lol, took a break, came back, dicked around with function vs arrow expression AGAIN, then finally saw that in my loop assigning my web workers' onmessage I indexed the array with a constant 0 instead of i. =) Yay threaded GB of Ms!

Categories

Resources