How to copy an array array of numbers using slice() - javascript

I am trying to fill a game board (8 by 8) with random game pieces (images), but not the entire board is filled. Some will be empty. To do so, I am randomly generating x,y coordinates on the board and assigning a random number to it. Since fillBoard is private function in a module, I am copy it over to copyBoard, which is public function.
The idea is to randomly generate an x,y coordinate and put it in an array[x], array[y]. I am having trouble copying the array array though, since not all of them is defined. I was wondering how do you do so?
Here's what I got so far. Its displaying an error because splice() cannot work for an undefined variable.
function fillBoard(){
var x, y;
monsters=[]; //empty array
x = Math.floor(Math.random()*cols);
y = Math.floor(Math.random()*rows);
monsters[x] = []; /* making x variable an array */
monsters[x][y] = Math.floor(Math.random() * numMonsterTypes);
}
function copyBoard() {
var copy = [],
x;
for (x = 0; x < cols; x++) {
if(monsters[x]){
copy[x] = monsters[x].slice(0); //slice(array) -> returns the selected elements in an array
};
};
return copy;
}

It looks like your problem arises from only random array element being initialised.
You can either iterate over all rows AND columns and copy initialise elements only
or
you can store a list/array of initilised elements somewhere else (array element maxRow+1/maxCol+1 ?)

Here's the solution:
function fillBoard(){
var x, y;
monsters=[]; //empty array
x = Math.floor(Math.random()*cols);
y = Math.floor(Math.random()*rows);
monsters[x] = []; /* making x variable an array */
monsters[x][y] = Math.floor(Math.random() * numMonsterTypes);
}
function copyBoard() {
var copy = [],
x;
for (x = 0; x<cols; x++){
copy[x] = [];
if(monsters[x]){
copy[x] = monsters[x].slice(0);;
};
};
return copy;
}

Related

Is there a way to see if a value in a 2D array matches any value in another 2D array?

I'm building a battleship game in Javascript and React and I've been stuck on this issue for a while now even after much Googling and StackOverflowing.
Basically my board is a 2D array, with 10 arrays inside of one array. I'm trying to randomly place ships and I'm having difficulties checking if a ship intersects another ship.
Here's what I have for my ships:
placeShips = () => {
// Logic to place boats randomly below
// Checks required before placing a boat:
// 1. Does the boat go off the board
// 2. Does the boat overlap another boat
// 3. If checks above pass then place boat
let placedPosition = []
let board = this.state.board.slice()
let i
for (i = 0; i < this.state.ships.length; i++) {
// First randomly select coordinates for where the boat will start
let xcoord = Math.floor(Math.random() * 10)
let ycoord = Math.floor(Math.random() * 10)
// Get positions in array where a boat will be
let potentialBoat = []
let newCoords
let j
for (j = 0; j < this.state.ships[i].getLength(); j++) {
newCoords = [xcoord, ycoord + j]
potentialBoat.push(newCoords)
The first for loop repeats for each ship left in my state to place and the second for loop takes a ship's length, gets the intended coordinate ([[0, 1], [0,2]] for a 2 length ship for example) and stores it in the potentialBoat array.
My idea is to use this potentialBoat array and see if there's any [xcoordinate, ycoordinate] that exists already in the placedPosition array and if so, to loop again for the current boat and get new coordinates until they don't intersect.
Is this possible? Or should I rethink my entire implementation? Thanks!
Inside the inner loop, when in the process of creating a ship, consider creating a string representing the coordinates. Eg, for newCoords of 1, 3, create a string 1_3. To validate the location, check to see if that string exists in an array (or Set) of the locations of the validated ships. At the end of the inner loop, once all positions for the length of the ship have been validated, combine the possible-locations into the validated-locations array:
placeShips = () => {
const placedPosition = [];
const board = this.state.board.slice();
const validatedPositionStrings = []; // <---- Create this array
for (const ship of this.state.ships) {
const thisShipLength = ship.getLength();
tryShip:
while (true) {
const thisBoatPossiblePositionStrings = [];
// Generate ship positions until valid
const xcoord = Math.floor(Math.random() * 10);
const ycoord = Math.floor(Math.random() * 10);
const potentialBoat = [];
for (let j = 0; j < thisShipLength; j++) {
// Then check to see if the below position is already in it
const thisCoordinateString = `${x}_${y}`;
if (validatedPositionStrings.includes(thisCoordinateString)) {
// Invalid
continue tryShip;
}
thisBoatPossiblePositionStrings.push(thisCoordinateString);
// If this point is reached, then this particular coordinate is valid
// do whatever you need to do:
const newCoords = [xcoord, ycoord + j];
potentialBoat.push(newCoords);
}
// All positions for ship are valid
// do something with potentialBoat here?
// push positions to placedPosition?
validatedPositionStrings.push(...thisBoatPossiblePositionStrings);
break;
}
}
}
It could be made less computationally complex by using a Set instead of an array, but that probably doesn't matter unless there are a very large number of iterations.
It would also be possible to search your array of arrays to see if the position has already been placed, but that would require an unnecessary amount of code IMO.
If possible, you might consider changing your data structure around so that rather than an array of arrays, you have just a single object representing the coordinates, whose values indicate the ship at that position (and possibly other attributes needed for a particular point), eg:
{
1_3: { ship: 'destroyer', 'attackedYet': 'false' }
// ...
Such an object would probably be easier to look up and work through than an array of arrays of X-Y pairs.

Properties of an object being changed, even though they should be passed by Value

I was working on a small JavaScript project to make a tile based game (not homework, don't worry!), however I ran into a problem with my 'tile' class.
Each 'tile' has an X and a Y property, for its X and Y position on a grid. The grid is stored as a 2 dimensional array of these tiles. The tiles are constructed like so:
class tile {
constructor(x,y) {
this.x = x;
this.y = y;
console.log(`Genned Tile: ${x} , ${y}`);
}
}
There's also a small logging message contained
So then, I wrote a small loop to fill an array with 'tile' objects:
for (var x = 0; x < width; x++) {
for (var y = 0; y < height; y++) {
tiles[x,y] = new tile(x,y);
}
}
width and height are both set to 5. This should fill slots 0-4.
However, when displaying the tiles, the X coord is wrong! I'll attach a snippet to show you what I mean
// tile class
class tile {
constructor(x,y) {
this.x = x;
this.y = y;
console.log(`Genned Tile: ${x} , ${y}`);
}
}
var width = 5;
var height = 5;
var tiles = new Array(width,height);
// populates array
for (var x = 0; x < width; x++) {
for (var y = 0; y < height; y++) {
tiles[x,y] = new tile(x,y);
}
}
// displays each item in the array
for (var x = 0; x < width; x++) {
for (var y = 0; y < height; y++) {
let tile = tiles[x,y];
console.log(`Checked Tile: ${tile.x} , ${tile.y}`);
}
}
The Y coord looks correct, however the X coord remains at 4! The logging message seems to tell me that it's putting the correct number in the constructor, so I'm unsure why the X coord is changing like this. I did some research and found out that the 'x' and 'y' numbers SHOULD be passed by value (they are primitives), so any changes to 'x' and 'y' in the loop shouldn't affect my class, right?
Any help would be greatly appreciated.
Thanks for reading! :)
Each of these is incorrect for JavaScript:
var tiles = new Array(width,height);
// ...
tiles[x,y] = new tile(x,y)
That sort of notation is valid in other languages, but not JavaScript (or Java or similar).
In JavaScript, there are no multidimensional arrays. Instead, there are arrays of arrays. You create an array:
var tiles = [];
and then put arrays in it:
tiles[x] = [];
and then put tiles in that:
tiles[x][y] = new tile(x, y);
Live example, see the *** lines:
// tile class
class tile {
constructor(x,y) {
this.x = x;
this.y = y;
console.log(`Genned Tile: ${x} , ${y}`);
}
}
var width = 5;
var height = 5;
var tiles = []; // ***
// populates array
for (var x = 0; x < width; x++) {
tiles[x] = []; // ***
for (var y = 0; y < height; y++) {
tiles[x][y] = new tile(x,y); // ***
}
}
// displays each item in the array
for (var x = 0; x < width; x++) {
for (var y = 0; y < height; y++) {
let tile = tiles[x][y]; // ***
console.log(`Checked Tile: ${tile.x} , ${tile.y}`);
}
}
.as-console-wrapper {
max-height: 100% !important;
}
Side note: The overwhelming convention in JavaScript is that constructor functions (functions you call with new) start with an upper case character. So Tile rather than tile.
You may be wondering, if these are incorrect:
var tiles = new Array(width,height);
// ...
tiles[x,y] = new tile(x,y)
...why don't they cause a syntax (or other) error?
The reason is that they're both valid, they just don't do what you intended them to do (and what they would do in some other languages).
This:
var tiles = new Array(width,height);
creates an array with two entries in it (the values of width and height):
// Example 1:
const width = 5;
const height = 5;
const tiles = new Array(width,height);
console.log(tiles); // [5, 5]
// Example 2:
const array2 = new Array("10"); // <== Note that the "10" is a string
console.log(array2); // ["10"]
A better way to write it (if you'd wanted to do that) would be:
var tiles = [width, height];
There's almost never any reason to call the Array constructor in JavaScript, and it's a bit of a confusing function: If you call it with one numeric argument, it creates an empty array using that argument's value as the array length (which sounds weird, but JavaScript's arrays can be sparse, so having a length of [say] 10 but no entries is a valid state for a JavaScript array). If you pass it more than one argument ("Example 1"), or one argument that isn't a number ("Example 2"), it treats its arguments as the initial values to put in the array, which is why new Array(width, height) creates an array with those two values in it.
This one is trickier:
tiles[x,y] = new tile(x,y)
JavaScript has an unusual operator: The comma operator. It evaluates its left-hand operand, throws away that result, evaluates its right-hand operand, and takes the right-hand result as its result. So tile[x,y] is effectively just tile[y], because although x is evaluated, its result is thrown away, and only y is used to look up the entry in the array. Example:
const a = 2;
const b = 3;
const c = (a, b);
console.log(c);
// => 3;
function one() {
console.log("one ran");
return 1;
}
function two() {
console.log("two ran");
return 2;
}
const d = (one(), two());
// => one ran
// => two ran
// => 2

Why doesn't the array store the composite type in a proper way in JavaScript?

I have a function that generates numbers within a range.
I created a composite type like this:
var cowPosition = {
x: 0,
y: 0
};
I also created an array:
var positionsArray = [];
then, I proceed to iterate to fill the array with the composite type.
All of this is inside a function which returns the array.
Here's the function:
function generateCowPositions(numberOfCows){
var positionsArray = [];
var cowPosition = {
x: 0,
y: 0
};
var x,y;
for (var i = 0; i < numberOfCows; i++) {
x = randomPosition(0,5);
y = randomPosition(0,5);
x = x * 80;
y = y * 80;
cowPosition.x = x;
cowPosition.y = y;
positionsArray[i] = cowPosition;
}
return positionsArray;
}
When I run it, it fills the whole array with the last two generated coordinates.
There is no "composite type" in JavaScript. What you are referring to is called an object.
The problem you are having is that objects are passed by reference, not by value. This means that if you store an object into a variable called a and modify it in some function, the value stored in a will be modified too.
What you need to do is:
function generateCowPositions(numberOfCows) {
var positionsArray = [];
// note: cowPosition object is not needed anymore, so I've removed it
var x, y;
for (var i = 0; i < cantidadVacas; i++) {
x = randomPosition(0, 5);
y = randomPosition(0, 5);
x = x * 80;
y = y * 80;
// create a new object in every intration
positionsArray[i] = {
x: x,
y: y,
};
}
return positionsArray;
}
Because there is only one instance of cowPosition in your code. So every iteration of your loop simply changes that one object, and at the end of the loop you simple keep the result of the last iteration.
Each index in the array is pointing to the exact same object, being cowPosition
I'm not sure what you are trying to accomplish 100%, but you should create a new object at each iteration. There isn't a need to initialize x and y in the object.
Simply:
function generateCowPositions(numberOfCows){
var positionsArray = [];
var cowPosition = {}
var x,y;
for (var i = 0; i < cantidadVacas; i++) {
x = randomPosition(0,5);
y = randomPosition(0,5);
x = x * 80;
y = y * 80;
cowPosition.x = x;
cowPosition.y = y;
positionsArray[i]= Object.assign({}, cowPosition);
}
return positionsArray;
}
JavaScript is an object-oriented language where objects are passed by-reference, not by-value. I suspect you're thinking of C and C++ where struct/class values are by default copied in their entirety by the = operator. In JavaScript it's closer to C# and Java where object / non-primitive types have their references copied instead.
Objects in javascript are passed as references.
positionsArray[i] = cowPosition; sets positionsArray[i] to a reference to the cowPosition object.
After the loop, all array elements reference the same cowPosition object. Therefore, since each loop of the array changes the underlying cowPosition object, all the array elements appear to have the same x and y position.
A simple solution to this problem would be to shallow copy the cowPosition object inside the loop, such that each array element references a difference position object and changing the underlying cowPosition object has no effect on the shallow copies inside the positionsArray, like so:
positionsArray[i] = JSON.parse(JSON.stringify(cowPosition));
This works by converting the object to a string and back again using javascript's native JSON implementation.

What is the purpose of this Javascript object key?

Consider this function in matter.js:
/**
* Creates a new set of axes from the given vertices.
* #method fromVertices
* #param {vertices} vertices
* #return {axes} A new axes from the given vertices
*/
Axes.fromVertices = function(vertices) {
var axes = {};
// find the unique axes, using edge normal gradients
for (var i = 0; i < vertices.length; i++) {
var j = (i + 1) % vertices.length,
normal = Vector.normalise({
x: vertices[j].y - vertices[i].y,
y: vertices[i].x - vertices[j].x
}),
gradient = (normal.y === 0) ? Infinity : (normal.x / normal.y);
// limit precision
gradient = gradient.toFixed(3).toString();
axes[gradient] = normal;
}
return Common.values(axes);
};
For completion, here is the Common.values() function:
/**
* Returns the list of values for the given object.
* #method values
* #param {} obj
* #return {array} Array of the objects property values
*/
Common.values = function(obj) {
var values = [];
if (Object.keys) {
var keys = Object.keys(obj);
for (var i = 0; i < keys.length; i++) {
values.push(obj[keys[i]]);
}
return values;
}
// avoid hasOwnProperty for performance
for (var key in obj)
values.push(obj[key]);
return values;
};
I can't quite figure out the structure of the axes object. I don't see the point of the axes[gradient] = normal code since the Common.values() function only returns the values and, therefore, the gradient will never be returned?
Yes, the gradient is never returned, only the normal values are. The entire process of stuffing them in that object is to avoid duplication, as the comment explains:
// find the unique axes, using edge normal gradients
If you have multiple normals with similar (up to three digits) gradients, only the last of them will be returned.

Where are the arguments critter & vector defined in Eloquent JavaScript Chapter 7

Working slowly through the Eloquent JavaScript book by Marijn Haverbeke and am trying to get my head around the first step of the World.turn() function:
World.prototype.turn = function() {
var acted = [];
this.grid.forEach(function(critter, vector) {
if (critter.act && acted.indexOf(critter) == -1) {
acted.push(critter);
this.letAct(critter, vector);
}
}, this);
};
Where does the prototype forEach function get the arguments critter and vector from.
When I log their output to the console, I get an object with the originChar and a direction if valid, but can't get my head around where it is getting the arguments.
The link to the section is as follows: http://eloquentjavascript.net/07_elife.html#h_6OGIzAd5Tr
Thanks in advance :)
The World owns a Grid, which has forEach defined on it just above the section you're looking at.
Grid.prototype.forEach = function(f, context) {
for (var y = 0; y < this.height; y++) {
for (var x = 0; x < this.width; x++) {
var value = this.space[x + y * this.width];
if (value != null)
f.call(context, value, new Vector(x, y));
}
}
};
The grid's cells contain critters, which you see captured in var value = this.space[x + y * this.width] and passed to the function you provide forEach, along with new Vector(x, y) containing the location in the grid.

Categories

Resources