Creating a tic tac toe game. I am trying to read columns and rows of the tic tac toe to check who won. I am using $scope.checkResults() to grab the columns and rows and pass them into the allTheSame function to check if the values are equal which is not working.
Here is the codepen link. http://codepen.io/theMugician/pen/ojJrRp
var app = angular.module("ticTacToe", []);
app.controller("MainCtrl", function($scope){
var cell = $(".square");
$scope.player = "";
$scope.AI = "";
// changed special chars to X and O as the if statement failed.
var cross = "✖";
var circle = "◯";
/*** Choose a shape ***/
$scope.choosePlayer = function(e) {
$scope.player = $(e.currentTarget).text();
$('.choose').css('top', '-2000px');
$('#wrapper').css('top', '-600px');
$('#wrapper').css('opacity', '1');
//these if statements failed before (AI was always empty)
if($scope.player === cross){
$scope.AI = circle;
}else if($scope.player === circle){
$scope.AI = cross;
}
}
/*** Shape Cells ***/
$scope.cells = [ { value: '' }, { value: '' }, { value: '' },
{ value: '' }, { value: '' }, { value: '' } ,
{ value: '' }, { value: '' }, { value: '' }
];
// made a ref to scope cells
$scope.emptyCells = $scope.cells;
/*** Make a move ***/
$scope.move = function(cell){
cell.value = $scope.player;
var round = 0;
/*** AI makes a move ***/
while(round < 1){
// filtered to get only available cells (for performance)
$scope.emptyCells = $scope.cells.filter(function(cell){
return cell.value === '';
});
// got random cell according to empty cells
var randomCell = $scope.emptyCells[Math.floor((Math.random()*($scope.emptyCells.length-1))+1)];
if(randomCell.value === "" ){
randomCell.value = $scope.AI;
round = 1;
}else{
round = 0;
}
}
$scope.checkResults();
};
//checks if values are the same
function allthesame(arr){
var L= arr.length-1;
while(L){
if(arr[L--]!==arr[L]) return false;
}
alert(arr[L].value + "is the winner");
}
//checks Columns and rows
$scope.checkResults = function(){
var allCells = $scope.cells;
// check rows
var cellRows = [];
while(allCells > 0){
cellRows.push(allCells.splice(0,3));
}
for(var i = 0; i < cellRows.length; i++){
allTheSame(cellRows[i]);
}
// check columns
var cellCols = [];
while(allCells > 0){
cellCols.push(allCells.splice(0));
cellCols.push(allCells.splice(3));
cellCols.push(allCells.splice(6));
}
while(cellCols > 0){
cellCols.push(cellCols.splice(0,3));
}
for(var i = 0; i < cellCols.length; i++){
allTheSame(cellCols[i]);
}
}
$scope.reset = function(){
$scope.cells = [ { value: '' }, { value: '' }, { value: '' },
{ value: '' }, { value: '' }, { value: '' } ,
{ value: '' }, { value: '' }, { value: '' }
];
}
});
So you have an array of 9 values and need to compare to 8 possible winning arrangements, 3 vertical, 3 horizontal and 2 diagonal.. an array iteration against the follow list of "winning" combinations might be what you want to do.
{0,1,2}
{3,4,5}
{6,7,8}
{0,3,6}
{1,4,7}
{2,5,8}
{0,4,8}
{2,4,6}
I was just on my way out the door I can post some code later but I think saving this list as a comparison item for your cells might be the easier method.
Here is a reference article I grabbed quickly: this should be easy to convert to javascript: http://www.codeproject.com/Articles/2400/Tic-Tac-Toe-in-C
Using Frodo's advice I came up with a solution which is a lot simpler than what I was doing before.
I stored all winning combinations into an array. Then I checked each winning combo to see if each cell's value in that combo matched. Simple as that.
var winningNums = [
[0,1,2],
[3,4,5],
[6,7,8],
[0,3,6],
[1,4,7],
[2,5,8],
[0,4,8],
[2,4,6]
];
//checks if values are the same
$scope.checkResults = function(){
var allCells = $scope.cells;
for(var i = 0; i < winningNums.length; i++){
var a = winningNums[i][0],b=winningNums[i][1],c=winningNums[i][2];
var cell1 = allCells[a].value, cell2 = allCells[b].value, cell3 = allCells[c].value;
if(cell1 == "" || cell2 == "" || cell3 == "" ){
break;
}
if(cell1 === cell2 && cell2 === cell3 ){
var winnerDiv = "<div><h1>" + cell1 + " is the winner</h1></div>";
$(
"#wrapper").append(winnerDiv);
}
}
}
This Answer Does not Use jQuery
Using an array similar to what's shown in the above 👆🏾answer, we can just use a few different array methods:
const WINNING_INDICES = [
[0, 1, 2],
[0, 3, 6],
[0, 4, 8],
[1, 4, 7],
[2, 5, 8],
[2, 4, 6],
[3, 4, 5],
[6, 7, 8],
];
function check4Winner(board, letter) {
return WINNING_INDICES.some(winningIndices =>
winningIndices.every(winningIndex => board[winningIndex] === letter),
);
}
console.log(check4Winner(['X', 'O', 'O', 'X', 'O', 'X', 'X', '', '']), "X");
Here, I have passed in a hard-coded board that's a flat array. It represents the nine squares on a Tic-Tac-Toe board.
Summarily, we want to see if there is at least one instance (some) where all the indices of a given element (winningIndices) of WINNING_INDICES, when used with board, match the given letter ("X").
This is just the function of my code to determine who won in the game. I also use the winning combination numbers in my code.
var i = -1;
var WinnerChecker = setInterval(() =>{
var p1 = 0, p2 = 0;
i = i<TileCombos.length ? ++i : 0
for(let j = 0; j<3; j++){
if(player[TileCombos[i][j]].innerHTML == chk[0].innerHTML){
p1 += 1;
}
if(player[TileCombos[i][j]].innerHTML == chk[1].innerHTML){
p2 += 1;
}
}
if(p1 == 3){
console.log('PLAYER 1 WON');
}
if(p2 == 3){
console.log('PLAYER 2 WON');
}
})
Related
If I populate an array of pairs (inside a get json) like so:
var arrItems = [];
for (let y=0; y<50 ;y++){
var titl = data.item[y].name;
var img = data.item[y].image[0].url;
arrItems.push({t:titl,i:img});
}
How can I then filter it to leave only 3 pairs where value is the same?
Example:
arrItems = [
{t:one,i:square.jpg},
{t:two,i:square.jpg},
{t:three,i:square.jpg},
{t:four,i:square.jpg},
{t:five,i:triangle.jpg}
];
Becomes
arrItems = [
{t:one,i:square.jpg},
{t:two,i:square.jpg},
{t:three,i:square.jpg},
{t:five,i:triangle.jpg}
];
Both JavaScript or jQuery are OK.
You could take a hash table and count the occurences of the wanted property and filter with a max value.
var items = [{ t: 'one', i: 'square.jpg' }, { t: 'two', i: 'square.jpg' }, { t: 'three', i: 'square.jpg' }, { t: 'four', i: 'square.jpg' }, { t: 'five', i: 'triangle.jpg' }],
count = {},
result = items.filter(({ i }) => {
count[i] = (count[i] || 0) + 1;
return count[i] <= 3;
});
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
There might be some more efficient ways to write it, but I would think the easiest to understand is the straightforward iteration with counting:
var counts = {};
for (var i = 0; i < arrItems.length; i++) {
var item = arrItems[i];
var count = counts[item.i] || 0;
if (count >= 3) {
arrItems.splice(i, 1);
i--;
} else {
counts[item.i] = ++count;
}
}
You can use reduce function
var arrItems = [
{t:'one',i:'square.jpg'},
{t:'two',i:'square.jpg'},
{t:'three',i:'square.jpg'},
{t:'four',i:'square.jpg'},
{t:'five',i:'triangle.jpg'}
];
var output = arrItems.reduce((acc, {t,i})=>{
acc['mem'][i] = (acc['mem'][i] || 0) + 1;
acc['mem'][i] <= 3 ? acc['output'].push({t, i}) : '';
return acc;
}, {'mem':{}, 'output':[]});
console.log(output);
I'm take a 2d array that looks something like [[player1, 10], [player2, 8]] but with around 12 players. I'm satisfied enough with the sorting I have hear, except with this method teamA always gets the "first pick" of the better player. I am struggling to figure out a way to give teamB the better player every other time. below is the code as is that works "well enough".
data = [["player1", 10]. ["player2", 8], ["player3", 7], ["player4", 9]];
var teamA = [];
var teamB = [];
var remaining = [];
for (item in data) {
remaining.push(data[item].slice());
}
for (i in data) {
var max = 0;
var selection = [,];
var index = -1;
for (k in remaining) {
if (remaining[k][1] > max) {
selection = remaining[k];
max = remaining[k][1];
index = k;
}
}
remaining.splice(index, 1);
if (i % 2 == 0) {
teamA.push(selection);
} else {
teamB.push(selection);
}
}
This results in teamA: [["player1, 10],["player2", 8]] and teamB: [["player4", 9],["player3", 7]]
What I would prefer is player2 and player3 switch teams. Here's what I tried.
// inside my first for loop
if (3 % i == 0) {
i++;
} else if (4 % i == 0) {
i--;
}
In my brain, this should have worked just fine but wow did it not! I ended up with like 9 players on teamB and 3 on teamA. I fiddled with different variations of this approach with no luck.
Any pointers?
EDIT: For clarification, it can be assumed the data set will be unsorted coming in, and that the length of the data set will always be even. There won't be a team with more players than the other.
I have edited your code so that it will choose first for teamA, then it will choose 2 for teamB, then 2 for teamA and so on.
var data = [
["player1", 10],
["player2", 8],
["player3", 7],
["player4", 9],
["player5", 5],
["player6", 6]
];
var teamA = [];
var teamB = [];
var remaining = [];
for (item in data) {
remaining.push(data[item].slice());
}
var turnA = false;
var counterA = 0,
counterB = 0;
for (i in data) {
var max = 0;
var selection = [, ];
var index = -1;
for (k in remaining) {
if (remaining[k][1] > max) {
selection = remaining[k];
max = remaining[k][1];
index = k;
}
}
remaining.splice(index, 1);
// add first player to teamA and continue
if (i == 0) {
teamA.push(selection);
continue;
}
if (turnA) {
teamA.push(selection);
counterA++;
} else {
teamB.push(selection);
counterB++;
}
if (turnA && counterA == 2) {
counterA = 0;
turnA = false;
} else if (!turnA && counterB == 2) {
counterB = 0;
turnA = true;
}
}
So, the strategy you want to take is the partition problem, as described here: https://en.wikipedia.org/wiki/Partition_problem
You basically want to take the sum of the two teams, based on their players' scores, compare the sum, and assign the current player to the team with the lesser score.
First, we sort the player data.
At the first pass, we randomly select a team to assign the best player. As iterate through the rest of the player pool, we follow the algorithm as described in the partition problem.
I wrote something out really quick as an example of the partition problem in action: https://codepen.io/thenormalsquid/pen/Lymjbb?editors=0001
// stack overflow answer
var data = [["player1", 10], ["player2", 8], ["player3", 7], ["player4", 9]];
var teamA = [];
var teamB = [];
var selectionProbability = 0.5;
var remaining = [];
data.sort(function(playerA, playerB){
if(playerA[1] < playerB[1]) {
return 1;
}
if(playerA[1] > playerB[1]) {
return -1;
}
return 0;
});
var sum = function(team) {
if (team.length === 0) {
return 0;
}
var i,
s = 0;
for(i = 0; i < team.length; i++) {
s += team[i][1];
}
return s;
};
var chooseTeam = function() {
if(Math.random() < selectionProbability) {
return 'teamA';
}
return 'teamB';
};
function assignTeams() {
var i;
for(i = 0; i < data.length; i++) {
var sumA = sum(teamA),
sumB = sum(teamB);
// first pass, we'll have a 50/50 chance
// of placing the best player in either team A or team B
if (i === 0) {
var chosenTeam = chooseTeam();
if (chosenTeam === 'teamA') {
teamA.push(data[i]);
} else {
teamB.push(data[i]);
}
} else if (sumA < sumB) {
teamA.push(data[i]);
} else {
teamB.push(data[i]);
}
}
}
function addPlayerToHtml(player, teamId) {
var li = document.createElement('li'),
text = document.createTextNode(player[0] + ' ' + player[1]);
li.appendChild(text);
document.getElementById(teamId).appendChild(li);
}
var button = document.getElementById('assignPlayers');
button.addEventListener('click', function(){
assignTeams();
teamA.forEach(function(player){
addPlayerToHtml(player, 'teamA');
});
teamB.forEach(function(player){
addPlayerToHtml(player, 'teamB');
});
});
I have an array store Task information. Each task has also an array of taskId that it is depending on.
Input
let inputArr = [
{
id: 1,
dependOnTasks: [2, 3]
},
{
id: 2,
dependOnTasks: [3]
},
{
id: 3,
dependOnTasks: []
},
{
id: 4,
dependOnTasks: [5]
},
{
id: 5,
dependOnTasks: []
},
{
id: 6,
dependOnTasks: [5]
}
]
The expected output is grouping all the depending task into one array for displaying into the UI.
Output
[
[
{
id: 1,
dependOnTasks: [2, 3]
},
{
id: 2,
dependOnTasks: [3]
},
{
id: 3,
dependOnTasks: []
}
],
[
{
id: 4,
dependOnTasks: [5]
},
{
id: 5,
dependOnTasks: []
},
{
id: 6,
dependOnTasks: [5]
}
]
]
I have made a function to do it but seem I was thinking wrong by hard-coded it. Hope someone can help me archive it right way using pure JavaScript/TypeScript or Underscore since we have already used in the project.
Noted: TaskId will be random string like "5878465507b36e1f9c4c46fe"
// will contain the groups (results).
var result = [];
// will serve as a holder of the already treated indexes of inputArr.
var indexCache = [];
// insert obj into a group, insert its dependencies and the object that depend on it as well.
function insertWithDependencies(obj, group){
// insert this obj into this group
group.push(obj);
// First: look for the objects it depends on
obj.dependOnTasks.forEach(function(id){
for(var i = 0; i < inputArr.length; i++){
// if the object in this index is already treated, then ignore it
if(indexCache.indexOf(i) != -1) continue;
// if this object is a dependency of obj then insert it with its own dependencies.
if(inputArr[i].id == id){
var o = inputArr[i];
indexCache.push(i); // cache this i as well
insertWithDependencies(o, group);
}
}
});
// Then: look for the objects that depends on it
for(var i = 0; i < inputArr.length; i++){
// if the object in this index is already treated, then ignore it
if(indexCache.indexOf(i) != -1) continue;
// if this object depends on obj then insert it with ...
if(inputArr[i].dependOnTasks.indexOf(obj.id) != -1){
var o = inputArr[i];
indexCache.push(i); // cache i
insertWithDependencies(o, group);
}
}
};
// while there is element in the inputArr that haven't been treated yet
while(inputArr.length != indexCache.length){
// the group that will hold the depending tasks all together
var group = [];
// look for the first untreated object in inputArr
var i;
for(i = 0; i < inputArr.length; i++)
if(indexCache.indexOf(i) == -1)
break;
var obj = inputArr[i];
// cache its index
indexCache.push(i)
// insert it along its dependencies
insertWithDependencies(obj, group);
// push the group into the result array
result.push(group);
}
ANOTHER WAY:
Here is an optimised way to do it, but the data inside inputArr will be lost afterwards. It won't use the indexCache to see if an index is already treated or not but instead it will make all treated items null in inputArr. So if you don't care or won't use inputArr afterwards, use this instead:
var result = [];
function insertWithDependencies(obj, group){
group.push(obj);
obj.dependOnTasks.forEach(function(id){
for(var i = 0; i < inputArr.length; i++){
if(!inputArr[i]) continue;
if(inputArr[i].id == id){
var o = inputArr[i];
inputArr[i] = null;
insertWithDependencies(o, group);
}
}
});
for(var i = 0; i < inputArr.length; i++){
if(!inputArr[i]) continue;
if(inputArr[i].dependOnTasks.indexOf(obj.id) != -1){
var o = inputArr[i];
inputArr[i] = null;
insertWithDependencies(o, group);
}
}
};
function findNotNull(){
for(var i = 0; i < inputArr.length; i++)
if(inputArr[i]) return i;
return -1;
}
var index;
while((index = findNotNull()) != -1){
var group = [];
var obj = inputArr[index];
inputArr[index] = null;
insertWithDependencies(obj, group);
result.push(group);
}
console.log(result);
The solution is straightforward,
Initialize empty group list
For each task find if there is a group in the group list with id or id of any dependent tasks
If not add a new group with task id and dependent tasks
var input = [
{ id: 1, dependOnTasks: [2, 3] },
{ id: 2, dependOnTasks: [3] },
{ id: 3, dependOnTasks: [] },
{ id: 4, dependOnTasks: [5] },
{ id: 5, dependOnTasks: [] },
{ id: 6, dependOnTasks: [5] }
];
var groups = [];
for (var i = 0; i < input.length; i++){
var group = findGroup(groups,input[i]);
if (!group){
group = {ids : []};
group.ids.push(input[i].id);
groups.push(group);
}
if (group.ids.indexOf(input[i].id) === -1){
group.ids.push(input[i].id);
}
for (var j = 0; j < input[i].dependOnTasks.length; j++){
if (group.ids.indexOf(input[i].dependOnTasks[j]) === -1){
group.ids.push(input[i].dependOnTasks[j]);
}
}
}
document.write(groups[0].ids + '</br>');
document.write(groups[1].ids + '</br>');
function findGroup(groups,task){
for (var i = 0; i < groups.length; i++){
var group = groups[i];
if (group.ids.indexOf(task.id) !== -1){
return group;
}
for (var j = 0; j < task.dependOnTasks.length; j++){
if (group.ids.indexOf(task.dependOnTasks[j]) !== -1){
return group;
}
}
}
return null;
}
If you don't care about the order of the tasks in the same group. Using union and find to implement a disjoint set might be an option.
Util data structure:
function UnionFind(n) {
this.parent = [...Array(n+1).keys()]
}
UnionFind.prototype.find = function(x) {
if (this.parent[x] === x) {
return x
}
const ret = this.find(this.parent[x])
this.parent[x] = ret
return ret
}
UnionFind.prototype.union = function(x, y) {
let x_rep = this.find(x)
let y_rep = this.find(y)
if (x_rep !== y_rep) {
this.parent[x_rep] = y_rep
}
}
Dumb data source:
let inputArr = [
{
id: 1,
dependOnTasks: [2, 3]
},
{
id: 2,
dependOnTasks: [3]
},
{
id: 3,
dependOnTasks: []
},
{
id: 4,
dependOnTasks: [5]
},
{
id: 5,
dependOnTasks: []
},
{
id: 6,
dependOnTasks: [5]
}
]
Driver program:
let len = inputArr.length
let uf = new UnionFind(len)
// iterate through all tasks to group them
inputArr.forEach(entry => {
entry.dependOnTasks.forEach(depsId => {
uf.union(entry.id, depsId)
})
})
// reiterate to retrieve each task's group and group them using a hash table
let groups = {}
inputArr.forEach(entry => {
const groupId = uf.find(entry.id)
if (!groups.hasOwnProperty(groupId)) {
groups[groupId] = [entry]
return
}
groups[groupId].push(entry)
})
let result = Object.keys(groups).map(groupId => groups[groupId])
console.log(JSON.stringify(result, null, 2))
note: if id is random string in your case, simply change this.parent to hash map, and if you care about the order(as there're dependency trees), consider using topological sort instead.
You can try with my code
var inputArr = [
{
id: 1,
dependOnTasks: [2, 3]
},
{
id: 2,
dependOnTasks: [3]
},
{
id: 3,
dependOnTasks: []
},
{
id: 4,
dependOnTasks: [5]
},
{
id: 5,
dependOnTasks: []
},
{
id: 6,
dependOnTasks: [5]
}
]
// make matrix graph
var map = {};
for (var i = 0; i < inputArr.length; i++) {
var task = inputArr[i];
map[task.id] = map[task.id] || {};
for (var j = 0; j < task.dependOnTasks.length; j++) {
var dependId = task.dependOnTasks[j];
map[dependId] = map[dependId] || {};
map[task.id][dependId] = true;
map[dependId][task.id] = true;
}
}
var groupTasks = [];
for (var key in map) {
var group = groupTasks.filter(function(e) {
return e.indexOf(key) >= 0;
})[0]
if (!group) {
group = [key];
groupTasks.push(group);
}
for (var dependKey in map[key]) {
if (group.indexOf(dependKey) == -1) {
group.push(dependKey);
}
}
}
var result = groupTasks.map(function(group) {
var tasks = [];
group.forEach(function(id) {
var task = inputArr.filter(function(e) { return e.id == id })[0];
tasks.push(task);
});
return tasks;
})
console.log(JSON.stringify(result, null, 4));
I have a function (getCoeff()) which returns one-dimensional arrays. I try to use it to fill a two-dimensional array:
//set up an 3x3 array for A
A = new Array(3);
for (var i=0; i<3; i++) {
A[i] = new Array(3);
}
//fill it per row using getCoeff()
for (var i=0; i<3; i++) {
A[i] = getCoeff(i+1);
}
console.log(A);
console.log(getCoeff(1));
console.log(getCoeff(2));
console.log(getCoeff(3));
but I only get the first row filled and the other two remain empty:
[ [ -3, 2, -1 ], [ , , ], [ , , ] ]
[ -3, 2, -1 ]
[ 2, -3, 2 ]
[ 1, -1, 3 ]
As you can see the function returns the rows correctly but for some reason It doesnt work inside the loop.
On the other hand if I try something like this:
for (var i=0; i<3; i++) {
A[i] = [1,2,3];
}
console.log(A);
it works fine!
[ [ 1, 2, 3 ], [ 1, 2, 3 ], [ 1, 2, 3 ] ]
What's wrong with my code?!
Update:
My original full code before the edits:
var fs = require('fs');
var input = "LPinput.txt";
var c = new Array();
var A = new Array();
var b = new Array();
var Eqin = new Array();
var MinMax;
open(input);
console.log(c);
console.log(A);
console.log(b);
console.log(Eqin);
console.log(MinMax);
function open(filename) {
if (fs.existsSync(filename)) {
var data = fs.readFileSync(filename).toString().split("\n");
analyse(data);
} else {
console.log("ERROR: File doesnt exist!");
}
}
function analyse(data) {
//clean up whitespaces
for (i in data) {
data[i] = data[i].replace(/\s/g, '');
}
//check LP type & clean up
if (data[0].substring(0,3) == "max") {
MinMax = 1;
data[0] = data[0].replace("max","");
} else if (data[0].substring(0,3) == "min") {
MinMax = -1;
data[0] = data[0].replace("min","");
} else {
console.log("ERROR: Invalid format!");
return;
}
//check constraints format & clean up
if ( data[1].substring(0,4) != "s.t.") {
console.log("ERROR: Invalid format!");
return;
} else {
data[1] = data[1].replace("s.t.","");
}
//get variables
var variables = data[data.length-1].split(",");
var last = variables[variables.length-1];
variables[variables.length-1] = last.substring(0,last.indexOf(">"));
//get number of constraints
var constraints = data.length-2;
c = getCoeff(0);
//===============================
//I JUST NEED TO POPULATE A TO FINISH THIS
for (var i=0; i<constraints; i++) {
A[i] = getCoeff(i+1);
}
//===============================
for (var i=1; i<data.length-1; i++) {
var end = data[i].length;
var start = end;
while (data[i].charAt(start) != "=") {
start = start - 1;
}
b[i-1] = parseInt(data[i].substring(start+1,end));
if (data[i].charAt(start-1) == "<") {
Eqin[i-1]=-1;
} else if (data[i].charAt(start-1) == ">") {
Eqin[i-1]=1;
} else {
Eqin[i-1]=0;
}
}
function getCoeff(row) {
var coeff = new Array();
for (i in variables) {
var pos = data[row].indexOf(variables[i]);
if ((data[row].charAt(pos-1) == "+") || (pos-1 < 0)) {
coeff[i]=1;
} else if (data[row].charAt(pos-1) == "-") {
coeff[i]=-1;
} else if (data[row].charAt(pos-1) == "*") {
var end = pos-1;
var start = end;
while ( (start > -1) && (data[row].charAt(start) != "+") && (data[row].charAt(start) != "-") ) {
start = start - 1;
}
coeff[i] = parseInt((data[row].substring(start,end)));
}
}
return coeff;
}
}
LPinput.txt:
max 2*x1+x2-4*x3-15
s.t.-3*x1+2*x2-x3>=5
2*x1-3*x2+2*x3<=9
x1-x2+3*x3<=5
x1,x2,x3>=0
Update #2:
Console output:
[ 2, 1, -4 ]
[ [ -3, 2, -1 ] ]
[ 5, 9, 5 ]
[ 1, -1, -1 ]
1
It should be:
[ 2, 1, -4 ]
[ [ -3, 2, -1 ],[ 2, -3, 2 ],[ 1, -1, 3 ]]
[ 5, 9, 5 ]
[ 1, -1, -1 ]
1
Here is the real problem:
you are using an i variable in your outer scope.
for (var i=0; i<constraints; i++) {
A[i] = getCoeff(i+1);
}
When you go inside the getCoef you have this for loop
for (i in variables) {
and since you have not declared the i here, it uses the same i declared in the outer scope. After the first run of the for loop (which fills the first row of A), i is changed to "0" as opposed to the numeric value 0. Therefore the condition of the for loop is no longer valid and it exits the loop.
There you go!
In my case, I got last 2 populated. Anyways, if you run loop from 0, you better write
A[i] = getCoeff(i+1);
OR
you run that loop starting from 1 to less than equals 3.
In your code:
A = new Array(3);
you should declare varaibles, and initialising an array with a length is usually unnecessary. Also, variables starting with a capital letter are, by convention, reserved for construtors (though some use variable names in all capitals to represent constants).
Consider using an array initialiser:
var a = [];
for (var i=0; i<3; i++) {
Initialising arrays in the following loop is a bit useless as you replace them in the next loop:
a[i] = [];
}
In the next loop, i is needlessly declared again (there is no block scope in javascript). It is common to use a different variable in these cases (though re-using i has no ill effects, it's just not liked):
for (var j=0; j<3; j++) {
a[j] = getCoeff(j + 1);
}
So creating a getCoeff function that just returns an array of the value passed to it (purely for testing):
function getCoeff(v){
return [v,v,v];
}
"works":
console.log(a.join(' - ')); // 1,1,1 - 2,2,2 - 3,3,3
Ok, consider this:
I have a big array containing arrays, -1, a and b.
The -1 means the field is empty:
var board = [
[-1,-1, a],
[-1,-1, b],
[ b,-1, a]
]
Now i want to check smaller arrays agains this:
var solutions = [
[
[1, 1, 1]
],
[
[1],
[1],
[1]
],
[
[1],
[0,1],
[0,0,1]
],
[
[0,0,1],
[0,1],
[1]
]
]
To see if one existing value from board match the pattern in solutions.
Does a match any of pattern?
Does b match any of the pattern?
Can any of you see a better way than making a crazy nested loop:
var q,w,e,r,t,y;
q=w=e=r=t=y=0;
for( ; q < 3; q++ ) {
for( ; w < 3; w++ ) {
for( ; e < SOLUTIONS.length; e++ ) {
.... and so on...
}
}
}
In this example I have used tic-tac-toe.
But i could be anything.
What you can do is to compile the patterns for speed. The same way as same languages allow regular expressions to be compiled for speed.
function compile(pattern) {
var code = "matcher = function(a) { return "
var first = null
for (var n = 0; n < pattern.length; n++) {
for (var m = 0; m < pattern[n].length; m++) {
if (pattern[n][m] == 0) continue
var nm = "["+n+"]["+m+"]"
if (first == null) {
code += "a" + nm + " != -1";
first = " && a" + nm + " == "
}
code += first + "a" + nm
}
}
code += "; }";
eval(code);
return matcher
}
So what is this doing?
For example
compile([[1],[0,1],[0,0,1]]).toString()
will create the following function
"function (a) { return a[0][0] != -1 && a[0][0] == a[0][0] && a[0][0] == a[1][1] && a[0][0] == a[2][2]; }"
So how do you use it?
To match positions on your board use it as follows
var patterns = solutions.collect(function(each) { return compile(each); })
var matches = patterns.any(function(each) { return each(board); })
NB, the very last snipped above assumes you're using one of the many popular higher-order programming libraries, as for example lodash, to provide collect and any functions on the array prototype, if not use plain old for loops instead.
Very interesting question. +1 :) Here is my take on this.
Check my fiddle http://jsfiddle.net/BuddhiP/J9bLC/ for full solution. I'll try to explain the main points in here.
I start with a board like this. I've used 0 instead of -1 because its easier.
var a = 'a', b = 'b';
var board = [
[a, 0, a],
[b, b, b],
[a, 0, a]
];
My Strategy is simple.
Check if any of the rows has the same player (a or b), if so we have a winner.
Else, Check if any of the columns has the same player
Else, Check if diagonals has a player
Those are the three winning cases.
First I created a function which can take set of rows (Ex: [a,0,b]), and check if entire row contains the same value, and if that value is not zero (or -1 in your case).
checkForWinner = function () {
lines = Array.prototype.slice.call(arguments);
// Find compact all rows to unique values.
var x = _.map(lines, function (l) {
return _.uniq(l);
});
// Find the rows where all threee fields contained the same value.
var y = _.filter(x, function (cl) {
return (cl.length == 1 && cl[0] !== 0);
});
var w = (y.length > 0) ? y[0] : null;
return w;
};
Here I take unique values in a row, and if I can find only one unique value which is not ZERO, the he is the winner.
If there is no winner in the rows, I then check for columns. In order to reuse my code, I use _.zip() method to transform columns into rows and then use the same function above to check if we have a winner.
var board2 = _.zip.apply(this, board);
winner = checkForWinner.apply(this, board2);
If I still don't find a winner, time to check the diagonals. I've written this function to extract two diagonals from the board as two rows, and use the same checkForWinner function to see if diagonals are dominated by any of the players.
extractDiagonals = function (b) {
var d1 = _.map(b, function (line, index) {
return line[index];
});
var d2 = _.map(b, function (line, index) {
return line[line.length - index - 1];
});
return [d1, d2];
};
Finally this is where I actually check the board for a winner:
// Check rows
winner = checkForWinner.apply(this, board);
if (!winner) {
var board2 = _.zip.apply(this, board);
// Check columns, now in rows
winner = checkForWinner.apply(this, board2);
if (!winner) {
var diags = extractDiagonals(board);
// Check for the diagonals now in two rows.
winner = checkForWinner.apply(this, diags);
}
}
If any of you wonder why I use apply() method instead of directly calling the function, reason is apply() allows you to pass an array elements as a list of arguments to a function.
I believe this should work for 4x4 or higher matrics as well, although I did not test them.
I had very little time to test the solution, so please let me know if you find any errors.
No, you only do need three nested loops: One to loop over your patterns, and two to loop your two-dimensional playing field:
function checkPatterns(patterns, player, field) {
pattern: for (var p=0; p<patterns.length; p++) {
for (var i=0; i<patterns[p].length; i++)
for (var j=0; j<patterns[p][i].length; j++)
if (patterns[p][i][j] && player !== field[i][j])
continue pattern;
// else we've matched all
return p;
}
// else none was found
return -1;
}
function getSolution(player)
return SOLUTIONS[checkPatterns(SOLUTIONS, player, currentBOARD)] || null;
}
OK, you might need a fourth loop for the players (players.any(getSolution)), but that doesn't make it any crazier and might be inlined for only two players as well.
However, it might be easier than formulating "pattern arrays" to construct algorithms for the patterns themselves:
function hasWon(player, field) {
vert: for (var i=0; i<field.length; i++) {
for (var j=0; j<field[i].length; j++)
if (field[i][j] !== player)
continue vert;
return "vertical";
}
hor: for (var j=0; j<field[0].length; j++) {
for (var i=0; i<field.length; i++)
if (field[i][j] !== player)
continue hor;
return "horizontal";
}
for (var i=0, l=true, r=true, l=field.length; i<l; i++) {
l == l && field[i][i] === player;
r == r && field[l-i-1][l-i-1] === player;
}
if (l || r)
return "diagonal";
return null;
}
You can have your board to be a string:
var board =
"-1,-1,a,
-1,-1,b,
b,-1,a"
and your solutions can be an array of strings (similar to the board)
var solutions = [
"1,1,1,
0,0,0,
0,0,0"
,
"1,0,0,
0,1,0,
0,0,1"
]
then for comparison, replace the -1 and b with 0s and a with 1s
then simply compare the strings
this is much faster than having 10 different loop inside another loop
You will always need loops to go trough it all. You can just make it easier to read and more flexible. The code below will work for any number of rows/cols larger then 1 and with a simple adjustment also for more then 2 players.
var board1 = [
[-1,-1, 'a'],
[-1,-1, 'b'],
['b',-1, 'a']
];
var board2 = [
['a','a', 'a'],
[-1,-1, 'b'],
['b',-1, 'a']
];
var board3 = [
[-1,'b', 'a'],
[-1,'b', 'b'],
['b','b', 'a']
];
var board4 = [
['a',-1, 'a'],
[-1,'a', 'b'],
['b',-1, 'a']
];
var solutions = [
[
[1, 1, 1]
],
[
[1],
[1],
[1]
],
[
[1],
[0,1],
[0,0,1]
],
[
[0,0,1],
[0,1],
[1]
]
];
function checkForWinner(playfield) {
var sl = solutions.length; //solutions
var bl = playfield.length; //board length
var bw = playfield[0].length; //board width
while(sl--) {
//foreach solution
var l = solutions[sl].length;
if (l==1) {
//horizontal
//loop trough board length to find a match
var tl = bl;
while(tl--) {
var pat = playfield[tl].join('')
var r = checkRow(pat)
if (r!==false)
return r;
}
} else {
//vertical or diagonal
var l1 = solutions[sl][0].length;
var l2 = solutions[sl][1].length;
if (l1==l2) {
//vertical
var tw = bw;
while (tw--) {
//loop for each column
var pat = "";
var tl = l;
while(tl--) {
//loop for vertical
pat += playfield[tl][tw];
}
var r = checkRow(pat)
if (r!==false)
return r;
}
} else {
//diagonal
var pat = "";
while(l--) {
//loop for vertical
var tw = solutions[sl][l].length;
while (tw--) {
//loop for horizontal
if (solutions[sl][l][tw]!=0)
pat += playfield[l][tw];
}
}
var r = checkRow(pat)
if (r!==false)
return r;
}
}
}
return 'no winner';
}
function checkRow(pat) {
if (!(pat.indexOf('a')>=0 || pat.indexOf('-1')>=0)) {
//only b on row. player B won
return 'B';
}
if (!(pat.indexOf('b')>=0 || pat.indexOf('-1')>=0)) {
//only a on row. player A won
return 'A';
}
return false;
}
console.log(checkForWinner(board1));
console.log(checkForWinner(board2));
console.log(checkForWinner(board3));
console.log(checkForWinner(board4));