Detect coherent neighbors / neighborhood in 2d array - javascript

Im having an arbitrary 2d array and each field has an id and a teamid (here illustrated as colors 1).
I want for every neighborhood an array with the ids
in it.
A neighborhood consists of fields with neighbors with the same teamid horizontally and vertically (not diagonally)
e.g.:
This is what i have:
array[0][0] = {id:1,teamId:1}
array[1][0] = {id:2,teamId:1}
array[2][0] = {id:3,teamId:0}
array[3][0] = {id:4,teamId:2}
array[4][0] = {id:5,teamId:2}
array[5][0] = {id:6,teamId:0}
array[0][1] = {id:7,teamId:1}
array[1][1] = {id:8,teamId:1}
array[2][1] = {id:9,teamId:1}
array[3][1] = {id:10,teamId:2}
array[4][1] = {id:11,teamId:2}
array[5][1] = {id:12,teamId:0}
//and so on..
This is what i want:
neighborhood[1] = [1,2,7,8,9,13,14]
neighborhood[2] = [4,5,10,11]
neighborhood[3] = [16,22,23,24,29,30]
neighborhood[4] = [25,31,32,37,38]
neighborhood[5] = [35,41]
I am not searching for the images, but for the array
neighborhood
thanks in advance!

You can use the logic from dots and block games. A block belongs to a player if he has surrounded it with the walls. So, you need for each cell also 4 walls except for the outer cells. To test if a cell is closed you can use 4 class variables:
var Block = function() {
this.isclosed=0;
this.left=0;
this.top=0;
this.right=0;
this.bottom=0;
return this;
}
Block.prototype = {
isClosed : function () {
if (this.isclosed==true) {
return false;
} else if (this.left && this.top && this.right && this.bottom) {
this.isclosed=true;
return true;
} else {
return this.left && this.top && this.right && this.bottom;
}
}
}
You can try my implementations of dots and blocks game # https://dotsgame.codeplex.com/.

The method for solving this issue is refered as Connected Component Labelling
A similar question was asked once before from which i have my solution:
// matrix dimensions
var row_count = 20;
var col_count = 20;
var numOfTeams = 2;
// the input matrix
var m = [];
// the labels, 0 means unlabeled
var label = [];
var source = document.getElementById("source");
for (var i = 0; i < row_count; i++) {
var row = source.insertRow(0);
m[i] = [];
label[i] = [];
for (var j = 0; j < col_count; j++) {
//m[i][j] = Math.round(Math.random());
m[i][j] = getRandomInt(0, numOfTeams + 1);
label[i][j] = 0;
var cell1 = row.insertCell(0);
cell1.innerHTML = m[i][j];
}
}
// direction vectors
var dx = [1, 0, -1, 0];
var dy = [0, 1, 0, -1];
function dfs(x, y, current_label, team) {
if (x < 0 || x == row_count) return; // out of bounds
if (y < 0 || y == col_count) return; // out of bounds
if (label[x][y] || team != m[x][y]) return; // already labeled or not marked with 1 in m
// mark the current cell
label[x][y] = current_label;
// recursively mark the neighbors
for (var direction = 0; direction < 4; ++direction) {
dfs(x + dx[direction], y + dy[direction], current_label, team);
}
}
function find_components() {
var component = 0;
for (var i = 0; i < row_count; ++i) {
for (var j = 0; j < col_count; ++j) {
if (!label[i][j] && m[i][j]) dfs(i, j, ++component, m[i][j]);
}
}
}
find_components();
var result = document.getElementById("result");
for (var i in label) {
var string = ""
var row = result.insertRow(0);
for (var j in label[i]) {
string += label[i][j] + " "
var cell1 = row.insertCell(0);
cell1.innerHTML = label[i][j];
}
}
function getRandomInt(min, max) {
return Math.floor(Math.random() * (max - min)) + min;
}
table tr td {
min-width: 14px
}
<div style="float:left">
<table id="source"></table>
</div>
<div style="float:right">
<table id="result"></table>
</div>

Related

hidding a line of text until game is compleate

I have a maze game ( making player and end goal later) and im trying to make a span that will show up once the game is completed. so far its doing nothing.
Ive tried this
var mazeComplete = document.getElementById("mazeComplete");
var gameComplete = false;
function checkGameFinished() {
if(gameComplete = false){
mazeComplete.style.visibility = 'hidden'
} else if (gameComplete = true) {
mazeComplete.style.visibility = 'visible'
}
}
but with all my code its not working at all. just wanted to see if i could hide the text.
html:
<head>
<title>Maze</title>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
<script src="mazegenerator.js"></script>
<script type="text/javascript" src="Maze.js"></script>
<style type="text/css">
#maze {
border-collapse: collapse;
}
#maze td {
width: 20px;
height: 20px;
}
canvas {
position: absolute;
}
</style>
</head>
<body>
<table id="maze">
<tbody></tbody>
</table>
<script>
var disp = newMaze(20,20);
for (var i = 0; i < disp.length; i++) {
$('#maze > tbody').append("");
for (var j = 0; j < disp[i].length; j++) {
var selector = i+"-"+j;
$('#maze > tbody').append("<td id='"+selector+"'> </td>");
if (disp[i][j][0] == 0) { $('#'+selector).css('border-top', '2px solid black'); }
if (disp[i][j][1] == 0) { $('#'+selector).css('border-right', '2px solid black'); }
if (disp[i][j][2] == 0) { $('#'+selector).css('border-bottom', '2px solid black'); }
if (disp[i][j][3] == 0) { $('#'+selector).css('border-left', '2px solid black'); }
}
$('#maze > tbody').append("");
}
</script>
<br>
<span id="mazeComplete">Maze complete <button id="resetMaze" onclick="mazeReset()">New Maze</button> </span>
Completed <span id="mazeCompletions">0</span>
</body>
</html>
Javascript:
function newMaze(x, y) {
// Establish variables and starting grid
var totalCells = x*y;
var cells = new Array();
var unvis = new Array();
for (var i = 0; i < y; i++) {
cells[i] = new Array();
unvis[i] = new Array();
for (var j = 0; j < x; j++) {
cells[i][j] = [0,0,0,0];
unvis[i][j] = true;
}
}
// Set a random position to start from
var currentCell = [Math.floor(Math.random()*y), Math.floor(Math.random()*x)];
var path = [currentCell];
unvis[currentCell[0]][currentCell[1]] = false;
var visited = 1;
// Loop through all available cell positions
while (visited < totalCells) {
// Determine neighboring cells
var pot = [[currentCell[0]-1, currentCell[1], 0, 2],
[currentCell[0], currentCell[1]+1, 1, 3],
[currentCell[0]+1, currentCell[1], 2, 0],
[currentCell[0], currentCell[1]-1, 3, 1]];
var neighbors = new Array();
// Determine if each neighboring cell is in game grid, and whether it has already been checked
for (var l = 0; l < 4; l++) {
if (pot[l][0] > -1 && pot[l][0] < y && pot[l][1] > -1 && pot[l][1] < x && unvis[pot[l][0]][pot[l][1]]) { neighbors.push(pot[l]); }
}
// If at least one active neighboring cell has been found
if (neighbors.length) {
// Choose one of the neighbors at random
next = neighbors[Math.floor(Math.random()*neighbors.length)];
// Remove the wall between the current cell and the chosen neighboring cell
cells[currentCell[0]][currentCell[1]][next[2]] = 1;
cells[next[0]][next[1]][next[3]] = 1;
// Mark the neighbor as visited, and set it as the current cell
unvis[next[0]][next[1]] = false;
visited++;
currentCell = [next[0], next[1]];
path.push(currentCell);
}
// Otherwise go back up a step and keep going
else {
currentCell = path.pop();
}
}
return cells;
}
function mazeReset(){
document.location.reload()
}
var mazeComplete = document.getElementById("mazeComplete");
var gameComplete = false;
function checkGameFinished() {
if(gameComplete = false){
mazeComplete.style.visibility = 'hidden'
} else if (gameComplete = true) {
mazeComplete.style.visibility = 'visible'
}
}
I expect when game is launched for
<span id="mazeComplete">Maze complete <button id="resetMaze" onclick="mazeReset()">New Maze</button> </span>
to be hidden until gamecomplete becomes true. but at the moment its not hidden at all.
js fiddle:
https://jsfiddle.net/pma17sfq/3/
There are several things wrong with your code:
You are never calling checkGameFinished(), so the check and the hiding are never performed
By the time your script is executed, the element #mazeComplete does not exist yet. Move your script to the end of the body.
In your if conditions, gameComplete = false and gameComplete = true are not comparing the value, but setting it! Change the operator to === to perform a type-safe comparison.
var disp = newMaze(20, 20);
for (var i = 0; i < disp.length; i++) {
$('#maze > tbody').append("<tr>");
for (var j = 0; j < disp[i].length; j++) {
var selector = i + "-" + j;
$('#maze > tbody').append("<td id='" + selector + "'> </td>");
if (disp[i][j][0] == 0) {
$('#' + selector).css('border-top', '2px solid black');
}
if (disp[i][j][1] == 0) {
$('#' + selector).css('border-right', '2px solid black');
}
if (disp[i][j][2] == 0) {
$('#' + selector).css('border-bottom', '2px solid black');
}
if (disp[i][j][3] == 0) {
$('#' + selector).css('border-left', '2px solid black');
}
}
$('#maze > tbody').append("<tr>");
}
function newMaze(x, y) {
// Establish variables and starting grid
var totalCells = x * y;
var cells = new Array();
var unvis = new Array();
for (var i = 0; i < y; i++) {
cells[i] = new Array();
unvis[i] = new Array();
for (var j = 0; j < x; j++) {
cells[i][j] = [0, 0, 0, 0];
unvis[i][j] = true;
}
}
// Set a random position to start from
var currentCell = [Math.floor(Math.random() * y), Math.floor(Math.random() * x)];
var path = [currentCell];
unvis[currentCell[0]][currentCell[1]] = false;
var visited = 1;
// Loop through all available cell positions
while (visited < totalCells) {
// Determine neighboring cells
var pot = [
[currentCell[0] - 1, currentCell[1], 0, 2],
[currentCell[0], currentCell[1] + 1, 1, 3],
[currentCell[0] + 1, currentCell[1], 2, 0],
[currentCell[0], currentCell[1] - 1, 3, 1]
];
var neighbors = new Array();
// Determine if each neighboring cell is in game grid, and whether it has already been checked
for (var l = 0; l < 4; l++) {
if (pot[l][0] > -1 && pot[l][0] < y && pot[l][1] > -1 && pot[l][1] < x && unvis[pot[l][0]][pot[l][1]]) {
neighbors.push(pot[l]);
}
}
// If at least one active neighboring cell has been found
if (neighbors.length) {
// Choose one of the neighbors at random
next = neighbors[Math.floor(Math.random() * neighbors.length)];
// Remove the wall between the current cell and the chosen neighboring cell
cells[currentCell[0]][currentCell[1]][next[2]] = 1;
cells[next[0]][next[1]][next[3]] = 1;
// Mark the neighbor as visited, and set it as the current cell
unvis[next[0]][next[1]] = false;
visited++;
currentCell = [next[0], next[1]];
path.push(currentCell);
}
// Otherwise go back up a step and keep going
else {
currentCell = path.pop();
}
}
return cells;
}
function mazeReset() {
document.location.reload()
}
var gameComplete = false;
var mazeComplete = document.getElementById("mazeComplete");
function checkGameFinished() {
if (gameComplete === false) {
mazeComplete.style.visibility = 'hidden'
} else if (gameComplete === true) {
mazeComplete.style.visibility = 'visible'
}
}
checkGameFinished();
#maze {
border-collapse: collapse;
}
#maze td {
width: 20px;
height: 20px;
}
canvas {
position: absolute;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<table id="maze">
<tbody></tbody>
</table>
<br>
<span id="mazeComplete">Maze complete <button id="resetMaze" onclick="mazeReset()">New Maze</button> </span> Completed <span id="mazeCompletions">0</span>
I agree with #Constantin in general but your most "immediate" problem you have, where "the span is always visible" is because, in your fiddle at least, the <span> is not hidden at start.. you have to make it like:
`<span hidden id="mazeComplete">`
Then later on when the time comes you will set its visibility property like you do.

Non-loading Results with insertCell.innerHTML

For some reason when I put use .innerHTML to change the text of the cell, the code never stops and stays within the last statement. How do I get the code to run how it's supposed to run and not stop within the last if statement.
w3.filterHTML = function(id, sel, filter) {
var a, b, c, i, ii, iii, hit;
var searchItem = document.getElementById('search-list');
var row = searchItem.insertRow(0);
var rows = searchItem.rows.length;
var cell = row.insertCell(0);
a = w3.getElements(id);
for (i = 0; i < a.length; i++) {
b = w3.getElements(sel);
for (ii = 0; ii < b.length; ii++) {
hit = 0;
if (b[ii].innerHTML.toUpperCase().indexOf(filter.toUpperCase()) > -1) {
hit = 1;
}
c = b[ii].getElementsByTagName("*");
for (iii = 0; iii < c.length; iii++) {
if (c[iii].innerHTML.toUpperCase().indexOf(filter.toUpperCase()) > -1) {
hit = 1;
}
}
//Here is what you need to edit.
if (hit == 1) {
// b[ii].style.display = "";
console.log(filter);
var row = searchItem.insertRow(0);
var cell = row.insertCell(0);
document.getElementById("search-list").insertRow(0).insertCell(0).innerHTML = '<a class="list-group-item" href="#">Hello</a>';
} else {
// searchItem.b[ii].style.display = "none";
}
}
}
};
`
I copied the code from W3Schools (the filterHTML function).

Set a custom % of 1's into a 2D blank array, where the 1's are randomly shuffled?

I've been a long time lurker on Stack Overflow but I couldn't seem to find a suitable existing solution...
I'm learning JS and HTML, and I've been playing around with 2D arrays to make game board. So far I made a custom # of rows/columns for a game board with all white tiles (represented as 0 for now).
My goal is to use an input field for a % of black tiles (represented as 1) to fill up the board (2D Array), but the black tiles have to be randomly distributed/shuffled among it.
Here's what I've got so far..
https://jsfiddle.net/5pvm4mmy/6/
function generateArray() {
var myNode = document.getElementById("table");
while (myNode.firstChild) {
myNode.removeChild(myNode.firstChild);
}
rows = $("#rows-field").val();
cols = $("#cols-field").val();
concentration = $("#concentration-field").val()
source = $("#source-field").val();
target = $("#target-field").val();
var table = document.getElementById("table");
for (var i = 0; i < rows; i++) {
var tr = document.createElement('tr');
for (var j = 0; j < cols; j++) {
var td = document.createElement('td');
if (i%2 == j%2) {
td.className = "white";
} else {
td.className = "black";
}
tr.appendChild(td);
}
table.appendChild(tr);
}
document.body.appendChild(table);
}
Thanks in advance for any help or advice.
If you need a random selection of a predefined set of values, you can use a stack. Think of it as a deck of cards and you pick a random card each time from the number of card left in the deck. In this case you have only 2 values but you may want to set the number of black and white. For this you can use a pseudo stack.
var black = 29; // 29 blacks the rest white
var white = (rows * cols) - black;
function getRandSquare(){
var select = Math.floor(Math.random() * (black + white));
if(select > black){
white -= 1;
return "white";
}
black -= 1;
return "black";
}
If you have many options like a deck of cards you use an array.
Example of a random stack.
// create a deck
var cards = [];
for(var c = 0; c < 52; c++){
cards[c] = c;
}
function shuffle(cards){
var shuf = []; // to hold the shuffled deck
while(cards.length > 0){ // pick a random item, take it from the stack and
// put on the new stack until there are no items
// left
shuf.push(cards.splice(Math.floor(Math.random() * cards.length),1));
}
return shuf; // return shuffled deck
}
cards = shuffle(cards); // get shuffled deck.
Which will work for anything where you need to pick randomly from a predefined set. It only takes one pass and the set is as random as the random number generator
To show psuedo stack working ... Always has 60 black
var cont;
function draw(){
var rows = 15;
var cols = 15;
var black = 60; // 29 blacks the rest white
var white = (rows * cols) - black;
function getRandSquare(){
var select = Math.floor(Math.random() * (black + white));
if(select > black-1){
white -= 1;
return "white";
}
black -= 1;
return "black";
}
var bCount = 0;
cont = document.createElement("div");
for(var y = 0; y < rows; y++){
for(var x = 0; x < cols; x++){
var s = document.createElement("span");
s.className = getRandSquare();
if(s.className === "black"){
s.textContent = bCount;
bCount += 1;
}
s.style.top = ((y+2) * 20) + "px";
s.style.left = (x * 20) + "px";
s.style.width = "20px";
s.style.height = "20px";
cont.appendChild(s);
}
}
document.body.appendChild(cont);
}
document.body.onclick = function(){
document.body.removeChild(cont);
cont = null;
draw();
}
draw();
span {
position:absolute;
border : 1px solid;
font-size : small;
text-align : center;
}
.black {
background : black;
border-color :white;
color : white;
}
.white {
background : white;
border-color :black;
}
<h3>Click to randomise</h3>
Never mind. I got it done, thanks!
https://jsfiddle.net/5pvm4mmy/8/
function generateArray() {
var myNode = document.getElementById("table");
while (myNode.firstChild) {
myNode.removeChild(myNode.firstChild);
}
rows = $("#rows-field").val();
cols = $("#cols-field").val();
concentration = $("#concentration-field").val();
source = $("#source-field").val();
target = $("#target-field").val();
var table = document.getElementById("table");
for (var i = 0; i < rows; i++) {
var tr = document.createElement('tr');
for (var j = 0; j < cols; j++) {
var td = document.createElement('td');
if (concentration < Math.floor((Math.random() * 100) + 1)) {
td.className = "white";
} else {
td.className = "black";
}
tr.appendChild(td);
}
table.appendChild(tr);
}
document.body.appendChild(table);
}

How do I bold one line in a Google Docs Script?

I'm writing a script to parse a Google Sheet and format the cells nicely on a Doc. I'd like the cell data from column 1 to always be bold and the cell data from column 6 to always be Italic. The problem is, after appending a paragraph to the document body, the attribute changes are applied to the entire document. Is there a way to bold/italicize the cell data before appending it to the doc body?
function readRows() {
var sheet = SpreadsheetApp.getActiveSheet();
var rows = sheet.getDataRange();
var numRows = rows.getNumRows();
var numCols = rows.getNumColumns();
var values = rows.getValues();
var doc = DocumentApp.create("Smogon Formatted");
var docBody = doc.getBody();
for (var i = 2; i <= numRows; i++) {
for (var j = 1; j <= numCols; j++){
var cellData = rows.getCell(i, j).getValue()
// Format data based on column
if (j == 1) {
docBody.appendParagraph(cellData).editAsText().setBold(true);
} else if (j == 2 || j == 3) {
var imgFormula = rows.getCell(i, j).getFormula();
var imgUrl = getImageUrl(imgFormula);
docBody.appendParagraph("[img]" + imgUrl + "[/img]");
} else if (j == 6) {
docBody.appendParagraph(cellData).editAsText().setItalic(true);
} else {
docBody.appendParagraph(cellData);
}
}
}
};
EDIT: Try #2, using the setAttributes method
function readRows() {
var sheet = SpreadsheetApp.getActiveSheet();
var rows = sheet.getDataRange();
var numRows = rows.getNumRows();
var numCols = rows.getNumColumns();
var values = rows.getValues();
var doc = DocumentApp.create("Smogon Formatted");
var docBody = doc.getBody();
for (var i = 2; i <= numRows; i++) {
for (var j = 1; j <= numCols; j++){
var cellData = rows.getCell(i, j).getValue()
// Format data based on column
if (j == 1) {
docBody.appendParagraph(cellData).setAttributes(style1);
} else if (j == 2 || j == 3) {
var imgFormula = rows.getCell(i, j).getFormula();
var imgUrl = getImageUrl(imgFormula);
docBody.appendParagraph("[img]" + imgUrl + "[/img]");
} else if (j == 6) {
docBody.appendParagraph(cellData).setAttributes(style2);
} else {
docBody.appendParagraph(cellData);
}
}
}
};
// Style definitions as global variables
var style1= {};
style1[DocumentApp.Attribute.BOLD] = true;
var style2= {};
style2[DocumentApp.Attribute.ITALIC] = true;
If you use style attributes you can assign a style to every paragraph very easily, you can actually do it for any document element...
Here is a basic example code to show how it works :
(doc here)
function exportToDoc(){
var doc = DocumentApp.openById('16i----L53WTDpzuLyhqQQ_E');// or create a new doc (but not while you test it :-)
var body = doc.getBody();
var sheet = SpreadsheetApp.getActiveSheet();
var values = sheet.getDataRange().getValues();
for (var i in values){
var rowData = values[i].join(' + ');
if (i == 1) {
body.appendParagraph(rowData).setAttributes(style2);
} else if (i == 2 ) {
body.appendParagraph(rowData).setAttributes(style1)
}
}
doc.saveAndClose();
}
// Style definitions as global variables
var style1 = {};// style example 1
style1[DocumentApp.Attribute.FONT_SIZE] = 10;
style1[DocumentApp.Attribute.FONT_FAMILY] = DocumentApp.FontFamily.CONSOLAS;
style1[DocumentApp.Attribute.FOREGROUND_COLOR] = "#444400";
var style2 = {};// style example 2
style2[DocumentApp.Attribute.FONT_SIZE] = 16;
style2[DocumentApp.Attribute.FONT_FAMILY] =DocumentApp.FontFamily.ARIAL_NARROW;
style2[DocumentApp.Attribute.FOREGROUND_COLOR] = "#005500";
//
example random data result :

Neural network in Javascript not learning properly

I've tried to rewrite neural network found here to javascript. My javascript code looks like this.
function NeuralFactor(weight) {
var self = this;
this.weight = weight;
this.delta = 0;
}
function Sigmoid(value) {
return 1 / (1 + Math.exp(-value));
}
function Neuron(isInput) {
var self = this;
this.pulse = function() {
self.output = 0;
self.input.forEach(function(item) {
self.output += item.signal.output * item.factor.weight;
});
self.output += self.bias.weight;
self.output = Sigmoid(self.output);
};
this.bias = new NeuralFactor(isInput ? 0 : Math.random());
this.error = 0;
this.input = [];
this.output = 0;
this.findInput = function(signal) {
var input = self.input.filter(function(input) {
return signal == input.signal;
})[0];
return input;
};
}
function NeuralLayer() {
var self = this;
this.pulse = function() {
self.neurons.forEach(function(neuron) {
neuron.pulse();
});
};
this.neurons = [];
this.train = function(learningRate) {
self.neurons.forEach(function(neuron) {
neuron.bias.weight += neuron.bias.delta * learningRate;
neuron.bias.delta = 0;
neuron.input.forEach(function(input) {
input.factor.weight += input.factor.delta * learningRate;
input.factor.delta = 0;
})
})
}
}
function NeuralNet(inputCount, hiddenCount, outputCount) {
var self = this;
this.inputLayer = new NeuralLayer();
this.hiddenLayer = new NeuralLayer();
this.outputLayer = new NeuralLayer();
this.learningRate = 0.5;
for(var i = 0; i < inputCount; i++)
self.inputLayer.neurons.push(new Neuron(true));
for(var i = 0; i < hiddenCount; i++)
self.hiddenLayer.neurons.push(new Neuron());
for(var i = 0; i < outputCount; i++)
self.outputLayer.neurons.push(new Neuron());
for (var i = 0; i < hiddenCount; i++)
for (var j = 0; j < inputCount; j++)
self.hiddenLayer.neurons[i].input.push({
signal: self.inputLayer.neurons[j],
factor: new NeuralFactor(Math.random())
});
for (var i = 0; i < outputCount; i++)
for (var j = 0; j < hiddenCount; j++)
self.outputLayer.neurons[i].input.push({
signal: self.hiddenLayer.neurons[j],
factor: new NeuralFactor(Math.random())
});
this.pulse = function() {
self.hiddenLayer.pulse();
self.outputLayer.pulse();
};
this.backPropagation = function(desiredResults) {
for(var i = 0; i < self.outputLayer.neurons.length; i++) {
var outputNeuron = self.outputLayer.neurons[i];
var output = outputNeuron.output;
outputNeuron.error = (desiredResults[i] - output) * output * (1.0 - output);
}
for(var i = 0; i < self.hiddenLayer.neurons.length; i++) {
var hiddenNeuron = self.hiddenLayer.neurons[i];
var error = 0;
for(var j = 0; j < self.outputLayer.neurons.length; j++) {
var outputNeuron = self.outputLayer.neurons[j];
error += outputNeuron.error * outputNeuron.findInput(hiddenNeuron).factor.weight * hiddenNeuron.output * (1.0 - hiddenNeuron.output);
}
hiddenNeuron.error = error;
}
for(var j = 0; j < self.outputLayer.neurons.length; j++) {
var outputNeuron = self.outputLayer.neurons[j];
for(var i = 0; i < self.hiddenLayer.neurons.length; i++) {
var hiddenNeuron = self.hiddenLayer.neurons[i];
outputNeuron.findInput(hiddenNeuron).factor.delta += outputNeuron.error * hiddenNeuron.output;
}
outputNeuron.bias.delta += outputNeuron.error * outputNeuron.bias.weight;
}
for(var j = 0; j < self.hiddenLayer.neurons.length; j++) {
var hiddenNeuron = self.hiddenLayer.neurons[j];
for(var i = 0; i < self.inputLayer.neurons.length; i++) {
var inputNeuron = self.inputLayer.neurons[i];
hiddenNeuron.findInput(inputNeuron).factor.delta += hiddenNeuron.error * inputNeuron.output;
}
hiddenNeuron.bias.delta += hiddenNeuron.error * hiddenNeuron.bias.weight;
}
};
this.train = function(input, desiredResults) {
for(var i = 0; i < self.inputLayer.neurons.length; i++) {
var neuron = self.inputLayer.neurons[i];
neuron.output = input[i];
}
self.pulse();
self.backPropagation(desiredResults);
self.hiddenLayer.train(self.learningRate);
self.outputLayer.train(self.learningRate);
};
}
Now I'm trying to learn it how to resolve XOR problem. I'm teaching it like this:
var net = new NeuralNet(2,2,1);
var testInputs = [[0,0], [0,1], [1,0], [1,1]];
var testOutputs = [[1],[0],[0],[1]];
for (var i = 0; i < 1000; i++)
for(var j = 0; j < 4; j++)
net.train(testInputs[j], testOutputs[j]);
function UseNet(a, b) {
net.inputLayer.neurons[0].output = a;
net.inputLayer.neurons[1].output = b;
net.pulse();
return net.outputLayer.neurons[0].output;
}
The problem is that all results that I get is close to 0.5 and pretty random, no matter what arguments I use. For example:
UseNet(0,0) => 0.5107701166677714
UseNet(0,1) => 0.4801498747476413
UseNet(1,0) => 0.5142463167153447
UseNet(1,1) => 0.4881829364416052
What can be wrong with my code?
This network is big enough for the XOR problem and I can't see any obvious mistakes, so I suspect it's getting stuck in a local minimum.
Try going through the training set 10,000 times instead of 1000; this gives it a better chance of breaking out of any minima and converging. You can also increase convergence a lot by upping the number of hidden neurons, tweaking η (the learning rate) or adding momentum. To implement the latter, try using this as your training function:
this.train = function(learningRate) {
var momentum = 0 /* Some value, probably fairly small. */;
self.neurons.forEach(function(neuron) {
neuron.bias.weight += neuron.bias.delta * learningRate;
neuron.bias.delta = 0;
neuron.input.forEach(function(input) {
input.factor.weight += (input.factor.delta * learningRate) + (input.factor.weight * momentum);
input.factor.delta = 0;
})
})
}
I've had good results changing the learning rate to 1.5 (which is pretty high) and momentum to 0.000001 (which is pretty small).
(Incidentally, have you tried running the .NET implementation with a few different seeds? It can take quite a while to converge too!)
This system uses fuzzy logic. As it says in the article don't use integers instead use "close" real numbers as the article suggests -- try
UseNet(0.1,0.1) =>
UseNet(0.1,0.9) =>
UseNet(0.9,0.1) =>
UseNet(0.9,0.9) =>
For the results anything above 0.5 is a 1 and below is 0
Hmmmm
Try instead of:
var testInputs = [[0,0], [0,1], [1,0], [1,1]];
var testOutputs = [[1],[0],[0],[1]];
This:
var testInputs = [[0.05,0.05], [0.05,0.95], [0.95,0.05], [0.95,0.95]];
var testOutputs = [[1],[0],[0],[1]];
or
var testInputs = [[0,0], [0,1], [1,0], [1,1]];
var testOutputs = [[0.95],[0.05],[0.05],[0.95]];

Categories

Resources