I'm working on a tic tac toe game, and though the game is functional, when one player wins, the debugger pops up if they win using certain spaces. I get a type error saying that 'O' can't be set of undefined. Also in my console a negative number will print out for the elementIndex when a player wins by clicking only on certain spaces.
It appears to be coming from this line in my game.win function:
game.winCombos[elementIndex][unitIndex] = game.currentPlayerTurn;
var gameboard = {
initialize: function() {
for (var x = 0; x < 3; x++) {
for (var y = 0; y < 3; y++) {
var unit = $("<div class='unit'></div>");
unit.appendTo('#gameboard');
}
}
console.log(100);
gameboard.addId();
},
addId: function() {
var id = 1
$('.unit').each(function() {
$(this).attr('id', id);
id++;
});
}
};
var players = {
firstPlayer: {
token: 'X'
},
secondPlayer: {
token: 'O'
}
};
var game = {
newGame: function() {
game.winCombos = game.clone();
game.currentPlayerTurn = players.firstPlayer.token;
gameboard.initialize();
game.displayToken();
},
currentPlayerTurn: players.firstPlayer.token,
displayToken: function() {
$('.unit').click(function() {
if (game.currentPlayerTurn === 'X' && !$(this).hasClass('selected')) {
$(this).addClass('selected').removeClass('unit').text("X");
game.currentPlayerTurn = players.secondPlayer.token;
} else if (game.currentPlayerTurn === 'O' && !$(this).hasClass('selected')) {
$(this).addClass('selected').removeClass('unit').text("O");
game.currentPlayerTurn = players.firstPlayer.token;
}
game.win($(this));
})
},
win: function(div) {
game.winCombos.forEach(function(element) {
element.forEach(function(unitIndex) {
if (unitIndex.toString() === div.attr('id').toString()) {
var elementIndex = game.winCombos.indexOf(element);
var unitIndex = element.indexOf(unitIndex);
game.winCombos[elementIndex][unitIndex] = game.currentPlayerTurn;
}
})
var counter = 0
for (var i = 0; i < element.length; i++) {
if (element[i] === game.currentPlayerTurn) {
counter++;
}
if (counter === 3) {
counter = 0;
game.gameOver(true);
}
}
})
},
gameOver: function(bool) {
if (bool === undefined) {
bool = false;
}
$('.unit').remove();
$('.selected').remove();
game.newGame();
},
winCombos: [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9],
[1, 4, 7],
[2, 5, 8],
[3, 6, 9],
[1, 5, 9],
[3, 5, 7]
],
clone: function() {
return [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9],
[1, 4, 7],
[2, 5, 8],
[3, 6, 9],
[1, 5, 9],
[3, 5, 7]
]
}
Related
I have an array with duplicate values
let ary = [5, 1, 3, 5, 7, 8, 9, 9, 2, 1, 6, 4, 3];
I want to set the repeated values to 0:
[0, 0, 0, 0, 7, 8, 0, 0, 2, 0, 6, 4, 0]
can find out the repeated value, but I want to change the repeated value to 0, is there any better way?
let ary = [5, 1, 3, 5, 7, 8, 9, 9, 2, 1, 6, 4, 3];
Array.prototype.duplicate = function () {
let tmp = [];
this.concat().sort().sort(function (a, b) {
if (a == b && tmp.indexOf(a) === -1) tmp.push(a);
});
return tmp;
}
console.log(ary.duplicate()); // [ 1, 3, 5, 9 ]
// ? ary = [0, 0, 0, 0, 7, 8, 0, 0, 2, 0, 6, 4, 0];
You could use indexOf() and lastIndexOf() method to solve your problem.
const array = [5, 1, 3, 5, 7, 8, 9, 9, 2, 1, 6, 4, 3];
const ret = array.map((x) =>
array.indexOf(x) !== array.lastIndexOf(x) ? 0 : x
);
console.log(ret);
const ary = [5, 1, 3, 5, 7, 8, 9, 9, 2, 1, 6, 4, 3];
// get set of duplicates
let duplicates = ary.filter((elem, index, arr) => arr.indexOf(elem) !== index)
duplicates = new Set(duplicates);
// set duplicate elements to 0
const res = ary.map(e => duplicates.has(e) ? 0 : e);
console.log(...res);
First, count values and store them in an object. Then loop over the array and check from that stored object whether the count of specific value is greater than 1 or not, if greater than 1, set that to 0. Here is the working example:
let ary = [5, 1, 3, 5, 7, 8, 9, 9, 2, 1, 6, 4, 3];
let countValue = {}, len = ary.length;
for (i = 0; i < len; i++) {
if (countValue[ary[i]]) {
countValue[ary[i]] += 1;
} else {
countValue[ary[i]] = 1;
}
}
for (i = 0; i < len; i++) {
if (countValue[ary[i]] > 1) {
ary[i] = 0;
}
}
console.log(...ary);
Probably this is the quickest algorithm, though it will alter your original array.
const array = [5, 1, 3, 5, 7, 8, 9, 9, 2, 1, 6, 4, 3];
const map = {};
for (let ind = 0; ind < array.length; ind++) {
const e = array[ind];
if (map[e] === undefined) {
map[e] = ind;
} else {
array[map[e]] = 0;
array[ind] = 0;
}
}
console.log(...array);
var nTimes = function (n, func) {
var numberOfTimesRan = 0;
var resultOfRunningFunc;
return function() {
if (numberOfTimesRan < n) {
resultOfRunningFunc = func.apply(null, arguments);
numberOfTimesRan++;
return resultOfRunningFunc;
}
return resultOfRunningFunc;
}
return resultOfRunningFunc;
}
var column = function(board) {
var counter = 0;
var checker = {};
for (var i = 0; i < board.length; i++) {
var key = board[i][counter];
checker[key] = '';
}
counter++;
if (Object.keys(checker).length !== 9) {
return "Try again!";
}
}
nTimes(9, column);
var result2 = column([[5, 3, 4, 6, 7, 8, 9, 1, 2],
[6, 7, 2, 1, 9, 0, 3, 4, 9],
[1, 0, 0, 3, 4, 2, 5, 6, 0],
[8, 5, 9, 7, 6, 1, 0, 2, 0],
[4, 2, 6, 8, 5, 3, 7, 9, 1],
[7, 1, 3, 9, 2, 4, 8, 5, 6],
[9, 0, 1, 5, 3, 7, 2, 1, 4],
[2, 8, 7, 4, 1, 9, 6, 3, 5],
[3, 0, 0, 4, 8, 1, 1, 7, 9]]) //, "Try again!")
console.log(result2)
Im new to JavaScript and im trying to practice closures. What I want is to run the column function 9 times. Currently in the nTimes function func is getting called on column and the right argument is getting passed in but nTimes is only getting ran once. Any advice on how i can fix it to get it to keep running?
You can use the function nTimes as used below to run a function multiple times.
function nTimes(count, func) {
for (x=0;count>=x;x++) {
func;
}
}
function testFunc(msg) {
console.log(msg)
}
nTimes(9, testFunc("Hello World!"));
This is a simpler version to the one you were trying to use. Your function did not work once you use return the code instantly exits out of the function, meaning the code below it fill not work.
function nTimes(count, func) {
for (x=0;count>=x;x++) {
func;
}
}
function testFunc(msg) {
console.log(msg)
}
nTimes(9, testFunc("Hello World!"));
I tried this code, but it logs "Hello world" only once for me, I changed it for this implemantaion and it works for me.
function nTimes(count, func) {
for (x=0;count>=x;x++) {
func();
}
}
function testFunc(msg) {
console.log(msg)
}
nTimes(9, testFunc.bind(null, "Hello World!"));
I have a multidimensional array. I want to group the values in this and know how many.
I've created a new array. I've looped a multidimensional array. If the current value does not exist in the new array, I add this value into the array. But I couldn't do it dynamically, they were all added to the bottom. I couldn't add it to "subCategories".
In this way I have a multidimensional array.
currentArray = [
[1, 2, 3, 5],
[1, 2, 3, 4],
[1, 2, 3, 4],
[1, 2, 3, 4],
[1, 2, 3, 4],
[1, 2, 3, 4],
[1, 2, 3, 4]
]
I used a loop like this.
newArray= [];
for (let i = 0; i < currentArray.length; i++) {
for (let k = 0; k < currentArray[i].length; k++) {
let obj = { id: currentArray[i][k], subCategories: [] };
let index = newCategories.findIndex(x => x.id === obj.id);
if (index === -1) {
if (k === 0) {
newCategories.push(obj);
}
} else {
newCategories[index].subCategories.push(obj);
}
}
}
I used a loop like this but I did not get a successful result. Logic error in the current code and I couldn't figure it out.
I want the same elements in the array to be added to the new array only once. And I want to get "count" in the last elements.
So the output I want to achieve is as follows.
{
"id": 1,
"subCategories": [
{
"id": 2,
"subCategories": [
{
"id": 3,
"subCategories": [
{
"id": 5,
"count": 1,
"subCategories": []
},
{
"id": 4,
"count": 6,
"subCategories": []
}
]
}
]
}
]
}
You could reduce the array by reduceing the inner array and look for the wanted id.
var array = [[1, 2, 3, 5], [1, 2, 3, 4], [1, 2, 3, 4], [1, 2, 3, 4], [1, 2, 3, 4], [1, 2, 3, 4], [1, 2, 3, 4]],
result = array
.reduce((r, a) => {
var o = a.reduce((p, id) => {
var temp = p.subCategories.find(q => q.id === id);
if (!temp) {
p.subCategories.push(temp = { id, subCategories: [] });
}
return temp;
}, r);
o.count = (o.count || 0) + 1;
return r;
}, { subCategories: [] })
.subCategories;
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
This is in the same style as you had, by using a starting object which matches the inner format and a search for the items for returning this object for next level.
var currentArray = [[1, 2, 3, 5], [1, 2, 3, 4], [1, 2, 3, 4], [1, 2, 3, 4], [1, 2, 3, 4], [1, 2, 3, 4], [1, 2, 3, 4]],
newArray = [],
temp,
item;
for (let i = 0; i < currentArray.length; i++) {
temp = { subCategories: newArray };
for (let k = 0; k < currentArray[i].length; k++) {
item = temp.subCategories.find(x => x.id === currentArray[i][k]);
if (!item) {
temp.subCategories.push(item = { id: currentArray[i][k], subCategories: [] });
}
temp = item;
}
temp.count = (item.count || 0) + 1;
}
console.log(newArray);
.as-console-wrapper { max-height: 100% !important; top: 0; }
function calWinner(arr) {
//winning combination
const winningIds = [
[0, 1, 2],
[3, 4, 5],
[6, 7, 8],
[0, 3, 6],
[1, 4, 7],
[2, 5, 8],
[0, 4, 8],
[2, 4, 6],
]
for (let i = 0; i < winningIds.length; i++) {
calculate(arr, winningIds[i])
}
}
function calculate(sup, sub) {
sup.sort()
sub.sort()
let i = 0
let j = 0
for (i, j; i < sup.length && j < sub.length;) {
if (sup[i] < sub[j]) {
++i
} else if (sup[i] == sub[j]) {
++i, ++j
} else {
return false
}
}
return j == sub.length;
}
calWinner([1, 3, 7, 4])
I'm trying to write a function that takes an array, and check if it has every element in a nested array in an object inside of the function.
I've added a function I found from this post, but not sure why i am getting undefined instead of true.
Assuming you want to return true if any array from winningIds is inside arr, this is the code:
function calWinner(arr) {
//winning combination
const winningIds = [
[0, 1, 2],
[3, 4, 5],
[6, 7, 8],
[0, 3, 6],
[1, 4, 7],
[2, 5, 8],
[0, 4, 8],
[2, 4, 6],
]
var containsWinner = false;
for (let i = 0; i < winningIds.length && !containsWinner; i++) {
containsWinner = calculate(arr, winningIds[i])
}
return containsWinner;
}
function calculate(sup, sub) {
sup.sort()
sub.sort()
for (var i = 0; i < sub.length; i++) {
if (sup.indexOf(sub[i]) == -1) {
return false;
}
}
return true;
}
calWinner([1, 3, 7, 4]);
sym([1, 1, 2, 5], [2, 2, 3, 5], [3, 4, 5, 5]) should return [1, 4, 5]
The expected return is shown above. But I couldn't find why it fails this test. What kind of fix is needed to code below?
function sym(args) {
//debugger;
var arr=Array.from(arguments);
var resArr=[];
arr.forEach(function(arrVal){
var c=0;
arrVal.forEach(function(val,index){
console.log("arrVal",arrVal,"index",index,"val",val,"|",arrVal.slice(index+1-c));
if(index<arrVal.length-1 && arrVal.slice(index+1-c).indexOf(val)!==-1){
console.log("here");
arrVal.splice(index-c,1);
c++;
}
console.log("arrVal",arrVal,"index",index,"|",arrVal);
});
resArr.push(arrVal);
});
console.log(resArr);
resArr=resArr.reduce(function(acc,curr){
return acc.concat(curr.filter(function(val){
var notContains=acc.indexOf(val)==-1;
if(!notContains)
acc.splice(acc.indexOf(val),1);
return notContains;
}));
},[]);
console.log(resArr);
return resArr;
}
sym([1, 1, 2, 5], [2, 2, 3, 5], [3, 4, 5, 5]);
You could use a more concise version with filtering duplicates.
function sym(array) {
return array.reduce(function (r, a) {
return r.concat(a.filter(function (a, i, aa) {
return i === aa.indexOf(a);
})).filter(function (a, i, aa) {
return aa.indexOf(a) === aa.lastIndexOf(a);
});
}, []);
}
console.log(sym([[1, 1, 2, 5], [2, 2, 3, 5], [3, 4, 5, 5]])); // [1, 4, 5]
Use Sets. Their support should be sufficent. Extend their prototype and benefit from meaningful methods (union, intersection and difference taken from MDN):
Set.prototype.union = function(setB) {
var union = new Set(this);
for (var elem of setB) {
union.add(elem);
}
return union;
}
Set.prototype.intersection = function(setB) {
var intersection = new Set();
for (var elem of setB) {
if (this.has(elem)) {
intersection.add(elem);
}
}
return intersection;
}
Set.prototype.difference = function(setB) {
var difference = new Set(this);
for (var elem of setB) {
difference.delete(elem);
}
return difference;
}
Set.prototype.symmetricDifference = function(setB) {
return this.union(setB).difference(this.intersection(setB));
}
var set1 = new Set([1, 1, 2, 5]);
var set2 = new Set([2, 2, 3, 5]);
var set3 = new Set([3, 4, 5, 5]);
var result = set1.symmetricDifference(set2).symmetricDifference(set3);
console.log(result); // as Set
console.log([...result]); // as Array