JavaScript Battleship Array splice - javascript

I'm making a battleship game in JavaScript and I stumbled upon a problem
var targetString = target.replace(/\s+/g, '');
for(var i = 0; i !== inputArray.length; i++) {
for(var j = 0; j !== boats[i].usedPositions.length; j++) {
if(targetString === boats[i].usedPositions[j].toString()) {
hit = true;
boats[i].hits[j] = 1;
console.log(boats[i].hits);
currentBoat = boats[i];
boats[i].usedPositions.splice(j,1);
break;
}
}
}
if(hit && stop == false) {
alert ("Hit!");
if(allEquals(currentBoat.hits, 1) && hit) {
alert("Boat with length " + currentBoat.hits.length + " has sunken!");
sunkenBoat++;
}
}
The first piece of code checks if the entered coordinate matches a coordinate of the boats (all of these are stored in usedPositions). To prevent that the player can hit that boat again, I want to take the hit coordiante out of that array using splice. But when I do this, it doesn't alert me when a boat has sunken (second piece of code). When the line with splice in is removed, it does give the alert. Can anyone help me? Full code is found here.

splice moves the subsequent array elements down to fill the space. Your logic doesn't look like it's expecting things to move like that.
Instead of splice, you probably just want to assign some other value to that array location, e.g.:
boats[i].usedPositions[j] = " "; // Where " " is assumed not to represent a boat

Related

Code is getting stuck somewhere in a succession of for-loops and I'm not sure why

EDIT - I changed the code to correctly declare variables below but nothing seems to have changed
I've written code using a for-loop that has to satisfy a number of criteria before executing what's within it. The problem is that, somewhere along the way, the code is getting stuck inside one of the loops, causing the computer to crash.
I've tried breaking the loop but this doesn't seem to help.
function compareKeypoints(varifiedKeypoints) {
outer_loop: for (i = 0; i < varifiedKeypoints.length; i++) {
let initialKeypoint = varifiedKeypoints[i];
for (j = 0; j < varifiedKeypoints.length; j++) {
let comparisonKeypoint = varifiedKeypoints[j];
if (initialKeypoint.part != comparisonKeypoint.part) {
if (Math.abs(comparisonKeypoint.position.x - initialKeypoint.position.x) <= 20
&& Math.abs(comparisonKeypoint.position.y - initialKeypoint.position.y) <= 20) {
if (keypointsCompatible(initialKeypoint.part, comparisonKeypoint.part)) {
console.log("Activating part: " + initialKeypoint.part);
console.log("Activated part: " + comparisonKeypoint.part);
let keypointPair = {
point_1: initialKeypoint.part,
point_2: comparisonKeypoint.part
}
console.log("Pushing parts!");
activeParts.push(keypointPair);
console.log("breaking loop!");
break outer_loop;
console.log("Loop NOT broken!!");
}
}
}
}
}
if (activeParts.length > 0) {
console.log(activeParts);
}
}
function keypointsCompatible(keypoint_1, keypoint_2) {
var outcome = true;
if (activeParts.length > 0) {
compatibility_loop: for (i = 0; i < activeParts.length; i++) {
if (Object.values(activeParts[i]).includes(keypoint_1) && Object.values(activeParts[i]).includes(keypoint_2)) {
console.log(keypoint_1 + " and " + keypoint_2 + " are not compatible because they already exist as " + activeParts[i].point_1 + " and " + activeParts[i].point_2 + " respectively");
outcome = false;
break compatibility_loop;
console.log("Compatibility NOT broken!!");
}
}
}
console.log("Compatibility outcome is " + outcome);
return outcome;
}
The code is suppose to take two values in the same array and compare them. If a number of conditions are met, including if they're a certain distance apart from one another, they will be pushed into a secondary array. If the values already appear in the secondary array, which the keypointCompatible function is suppose to determine, the loop should either continue looking for other candidates or stop before being called again. For some reason, however, the code is getting stuck within the keypointCompatible function when it detects that the values have already appeared in the secondary array and the console will repeatedly print "Compatibility is false" until the browser crashes.
Working Solution
Use let or const instead of var or nothing. Your issue may be related to closures and variables reused between loops. Make sure you use let or const in your loops too. for (let i=0).
When you use let or const, the runtime will create a new instance every time the block or loop iterates. However, using var will reuse the internal allocation.
So what happens with the standard var is the multiple closures or loops each use the same instance of the variable.
Unless you want the var behavior, always use let or const.
Another Solution
Put a newline after the label compatibility_loop
Still Another Solution
The first function is pushing into activeParts. The second function is looping activeParts. This can go on forever, or longer than expected. Pushing into the array could possibly make the loop limit never reached.
Put a log on the length of activeParts in the second function to see if it is growing out of control.
Your code should be OK if varifiedKeypoints.length has reasonable value. And all internal variables are declared properly!
You have two loops (this inner can start at j=i+1 to save time and multiple calculations) with few conditions inside.
function compareKeypoints(varifiedKeypoints) {
outer_loop: for (let i = 0; i < varifiedKeypoints.length; i++) {
let initialKeypoint = varifiedKeypoints[i];
for (let j = i+1; j < varifiedKeypoints.length; j++) {
let comparisonKeypoint = varifiedKeypoints[j];

How to search max and min values over a json object that gets data from inputs using just javascript?

as you may see over the link (https://jsfiddle.net/andresmcio/vLp84acv/) is a simple form that stores data as:
var _newStudent = {
"code": code,
"names": names,
"grade": grades,
};
I'm having trouble getting the highest and lowest grade with their respective buttons as it is showing the last entry as either maximum and minimum value.
Would be glad if anyone could help, keeping in mind that it should be displayed as it is (With the alerts) and only using javascript, not jquery or any other.
Thanks beforehand.
It looks like you might have two problems in your functions. 1.) You are resetting the variables used to track the information with each iteration. 2.) You are only comparing each student to the same student, during each iteration. Thus, I think something like the below would provide what you are looking for.
function hghGrade(json) {
if (json.length > 1) {
var text = "";
var maxGrade = 0;
var thisGrade = 0;
for (var i = 0; i < json.length; i++) {
thisGrade = parseFloat(json[i].grade);
if (maxGrade < thisGrade)) {
maxGrade = thisGrade;
test = json[i].names + " ID code " + json[i].code + " with grade " + json[i].grade;
}
}
alert("The best grade is from: " + text);
} else {
alert("Please register at least two students");
}
}

counter variable is holding 2 values on 2nd pass of the function

I'm making a typing game. When multiple players play the game it runs through the same set of functions again. I'm using the variable j as a counter to advance words when they are typed correctly. For some reason, on the second pass on each upkeystroke, it logs j = 1 & j = whatever the value of the previous players last word + 1 is. When each player plays, I want each set of words they are typing to be the same, so that it is fair. I can't figure out why this is happening or even how the variable has 2 values at the same time?!?!?
What gives?
Here's the code in question, but there's a bunch of callbacks that could be involved, although the only place this variable is called is inside this function.
//advances ship on correct typing
function runRace() {
timer();
var j = 1;
//BUG HERE !! Works fine on first iteration but on second
//iterations value jumps beteween 1 and whatever the next
//one is. It's like on every keystroke it reassigns var j
//back to 1, then back to the element number it was on
//last time
//!!! j has 2 values !!!it's keeping the value from the
//prior running of run race
$(document).keyup(function(e){
var targetWord = $(".toType").text();
var typedWord = $("#word").val();
//while (j < gameWords.length){
console.log("j = " + j);
if(typedWord === targetWord){
$(".player").css({left: "+=15px",});
targetWord = $(".toType").text(gameWords[j]);
$("#word").val("");
j++;
}else {
return
};
//}
});
}
If you need to see the rest of the code to figure this out, it's here. Eventhough it's not running right on jsfiddle for reason, it works other then the bug, locally https://jsfiddle.net/ujsr139r/1/
As i mentioned in my comment you're creating multiple listeners everytime runRace() is called.
You could try something like this instead (please note, this isn't the best way to do this, i'm just demoing. Global variables like j in this case aren't a clever idea.:
var j=1; // global because its outside of your function
$(function(){
$(document).keyup(function(e){
var targetWord = $(".toType").text();
var typedWord = $("#word").val();
//while (j < gameWords.length){
console.log("j = " + j);
if(typedWord === targetWord){
$(".player").css({left: "+=15px",});
targetWord = $(".toType").text(gameWords[j]);
$("#word").val("");
j++;
}else {
return
};
//}
});
});
//advances ship on correct typing
function runRace() {
j = 1;
timer();
}

Populate a prompt with elements of an array and number them off

(Stack Overflow doesn't have a tag for 'prompt' so I have used alert as I am guessing it is similar enough to attract the right answerers.)
Hello,
I am currently making a JavaScript-based game for an assignment at university. I am usually pretty good with problem solving but have been stumped by this issue.
To explain, I have an array which names the possible armour slots the player can pick. In any order these can be picked, and each time the choice gets pushed to a second array which handles what has already been picked (and in what order) and that item gets spliced from the original array. There is a while loop which runs through until all 3 have been picked.
var armourSlotToPick = ["Head", "Chest", "Legs"],
armourSlotPicked = [],
armourLoop = 1,
indexArmour = 0;
function numInArray() {
indexArmour++;
return (indexArmour + ". " + armourSlotToPick[indexArmour - 1] + "\n");
}
function armour() {
while (armourLoop < 4) {
var armourPick = prompt("Pick an armour slot to generate an item for:\n" + armourSlotToPick.forEach(numInArray));
if (armourPick == 1) {
armourSlotPicked.push(armourSlotToPick[0]);
armourSlotToPick.splice(0,1);
} else if (armourPick == 2) {
armourSlotPicked.push(armourSlotToPick[1]);
armourSlotToPick.splice(1,1);
} else if (armourPick == 3) {
armourSlotPicked.push(armourSlotToPick[2]);
armourSlotToPick.splice(2,1);
} else {
alert("Invalid choice, you suck");
break;
}
armourLoop++;
}
}
I know it probably wouldn't be possible to do the whole return in numInArray() to the prompt, but it shows some working.
Now the problem: I got it working so that each item in the array was numbered (var armourSlotToPick = ["1. Head", "2. Chest", "3. Legs"],) but as you could see, if the player chose 2, then the next time it would show "1. Head (new line) 3. Legs" and when the player chooses 3, a problem would occur, as they were really meant to choose 2. How is it possible to number the items in the array, in a prompt?
I'm possibly over thinking this but I have suffered for a few hours now.
I thank you in advance for any insight you may have,
Daniel.
EDIT: Solved.
Below is the end result, a slight variation from the edited answer from Jonathan Brooks.
var armourSlotToPick = [null, "Head", "Chest", "Legs"]
var armourSlotPicked = [null];
var armourLoop = 1;
function armour() {
while (armourLoop < 4) {
var message = "Pick an armour slot to generate an item for:\n";
for (var i = 0; i < armourSlotToPick.length; i++) {
if (armourSlotToPick[i] !== null) {
message += "" + i + ". " + armourSlotToPick[i] + "\n";
}
}
var armourPick = prompt(message);
if (armourPick > armourSlotToPick.length-1 || armourPick < 1) {
alert("Invalid choice, you suck");
} else {
var insert = armourSlotToPick.splice(armourPick, 1);
armourSlotPicked.push(insert);
}
armourLoop++;
}
armourSlotPicked.splice(0,1);
}
armour();
alert(armourSlotPicked.join("\n"));
I thank all that have contributed to this discussion and the end result, and I hope this is a good example for future problems people may have similar to this.
Check out my fiddle, I think I have a working solution.
What you really want to be using are Object Literals with your own indexing (starting from 1) - if it were me, I would create my own way to iterate over this custom indexing by adding a method to the Object's prototype, but I digress.
You're overcomplicating your code by using a while loop, and that large bulk of if statements is unnecessary: instead, all you need is some basic validation on the input and then you can just trust whatever input passes this validation. That is demonstrated here:
if ( armourPick > armourSlotToPick.length || armourPick < 1 ) {
alert("Invalid choice, you suck");
}
else {
armourSlotPicked.push( armourSlotToPick[armourPick-1] )
alert (armourSlotPicked[armourSlotPicked.length-1].value);
}
Read my code carefully, and you should get a better understanding of how to deal with certain issues.
EDIT:
As per your request, I think I have a solution that suits your needs. Basically all you have to do to have the arrays "start" at an index of 1 is to fill the zeroth element with a null value, like so:
var armourSlotToPick = [null, "Head", "Chest", "Legs"]
var armourSlotPicked = [null];
You just have to remember to take this null object into account in your code, for example:
if (armourSlotToPick[i] !== null) {
message += "" + i + "\n";
}
The indices will update automatically. See this updated fiddle for more details.
use structures / objects as content in the array, instead of just values.
the basic concept:
armourSlotPicked.push({ "key": 1, "value":armourSlotToPick[1]})
alert("value: " + armourSlotPicked[0].value)
alert("key: " + armourSlotPicked[0].key)
edit: responding to comments can take some space.
IMHO a prompt is the completely wrong tool for this, since most browsers would ask the user permission to prevent multiple popups, and since a promt can only return 1 piece of information, you can only ask for 1 thing per popup. Instead you ought to use a div element, with checkboxes for each information..
That being said it can easily be used in a promt.
The prompt is just a built in function, that takes a string as an argument (which is shown as text in the popup) and returns a string with the users input.
what does the magic for you is in fact this:
array.foreach(): The forEach() method executes a provided function once per array element.
in your case that means it calls a function that returns a string for each element in the array, and concatenates the strings.
in the old days you would have written this:
var messageText= "Pick an armour slot to generate an item for:\n"
for(var i = 1; i < armourSlotToPick.length; i++){
messageText += i + ". " + armourSlotToPick[i- 1] + "\n";
}
var armourPick = prompt(messageText);
but in this modern age, you define a printing function, and use it to generate the loop:
function numInArray() {
indexArmour++;
return (indexArmour + ". " + armourSlotToPick[indexArmour - 1] + "\n");
}
//more code before we get to where the function is used....
indexArmour = 0;
var messageText = "Pick an armour slot to generate an item for:\n" + armourSlotToPick.forEach(numInArray);
var armourPick = prompt(messageText);
or in a single line as in your code:
indexArmour = 0; //you forgot this - otherwise the list will only be complete once?
var armourPick = prompt("Pick an armour slot to generate an item for:\n" + armourSlotToPick.forEach(numInArray));
It produces the same output, because it does the same thing, its just written very differently!
If the array holds "object literals" instead of simply values, as I suggest, the old fashioned code would look something like this:
function contains(a, value) {
try{
for (var i = 0; i < a.length; i++) {
if (a[i].value == value) {
return true;
}
}
}
catch(err) {
// do nothing
};
return false;
}
and later..
for(var j = 0; j < 4; j++){
for(var i = 0; i < Math.min(armourSlotToPick.length); i++){
if( contains(armourSlotPicked, armourSlotToPick[i- 1]) )
continue;
var messageText = "Generate an item for armour in slot: " + i + "\n"
messageText += armourSlotToPick[i- 1] + "\n";
}
var armourPick = prompt(messageText);
if (armourPick > 0 && armourPick < armourSlotToPick.length) {
armourSlotPicked.push({"key":j, "value":armourSlotToPick[armourPick]);
}
...
}
//now we have an array that holds information about when what was picked..
or something along those lines.. this is bt.w completely untested, it's just for illustration
You want to use the array index to number your items. Since your numbers are one-based and the index is zero-based, you will need to convert between the two when outputting and interpreting the response.
This approach will also allow you to eliminate all but two of the cases in your if-else statement.

For Loop Manipulate Multidimensional Array Javascript

Okay so I have a 2D array that I am trying to alter using javascript. This is what I have so far:
for (var i = 0; i <= inputData.length; i++ {
inputData[0,0] = inputData[0,0];
inputData[i,0] = inputData[((i - 1) + 1/12), 0];
I want this to take array [i-1] value and then add 1/12 to it
for (j = 13; inputData.length; j += 13) {
delete inputData[j,0];
delete inputData[j,1];
}
Also, I want to delete the entire 2D array at every 13th increment value.
}
This is what I have so far. I am sure there are probably errors within it. Can you guys help me out here? Any help would be greatly appreciated.
Couple of things - you need to be careful when iterating over an array that you're removing from, your indexes will end up offset with respect to your data as soon as you do a delete. Secondly your syntax for deletion is off.
Normally in these situations I favour creating a new array containing the data I want to keep.
var inputData = [[1,1],[2,2],[3,3],[4,4]];
var b = [];
for (i=0; i < inputData.length; i++) {
if ((i + 1) % 13 != 0) {
var year_with_month = inputData[i][0] + i * 1/12;
var e = [year_with_month, inputData[i][1]]
b.push(e);
}
}
inputData = b;
Also, given a choice I'd use a library like underscore to make it easy to do the looping. I never manually write for loops anymore, took me a couple of attempts to get that one right :)

Categories

Resources