Wrong values with using gpu.js - javascript

I use my code in modes 'dev', 'gpu' and 'cpu'. When I run up my code in 'dev' mode I had right number of results word 'more' in almost 100mb file. There is 259200 words 'more'. But when I run it up in 'gpu' or 'cpu' mode, I have only 3 results from entire file. And second thing, even if I used 'gpu' and 'cpu' mode there is a time differences. When I used gpu it finish in 4000-4100ms and when I use cpu it finish in 2600-2700ms. I think using gpu should be faster than cpu in this searching. Hope for help!
const { GPU } = require('gpu.js');
const fs = require('fs');
const readline = require('readline');
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
const gpu = new GPU({ mode: 'cpu' });
rl.question('Wprowadż szukaną frazę: ', (value) => {
let phrase = value;
let start = new Date();
fs.readFile('plik2.txt', 'utf8', function(err, data) {
if (err) throw err;
let tempPhraseLen = phrase.length;
let tempDataLen = data.length;
let tempPhrase = [];
for (let i = 0; i < tempPhraseLen; i++) {
tempPhrase.push(phrase[i].charCodeAt(0));
}
let tempData = [];
for (let i = 0; i < tempDataLen; i++) {
tempData.push(data[i].charCodeAt(0));
}
function kernelFunction(pFunc, arrFunc, pLen, arrLen) {
let done = false;
let counter = 0;
for (let i = 0; i < arrLen; i++) {
if (arrFunc[i] === pFunc[0]) {
let isChosen = true;
let k = 0;
for (let j = i; j < i + pLen; j++) {
isChosen = true;
if (arrFunc[j] !== pFunc[k]) {
isChosen = false;
break;
}
k++;
}
if (isChosen) {
done = true;
counter++;
}
}
}
if (!done) {
return 0;
} else {
return counter;
}
}
const kernel = gpu.createKernel(kernelFunction, {
output: [ 1 ],
tactic: 'precision'
});
const result = kernel(tempPhrase, tempData, tempPhraseLen, tempDataLen);
if (result[0] === 0) {
console.log('Nie udało się znaleźć podanego wzorca');
} else {
console.log('Znalazłeś ' + result[0] + ' powtórzeń/nia frazy "' + phrase + '"');
}
let end = new Date() - start;
console.info('Time: %dms', end);
});
rl.close();
});

Related

How do I run code after all threads have finished running?

I have a multithreaded web crawler that downloads a website and stores it in a database (which takes around 4 minutes). To make the crawling faster, I used node.js cluster module, but I have a problem, I want to iterate over to the next segment of the while loop, after all the threads have done their processes, not as soon as they start. How do I make sure all my threads are concluded and then move on?
Here is the relevant code in the main while loop:
while (indexSize !== indexSizeLimit) {
const queueLength = queue.length;
const numberOfThreads = Math.min(numberOfCPUs, queueLength);
const threadAllocations = Array(numberOfThreads).fill(0);
let queuesAllocated = 0;
const queueChunks = [];
function fillQueueChunks() {
loop: while (true) {
for (let i = 0; i < numberOfThreads; i++) {
threadAllocations[i] += 1;
queuesAllocated += 1;
if (queuesAllocated === queueLength) {
break loop;
};
};
};
let start = 0;
for (let threadAllocation of threadAllocations) {
const end = start + threadAllocation;
queueChunks.push(queue.slice(start, end));
start = end;
};
};
fillQueueChunks();
// Find out how to make multithreading finish, and then move on with the loop.
if (cluster.isMaster) {
for (let i = 0; i < numberOfThreads; i++) {
cluster.fork();
};
} else {
const chunk = queueChunks[cluster.worker.id - 1];
await Promise.all(chunk.map(function (url) {
return new Promise(async function (resolve) {
const webcode = await request(url);
if (webcode !== "Failure") {
indexSize += 1;
const document = new Document(url, webcode);
const hrefs = document.hrefs();
const hrefsQuery = Query(hrefs);
// Also make sure it is not included in indexed webpages.
const hrefIndividualized = hrefsQuery.individualize();
hrefIndividualized;
// Do something with hrefIndividualized in regards to maintaining a queue in the database.
// And in adding a nextQueue which to replace the queue in code with.
await document.save();
};
resolve("Written");
});
}));
process.exit(0);
};
};
Wrap the threading in a promise. You can check in the parent thread if there is a disconnect event, and if the amount of disconnects is equal to the number of threads, then you can resolve the promise.
Here is what I have
while (indexSize !== indexSizeLimit) {
let nextQueue = [];
const queueLength = queue.length;
const numberOfThreads = Math.min(numberOfCPUs, queueLength);
const threadAllocations = Array(numberOfThreads).fill(0);
let queuesAllocated = 0;
// queueChunks: [[{_id: ..., ...}], [...], ...]
const queueChunks = [];
function fillQueueChunks() {
loop: while (true) {
for (let i = 0; i < numberOfThreads; i++) {
threadAllocations[i] += 1;
queuesAllocated += 1;
if (queuesAllocated === queueLength) {
break loop;
};
};
};
let start = 0;
for (let threadAllocation of threadAllocations) {
const end = start + threadAllocation;
queueChunks.push(queue.slice(start, end));
start = end;
};
};
fillQueueChunks();
await new Promise(async function (resolve) {
if (cluster.isMaster) {
let threadsDone = 0;
for (let i = 0; i < numberOfThreads; i++) {
cluster.fork();
};
cluster.on("disconnect", function (_) {
threadsDone += 1;
if (threadsDone === numberOfThreads) {
resolve("Queue Processed");
};
});
} else {
const queueJob = queueChunks[cluster.id - 1];
await Promise.all(queueJob.map(function (queueItem) {
return new Promise(async function (resolve) {
const url = queueItem._id;
const webcode = await request(url);
if (webcode !== "Failure") {
const document = Document(url, webcode);
let hrefs = document.hrefs();
const hrefsQuery = Query(hrefs);
await document.save();
indexSize += 1;
hrefs = hrefsQuery.individualize();
const hrefIncidences = Promise.all(hrefs.map(function (href) {
return new Promise(async function (resolve) {
const incidences = await Site.countDocuments({
url: href
});
resolve(incidences);
});
}));
hrefs = hrefs.filter(function (_, i) {
return hrefIncidences[i] === 0;
}).map(function (href) {
return {
_id: href
};
});
await Queue.insertMany(hrefs);
nextQueue = nextQueue.concat(hrefs);
};
await Queue.deleteOne({
_id: url
});
resolve("Success");
});
}));
process.exit(0);
};
});
queue = nextQueue;
};

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 combine multiple javascript files to 1 file using node.js

Hallo everyone,
I have created a big JavaScript Projekt with more than 10 js files. Now I want to combine the codes of all js files in 1 file. I have created a test file with node.js, but it does nothing and i dont know what the Problem is.
var fs = require("fs");
var codes = ["M:/HTML-Projekte/ChabanicSouls/combineFunc/file1.js","M:/HTML-Projekte/ChabanicSouls/combineFunc/file2.js","M:/HTML-Projekte/ChabanicSouls/combineFunc/file3.js"];
var combined = "";
for(let x = 0; x < codes.length; x++) {
fs.readFile(codes[x], "UTF-8", function(err, data) {
if (err) {
throw err;
}
combined += data;
});
}
fs.writeFile('M:/HTML-Projekte/ChabanicSouls/combineFunc/ugly.js', combined , function(err) {
if(err) {
return console.log(err);
}
console.log("The file was saved!");
});
If you have an Idee, how to solve this Problem, please let me know.
I have found a way to solve my Problem by my self,
var fs = require("fs");
const codes = ["M:/HTML-Projekte/ChabanicSouls/original_Codes/chaVar.js",
"M:/HTML-Projekte/ChabanicSouls/original_Codes/chaSocket.js",
"M:/HTML-Projekte/ChabanicSouls/original_Codes/chaChat.js",
"M:/HTML-Projekte/ChabanicSouls/original_Codes/chaVarMain.js",
"M:/HTML-Projekte/ChabanicSouls/original_Codes/chaField.js",
"M:/HTML-Projekte/ChabanicSouls/original_Codes/chaFriends.js",
"M:/HTML-Projekte/ChabanicSouls/original_Codes/chaArchive.js",
"M:/HTML-Projekte/ChabanicSouls/original_Codes/chaMain.js",
"M:/HTML-Projekte/ChabanicSouls/original_Codes/chaTimer.js",
"M:/HTML-Projekte/ChabanicSouls/original_Codes/chaActions.js",
"M:/HTML-Projekte/ChabanicSouls/original_Codes/chaFight.js",
"M:/HTML-Projekte/ChabanicSouls/original_Codes/chaKeys.js",
"M:/HTML-Projekte/ChabanicSouls/original_Codes/chaLvUp.js",
"M:/HTML-Projekte/ChabanicSouls/original_Codes/chaMarket.js",
"M:/HTML-Projekte/ChabanicSouls/original_Codes/chaMove.js",
"M:/HTML-Projekte/ChabanicSouls/original_Codes/chaNextPl.js",
"M:/HTML-Projekte/ChabanicSouls/original_Codes/chaParaUp.js",
"M:/HTML-Projekte/ChabanicSouls/original_Codes/chaSoul.js",
"M:/HTML-Projekte/ChabanicSouls/original_Codes/chaGetFunc.js",
"M:/HTML-Projekte/ChabanicSouls/original_Codes/chaStart.js"];
function readIt() {
let combined = [];
let doneCheck = [];
let errVal = false;
for (let x = 0; x < codes.length; x++) {
doneCheck.push(false);
}
for (let x = 0; x < codes.length; x++) {
fs.readFile(codes[x], "UTF-8", function (err, data) {
if (err || !data) {
console.log(codes[x]);
} else {
combined.push(data);
doneCheck[x] = true;
}
if (x == (codes.length - 1)) {
saveIt(combined, doneCheck);
}
});
}
}
function saveIt(combined, doneCheck, round = 0) {
let counter = 0;
for (let x = 0; x < doneCheck.length; x++) {
if (doneCheck[x] == false) {
fs.readFile(codes[x], "UTF-8", function (err, data) {
if (err || !data) {
console.log(codes[x]);
} else {
combined.push(data);
doneCheck[x] = true;
}
if (x == (codes.length - 1)) {
return saveIt(combined, doneCheck, round);
}
});
} else {
counter++;
}
}
if (counter < doneCheck.length) {
return false;
}
let combined_string = "";
for (let y = 0; y < combined.length; y++) {
combined_string = combined_string + combined[y] + " ";
}
fs.writeFile('M:/HTML-Projekte/ChabanicSouls/ugly.js', combined_string, function (err) {
if (err) {
return readIt();
}
console.log("The file was saved!");
});
}
readIt();
This way works perfectly :-)

iframe element returns undefined after onload event?

EDIT: Is there no one who can shed some light on this issue? Anything would be appreciated. :)
I have a script that is supposed to check to see if an elements html contains a given string..
When these elements do exist, my code throws this error: Uncaught TypeError: Cannot read property 'outerHTML' of null
This is the line: let check = document.querySelector("#iframe_${globalI}").contentWindow.document.querySelector(".Row"+inc).outerHTML
I then check to see if the string includes a check string.. IE: check.includes("Pre Trip")
If I run this line directly in the console it works and returns true... So what is going on here..?? How can I get this check to pass..?
I have this check executing after a setTimeout of 20 seconds, then wrapped again in another setTimeout for 500ms as I was trying to figure this out..
Also, I need to note that there are no XSS / CORS issues.
Here is my code..
function checkRowCount(x){
console.log("Row count called on "+x);
let rowCount = 0;
for(let i = 0; i < 30; i++){
if(typeof(document.querySelector(`#iframe_${x}`).contentWindow.document.querySelector('.Row'+i)) != 'undefined' && document.querySelector(`#iframe_${x}`).contentWindow.document.querySelector('.Row'+i) != null){
rowCount++;
}
}
console.log(rowCount);
return rowCount;
}
let globalCompiler = []; //globalCompiler[globalI] = {unit: unitNumber[globalI], data: ["X", " ", "NO POST TRIP]}
let unitNumber = [1031,1743,1744,1986,3239,3256,3257,4024,4062,4063,4064,4065,4247,4309,4315,4326,4327,4334,4335,4337,4350,4382,4385,7166,7380,7381,8765,8823,8945,8950,8988,10720,17045,17163,40014,40069,40122,40380,80129,80188,80700,80701,80702,80728,80831,80852,80875,"80876","81027","81038","401288","401306","402409","60099T","CH889","CH890","SR31077","T19","U5509","U6660","U6667","U6675","U8854","US1025T"];
let url = "http://winweb.cleanharbors.com/Vehicle/VehicleTDSearch.aspx?SearchType=DVIR";
function iframeLoaded(selector, unit, setDate, callback){
document.querySelector(`#iframe_${selector}`).contentWindow.document.querySelector("#txtStartDate").value = setDate;
document.querySelector(`#iframe_${selector}`).contentWindow.document.querySelector("#txtEndDate").value = setDate;
document.querySelector(`#iframe_${selector}`).contentWindow.document.querySelector("#txtVhcleNo").value = unit;
document.querySelector(`#iframe_${selector}`).contentWindow.document.querySelector("#btnRetrieve").click();
}
let loadFinished = {};
for(let dec = 0; dec < unitNumber.length; dec++){
loadFinished[unitNumber[dec]] = false;
}
console.log(loadFinished);
for(let globalI = 0; globalI < 3; globalI++){
globalCompiler[globalI] = {unit: unitNumber[globalI], data: []};
let iframeObj = document.createElement('iframe');
iframeObj.id = `iframe_${globalI}`;
iframeObj.hidden = false;
iframeObj.src = url;
iframeObj.onload = () => {
if (loadFinished[unitNumber[globalI]] == false) {
loadFinished[unitNumber[globalI]] = true;
let setDate = "11/01/2019";
iframeLoaded(globalI, unitNumber[globalI], setDate);
console.log("iframeloaded called on " + globalI);
setTimeout(() => {
setTimeout(() => {
let dateCheckObject = {}, rowCount = checkRowCount(globalI), trackingArr = [];
if (rowCount == 0) {
globalCompiler[globalI].data.push(" ");
} else {
for (let inc = 1; inc <= rowCount; inc++) {
//let check = $('#iframe_'+globalI).contents().find(`.Row` + inc).html().includes("Pre Trip");
let check = document.querySelector(`#iframe_${globalI}`).contentWindow.document.querySelector(".Row"+inc).outerHTML
if (check.includes("Pre Trip")) {
dateCheckObject.pre = true;
} else {
dateCheckObject.post = true;
}
}
if(dateCheckObject.pre && dateCheckObject.post) {
console.log("X");
globalCompiler[globalI].data.push("X");
dateCheckObject = {};
} else if (dateCheckObject.pre == 'undefined') {
console.log("NO PRE");
globalCompiler[globalI].data.push("NO PRE TRIP");
dateCheckObject = {};
} else {
console.log("NO POST");
globalCompiler[globalI].data.push("NO POST TRIP");
dateCheckObject = {};
}
}
},500);
}, 20000);
}
};
document.body.appendChild(iframeObj);
console.log("Global Loop called");
}
```
A for loop ran one count too far...
e.g.: for (let inc = 1; inc <= rowCount; inc++)
Should have been for (let inc = 1; inc < rowCount; inc++)

How to pause the entire code for 1 second in 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);

Categories

Resources