basic 2d gaming question involving arrays and json javascript - javascript

Now that the format of this game is established let's start writing some functionality one small piece at a time. For this part, write a function named "movePlayer" that takes as parameters a JSON string representing a level in the format above and a string representing the first movement of the player. The movement string will be either "w", "a", "s", or "d" based on the direction the player intends to move with "w" for up, "a" for left, "s" for down, and "d" for right. Return the new player location as an array in the format [x, y]. Remember to check for walls, locked doors, and the exit as the player cannot move through these (you can assume the player has no keys and does not have all the crystals). You may assume that the player did not walk into a lava tile. If the player walked into the edge of the map it should be treated as a wall (Ex. walking to the right while already on the right edge of the map shouldn't move the player)
function movePlayer(json_object, f){
var level = JSON.parse(json_object);
var grid = level["grid"];
var pos = level["start"];
var x = pos[0];
var y = pos[1];
if (f=="w"){
y = y - 1;
}
else if (f=="s"){
y = y + 1;
}
else if (f=="a"){
x = x - 1;
}
else if(f=="d"){
x = x + 1;
}
if (grid[y][x] == 1 || grid[y][x] == 3 || grid[y][x] == 7){
pos = level["start"];
}
else if (grid.includes(grid[y][x])){
pos = [x, y];
}
else{
pos = level["start"]
}
return pos;
}
function movePlayer incorrect on input ['{"start": [2, 1], "grid": [[1, 1, 0, 1], [0, 3, 0, 7], [1, 0, 0, 1], [0, 1, 0, 0]]}', 'w']
returned: [2, 1]
expected: [2, 0]
I'm so confused and don't know how to fix this, if anyone could help I'd greatly appreciate it.

You get [2,0], but then fall into checks and last else statement returns you to [2,1], which is your start.
I think you are mistaken in grid.includes(grid[y][x]) condition, it is always false, because grid is array of arrays and you are checking it for exact integer value.

Related

Which algorithm should be used to find the point?

You need to find some unknown, predetermined point in three-dimensional space, in the smallest number of attempts, using only a function that can return the distance from
any point you pass to it to the desired unknown point.
To solve the problem, first implement a function f that, by taking the coordinates of any point s(x, y, z), return the distance between that point and a conditionally unknown, randomly generated point
point you arbitrarily generate r(x, y, z), where x, y, z can be integers between
0 и 100.
For example, for an arbitrarily generated point r(0, 0, 10) and a point passed to the function
s(0, 0, 0), the result of the function would be as follows:
f(s) = 10 // the distance between s(0, 0, 0) and r(0, 0, 10) is 10
Next, implement the algorithm itself for the assignment. The algorithm should find the coordinates of
of an arbitrarily generated point with the least number of calls to the function f.
I have a randomizer instead of an algorithm, that's all I got. Help.
const pointToFound = {
x: 12,
y: 9,
z: 76,
};
let attemts = 0;
let isXFound = false;
let isYFound = false;
let isZFound = false;
const pointHistory = [];
const getRandomPoint = () => {
return {
x: isXFound ? isXFound : Math.floor(Math.random() * 101),
y: isYFound ? isYFound : Math.floor(Math.random() * 101),
z: isZFound ? isZFound : Math.floor(Math.random() * 101),
};
};
const getDifference = (point, pointToCompare) => {
return {
x:
Math.max(point.x, pointToCompare.x) - Math.min(point.x, pointToCompare.x),
y:
Math.max(point.y, pointToCompare.y) - Math.min(point.y, pointToCompare.y),
z:
Math.max(point.z, pointToCompare.z) - Math.min(point.z, pointToCompare.z),
};
};
const condition = !isXFound && !isYFound && !isZFound;
while (condition) {
const point = getRandomPoint();
const difference = getDifference(point, pointToFound);
pointHistory.push(point);
attemts += 1;
if (isXFound && isYFound && isZFound) {
console.log("Total attempts: ", attemts);
console.log(point);
break;
}
if (difference.x === 0 && !isXFound) {
isXFound = point.x;
}
if (difference.y === 0 && !isYFound) {
isYFound = point.y;
}
if (difference.z === 0 && !isZFound) {
isZFound = point.z;
}
}
console.log(pointHistory);
I have a randomizer instead of an algorithm, that's all I got. Help.
This can be done with at most 3 guesses and often with 2 guesses:
Let the first guess be [0, 0, 0], and ask for the distance
Find in the 100x100x100 cube all points that have that distance to [0, 0, 0]. There might be around 100-200 points that have that distance: consider all of these candidates.
Take the first candidate as the second guess and ask for the distance
Find among the other candidates the ones that have exactly that distance to the first candidate. Often there will be only one point that satisfies this condition. In that case we can return that candidate and only 2 guesses were necessary.
Otherwise (when there is more than one candidate remaining) repeat the previous step which will now certainly lead to a single point.
Here is an implementation that provides a blackbox function which chooses the secret point in a local variable, and which returns two functions: f for the caller to submit a guess, and report for the caller to verify the result of the algorithm and report on the number of guesses. This is not part of the algorithm itself, which is provided in the findPoint function.
const rnd = () => Math.floor(Math.random() * 101);
const distance = (a, b) =>
a.reduce((acc, x, i) => acc + (x - b[i]) ** 2, 0) ** 0.5;
function findPoint(f) {
// First guess is always the zero-point
let guess = [0, 0, 0];
let dist = f(guess);
if (dist === 0) return guess; // Extremely lucky!
// Find the points in the cube that have this distance to [0,0,0]
let candidates = [];
const limit = Math.min(100, Math.round(dist));
for (let x = 0; x <= limit; x++) {
const p = [x, limit, 0];
// Follow circle in X=x plane
while (p[1] >= 0 && p[2] <= limit) {
const d = distance(p, guess);
const diff = d - dist;
if (Math.abs(diff) < 1e-7) candidates.push([...p]);
if (diff >= 0) p[1]--;
else p[2]++;
}
}
// As long there are multiple candidates, continue with a guess
while (candidates.length > 1) {
const candidates2 = [];
// These guesses are taking the first candidate as guess
guess = candidates[0];
dist = f(guess);
if (dist === 0) return guess; // lucky!
for (const p of candidates) {
let d = distance(p, guess);
let diff = d - dist;
if (Math.abs(diff) < 1e-7) candidates2.push(p);
}
candidates = candidates2;
}
return candidates[0]; // Don't call f as we are sure!
}
function blackbox() {
const secret = [rnd(), rnd(), rnd()];
console.log("Secret", JSON.stringify(secret));
let guessCount = 0;
const f = guess => {
guessCount++;
const dist = distance(secret, guess);
console.log("Submitted guess " + JSON.stringify(guess) + " is at distance " + dist);
return dist;
};
const report = (result) => {
console.log("Number of guesses: " + guessCount);
console.log("The provided result is " + (distance(secret, result) ? "not" : "") + "correct");
}
return {f, report};
}
// Example run
const {f, report} = blackbox();
const result = findPoint(f);
console.log("Algorithm says the secret point is: " + JSON.stringify(result));
report(result);
Each run will generate a new secret point. When running this thousands of times it turns out that there is 1/9 probability that the algorithm needs a third guess. In the other 8/9 cases, the algorithm needs two guesses.
One idea is as follows:
You pick an initial random point, and for each dimension, find the exact value. How? For the sake of symmetry, suppose that you desire to find x of the target point. Increase by one the x, and compute the distance of the new point from the target point. If it goes further, it means that you should move in the opposite direction. Hence, you can run a binary search and get the distance to find the exact x of the target point. Otherwise, it means that you are going in the right direction along X-axis. So, do a binary search between all points with the same y and z such that their x values can change from x+1 to 100. A more formal solution comes in the following (just a pseudo-code).
You should also ask about the complexity of this solution. As the dimension of the point is constant (3) and checking these conditions take a constant time, the complexity of number of calling getDifference function is O(log(n)). What is n here? the length of valid range for coordinates (here is 100).
1. p: (x,y,z) <- Pick a random coordinate
2. dist: (dist_x, dist_y, dist_z) <- getDifference(p, TargetPoint)
3. For each dimension, do the following (i can be 0 (x), 1 (y), 2 (3)):
4. if(dist == 0):
5. isFound[i] <- p[i]
6. continue
7. new_i <- p[i] + 1
8. new_point <- p
9. new_point[i] <- new_i
10. new_dist <- getDifference(new_point, pointToFound)
11. if(new_dist == 0):
12. isFound[i] <- new_point[i];
13. continue
14. if(new_dist[i] > dist[i]):
15. isFound[i] <- binary_search for range [0, p[i]-1] to find the exact value of the pointToFound in dimension i
15. continue
16. else:
17. isFound[i] <- binary_search for range [p[i] + 1, 100] to find the exact value of the pointToFound in dimension i
18. continue
Following method will work for coordinates with positive or negative real values as well.
Let's say you are searching for the coordinates of point P. As the first query point, use origin O. Let the distance to the origin O be |PO|. At this point, you know that P is on the surface of sphere
(P.x)^2 + (P.y)^2 + (P.z)^2 = |PO|^2 (1)
As the second query point, use Q = (|PO|, 0, 0). Not likely but if you find the distance |PQ| zero, Q is the point you are looking for. Otherwise, you get another sphere equation, and you know that P is on the surface of this sphere as well:
(P.x - |PO|)^2 + (P.y)^2 + (P.z)^2 = |PQ|^2 (2)
Now, if you subtract (1) from (2), you get
(P.x - |PO|)^2 - (P.x)^2 = |PQ|^2 - |PO|^2 (3)
Since the only unknown in this equation is P.x you can get its value:
P.x = (((-|PQ|^2 + |PO|^2) / |PO|) + |PO|)/2)
Following similar steps, you can get P.y with R = (0, |PO|, 0) and P.z with S = (0, 0, |PO|). So, by using four query points O, Q, R, and S you can get the coordinates of P.

Unexpected indexOf behavior with multidimensional array

I am attempting to make the classic snake game in p5.js. I have a snake object and I'm storing the locations of its body in a 2d array, this.data, where each element stores an x value and a y value (at index 0 and 1 respectively). As the snake moves, I push new positions into the array.
I ran into a problem when I tried to detect whether or not the snake had run into itself. What I tried to do was test whether its current position was already in the array using indexOf, reasoning that if the new location was open, it would only occur once in the array, at index one less than the array's length. Otherwise, if the location already existed elsewhere in the array (indicating the snake had run into itself), it would return a value less than the length minus one.
However, this doesn't seem to be happening.
function Snake()
{
this.x; //x-coordinate of head
this.y; //y-coordinate of head
this.dx; //velocity in x-direction
this.dy; //velocity in y-direction
this.length; //length of snake
this.data; //stores all locations snake occupies
this.alive = 1; //is the snake alive?
this.update = function(board)
{
if (this.alive)//update head position
{
this.x += this.dx;
this.y += this.dy;
let tempCoords = [this.x,this.y];
this.data.push(tempCoords);
while (this.data.length > this.length) //janky
{
this.data = this.data.slice(1);
}
if (this.data.indexOf(tempCoords) + 1 != this.data.length) //make sure snake hasn't hit itself
{
this.alive = 0;
}
}
}
}
The final if statement always evaluates false even when the snake intersects itself. From the testing I've done, this seems to be an issue with using indexOf on multidimensional arrays. What solutions are there to this problem?
Essentially, you have the following data and you want to see if the data in head is equal to any of the elements in points
var points = [[1,1],[1,2],[1,3]]
var head = [1,2]
You can check for any matches in an array using Array.some() like this:
var overlap = points.some(p => p[0] === head[0] && p[1] === head[1])
var points = [[1,1],[1,2],[1,3]]
var head = [1,2]
var overlap = points.some(p => p[0] === head[0] && p[1] === head[1])
console.log(overlap)
indexOf uses an equality check to find the index, and
[0, 0] === [0, 0]
is false, as objects (and arrays are objects), are compared by reference (and you do have two different arrays). To compare them by their inner values you have to manually check the x and ys against each other:
const collides = this.data.some(coords => coords[0] === this.x && coords[1] === this.y);

Isometric topological sort issue

I've just implemented a topological sort algorithm on my isometric game using this guide: https://mazebert.com/2013/04/18/isometric-depth-sorting/
The issue
Here's a little example (this is just a drawing to illustrate my problem because as we say, a picture is worth a thousand words), what I'm expecting is in left and the result of the topological sorting algorithm is in right
So in the right image, the problem is that the box is drawn BEFORE the character and I'm expecting it to be drawn AFTER like in the left image.
Code of the topological sorting algorithm (Typescript)
private TopologicalSort2() {
// https://mazebert.com/2013/04/18/isometric-depth-sorting/
for(var i = 0; i < this.Stage.children.length; i++) {
var a = this.Stage.children[i];
var behindIndex = 0;
for(var j = 0; j < this.Stage.children.length; j++) {
if(i == j) {
continue;
}
var b = this.Stage.children[j];
if(!a.isoSpritesBehind) {
a.isoSpritesBehind = [];
}
if(!b.isoSpritesBehind) {
b.isoSpritesBehind = [];
}
if(b.posX < a.posX + a.sizeX && b.posY < a.posY + a.sizeY && b.posZ < a.posZ + a.sizeZ) {
a.isoSpritesBehind[behindIndex++] = b;
}
}
a.isoVisitedFlag = 0;
}
var _sortDepth = 0;
for(var i = 0; i < this.Stage.children.length; ++i) {
visitNode(this.Stage.children[i]);
}
function visitNode(n: PIXI.DisplayObject) {
if(n.isoVisitedFlag == 0) {
n.isoVisitedFlag = 1;
if(!n.isoSpritesBehind) {
return;
}
for(var i = 0; i < n.isoSpritesBehind.length; i++) {
if(n.isoSpritesBehind[i] == null) {
break;
} else {
visitNode(n.isoSpritesBehind[i]);
n.isoSpritesBehind[i] = null;
}
}
n.isoDepth = _sortDepth++;
}
}
this.Stage.children.sort((a, b) => {
if(a.isoDepth - b.isoDepth != 0) {
return a.isoDepth - b.isoDepth;
}
return 0;
});
}
Informations
Player:
posX: [the x coordinate of the player]
posY: [the y coordinate of the player]
posZ: 0
sizeX: 1
sizeY: 1
sizeZ: 1
Box:
posX: [the x coordinate of the box]
posY: [the y coordinate of the box]
posZ: 0
sizeX: 3
sizeY: 1
sizeZ: 1
X and Y axis
Do you have any idea of the source of this problem? and maybe how to solve it?
The way to determine whether one object is before the other requires a bit more linear algebra.
First of all, I would suggest to translate the coordinates from the "world" coordinates to the "view" 2D coordinates, i.e. to the rows and columns of the display.
Note also that the original Z coordinate does not influence the sort order (imagine that an object would be lifted up along the Z axis: we can find a sort order where this move would not have any impact). So the above-mentioned translation could assume all points are at Z=0.
Let's take this set-up, but depicted from "above", so when looking along the Z axis down to the game floor:
In the picture there are 7 objects, numbered from 0 to 6. The line of view in the game would be from the bottom-left of this picture. The coordinate system in which I would suggest to translate some points is depicted with the red row/col axis.
The white diagonals in each object link the two points that would be translated and used in the algorithm. The assumption is that when one object is in front of another, their diagonal lines will not intersect. If they would, it would mean that objects are overlapping each other in the game world, which would mean they are like gasses, not solids :) I will assume this is not the case.
One object A could be in front of another object B when in the new coordinate system, the left-most column coordinate of B falls between the two column coordinates of A (or vice versa). There might not really be such an overlap when their Z coordinates differ enough, but we can ignore that, because when there is no overlap we can do no harm in specifying a certain order anyway.
Now, when the coordinates indicate an overlap, the coordinates of diagonals (of A and B) must be compared with some linear algebra formula, which will determine which one is in front of the other.
Here is your adapted function that does that:
topologicalSort() {
// Exit if sorting is a non-operation
if (this.Stage.children.length < 2) return;
// Add two translated coordinates, where each of the resulting
// coordinates has a row (top to bottom) and column
// (left to right) part. They represent a position in the final
// rendered view (the screen).
// The two pairs of coordinates are translations of the
// points (posX + sizeX, Y, 0) and (posX, posY + sizeY, 0).
// Z is ignored (0), since it does not influence the order.
for (let obj of this.Stage.children) {
obj.leftCol = obj.posY - obj.posX - obj.sizeX;
obj.rightCol = obj.posY - obj.posX + obj.sizeY;
obj.leftRow = obj.posY + obj.posX + obj.sizeX;
obj.rightRow = obj.posY + obj.posX + obj.sizeY;
obj.isoSpritesBehind = [];
}
for(let i = 0; i < this.Stage.children.length; i++) {
let a = this.Stage.children[i];
// Only loop over the next objects
for(let j = i + 1; j < this.Stage.children.length; j++) {
let b = this.Stage.children[j];
// Get the two objects in order of left column:
let c = b.leftCol < a.leftCol ? b : a;
let d = b.leftCol < a.leftCol ? a : b;
// See if they overlap in the view (ignoring Z):
if (d.leftCol < c.rightCol) {
// Determine which is behind: some linear algebra
if (d.leftRow <
(d.leftCol - c.leftCol)/(c.rightCol - c.leftCol)
* (c.rightRow - c.leftRow) + c.leftRow) {
// c is in front of d
c.isoSpritesBehind.push(d);
} else { // d is in front of c
d.isoSpritesBehind.push(c);
}
} // in the else-case it does not matter which one comes first
}
}
// This replaces your visitNode function and call:
this.Stage.children.forEach(function getDepth(obj) {
// If depth was already assigned, this node was already visited
if (!obj.isoDepth) {
// Get depths recursively, and retain the maximum of those.
// Add one more to get the depth for the current object
obj.isoDepth = obj.isoSpritesBehind.length
? 1+Math.max(...obj.isoSpritesBehind.map(getDepth))
: 1; // Depth when there is nothing behind it
}
return obj.isoDepth; // Return it for easier recursion
});
// Sort like you did, but in shorter syntax
this.Stage.children.sort((a, b) => a.isoDepth - b.isoDepth);
}
I add a snippet where I completed the class with a minimum of code, enough to make it run and output the final order in terms of object index numbers (as they were originally inserted):
class Game {
constructor() {
this.Stage = { children: [] };
}
addObject(posX, posY, posZ, sizeX, sizeY, sizeZ) {
this.Stage.children.push({posX, posY, posZ, sizeX, sizeY, sizeZ,
id: this.Stage.children.length}); // add a unique id
}
topologicalSort() {
// Exit if sorting is a non-operation
if (this.Stage.children.length < 2) return;
// Add two translated coordinates, where each of the resulting
// coordinates has a row (top to bottom) and column
// (left to right) part. They represent a position in the final
// rendered view (the screen).
// The two pairs of coordinates are translations of the
// points (posX + sizeX, Y, 0) and (posX, posY + sizeY, 0).
// Z is ignored (0), since it does not influence the order.
for (let obj of this.Stage.children) {
obj.leftCol = obj.posY - obj.posX - obj.sizeX;
obj.rightCol = obj.posY - obj.posX + obj.sizeY;
obj.leftRow = obj.posY + obj.posX + obj.sizeX;
obj.rightRow = obj.posY + obj.posX + obj.sizeY;
obj.isoSpritesBehind = [];
}
for(let i = 0; i < this.Stage.children.length; i++) {
let a = this.Stage.children[i];
// Only loop over the next objects
for(let j = i + 1; j < this.Stage.children.length; j++) {
let b = this.Stage.children[j];
// Get the two objects in order of left column:
let c = b.leftCol < a.leftCol ? b : a;
let d = b.leftCol < a.leftCol ? a : b;
// See if they overlap in the view (ignoring Z):
if (d.leftCol < c.rightCol) {
// Determine which is behind: some linear algebra
if (d.leftRow <
(d.leftCol - c.leftCol)/(c.rightCol - c.leftCol)
* (c.rightRow - c.leftRow) + c.leftRow) {
// c is in front of d
c.isoSpritesBehind.push(d);
} else { // d is in front of c
d.isoSpritesBehind.push(c);
}
} // in the else-case it does not matter which one comes first
}
}
// This replaces your visitNode function and call:
this.Stage.children.forEach(function getDepth(obj) {
// If depth was already assigned, this node was already visited
if (!obj.isoDepth) {
// Get depths recursively, and retain the maximum of those.
// Add one more to get the depth for the current object
obj.isoDepth = obj.isoSpritesBehind.length
? 1+Math.max(...obj.isoSpritesBehind.map(getDepth))
: 1; // Depth when there is nothing behind it
}
return obj.isoDepth; // Return it for easier recursion
});
// Sort like you did, but in shorter syntax
this.Stage.children.sort((a, b) => a.isoDepth - b.isoDepth);
}
toString() { // Just print the ids of the children
return JSON.stringify(this.Stage.children.map( x => x.id ));
}
}
const game = new Game();
game.addObject( 2, 2, 0, 1, 1, 1 );
game.addObject( 1, 3, 0, 3, 1, 1 );
game.addObject( 6, 1, 0, 1, 3, 1 );
game.addObject( 9, 3, 0, 1, 1, 1 );
game.addObject( 5, 3, 0, 1, 3, 1 );
game.addObject( 7, 2, 0, 1, 1, 1 );
game.addObject( 8, 2, 0, 3, 1, 1 );
game.topologicalSort();
console.log(game + '');
The objects in the snippet are the same as in the picture with the same numbers. The output order is [0,1,4,2,5,6,3] which is the valid sequence for drawing the objects.

How to correctly detect neighbour cells

As a learning project, I'm writing a simple logical game where the user has html canvas grid where he needs to connect same-colored squares.
I'm stuck at detecting neighbour cell's properties(its colour) and if condition is matched (neighbour colour is different from mine), the cell shouldn't be filled.
My question are:
1) Is there a better way to check if target square is viable for colour-filling?
2) If there is, how do I handle clicks on new cells correctly?
function checkNeighbourTiles(aX, aY) {
var coords = [
[(aX - 1), (aY - 1)],
[(aX - 1), (aY)],
[(aX - 1), (aY + 1)],
[(aX), (aY - 1)],
[(aX), (aY + 1)],
[(aX + 1), (aY - 1)],
[(aX + 1), (aY - 1)],
[(aX + 1), (aY + 1)]
]
for (i = 0; i < coords.length; i++) {
var x = coords[i][0]
var y = coords[i][1]
var b = cells[x][y]
}
}
My code so far - jsfiddle
It depends on the complexity and performance constraints. As you have done a direct lookup table is about as efficient as can be for a simple grid lookup, though instead of creating the lookup array each time just create an offsets array once.
// an array of offset coordinates in pairs x,y 8 pairs skipping the center
const NEIGHBOURS = [-1, -1, 0, -1, 1, -1, -1, 0, 1, 0, -1, 1, 0, 1, 1, 1];
const GIRD_SIZE = 10; // 10 by ten grid
function checkNeighbourTiles(x,y){
var lx, ly, cellResult;
var i = 0;
while(i < 16){ // check for each offset
lx = x + NEIGHBOURS[i++]; // get the x offset
ly = y + NEIGHBOURS[i++]; // get the y offset
// ensure you are inside the grid
if( ly >= 0 && ly < GRID_SIZE && lx >= 0 && lx < GRID_SIZE ){
cellResult = cell[lx][ly];
// do what is needed with the result;
}
}
}
For the type of 2D array that is about simplest way to do it.
The alternative is a linked array were each cell holds and array of references to the neighbouring cells.
Thus (and with simplicity in mind) just the top left right and bottom. Then each cell would look like
cell = {
top : undefined,
left : undefined,
right : undefined,
bottom : undefined,
... other data
}
Then when you add the cell you set the references to the appropriate cells
// first add all the cells to the array
// then for each cell call this
function AddCell(cell,x,y){
cell.top = cells[x][y-1];
cell.left = cells[x-1][y];
cell.right = cells[x+1][y];
cell.bottom = cells[x][y+1];
// also the cells you just reference should also reference back
// where top refs botton and left refs right and so fourth.
cells.top.bottom = cell;
cells.bottom.top = cell;
cells.left.right = cell;
cells.right.left = cell;
}
Then at any point if you want to find which cell is above
//x and y are the cell
var cellAbove = cell[x][y].top;
This method has many advantages when you start getting complex linking, like dead cells, or skipping cells, or even inserting cells so that you change the topology of the grid.
You can also do complex searches like two left one down
resultCall = cell[x][y].left.left.bottom; // returns the cell two left one down
But it is a pain to maintain the links as there is a lot of extra code involved so for a simple 2D grid your method is the best.
A cleaner way for selecting squares might be using loops:
function (x, y){
var coords = [];
for(i = (x-1); i < (x+2); i++){
for(j = (y-1); j < j (y+2); j++){
coords.push([i, j]);
}
}
//rest of the code
}
If you check the square at [0, 0], this code will search for a square at [-1, -1]. You're gonna need some serious if statements for filtering out the proper squares.
Likewise, if you have a 9x9 grid and you search for [8, 8], the code will look for the [9, 9] square eventually and go out of bounds.

How to create a data model for a boat?

How would one create a model for a boat in javascript that exists as a grid reference in a cartesian plane?
I would like to learn javascript by creating clone of the popular game Battleship!
To this end I need assistance in my quest to start programming boats!
Here's something to get you started:
function Boat(name, length) {
this.name = name
this.pegs = new Array(length)
this.sunk = false
}
Boat.prototype.place = function (x, y, orientation) {
// Before calling this method you'd need to confirm
// that the position is legal (on the board and not
// conflicting with the placement of existing ships).
// `x` and `y` should reflect the coordinates of the
// upper-leftmost peg position.
for (var idx = 0, len = this.pegs.length; idx < len; idx++) {
this.pegs[idx] = {x: x, y: y, hit: false}
if (orientation == 'horizontal') x += 1
else y += 1
}
}
Boat.prototype.hit = function (x, y) {
var sunk = true
var idx = this.pegs.length
while (idx--) {
var peg = this.pegs[idx]
if (peg.x == x && peg.y == y) peg.hit = true
// If a peg has not been hit, the boat is not yet sunk!
if (!peg.hit) sunk = false
}
return this.sunk = sunk // this is assignment, not comparison
}
Usage:
var submarine = new Boat('submarine', 3)
submarine.place(2, 6, 'horizontal')
submarine.hit(2, 6) // false
submarine.hit(3, 6) // false
submarine.hit(4, 6) // true
Storing pegs as objects with x, y, and hit keys is not necessarily the best approach. If you wanted to be clever you could, for example, store the upper-leftmost coordinates on the object along with the orientation. Then, the hits could be stored in an array. Something like:
name: 'submarine'
x: 2
y: 6
orientation: 'horizontal'
pegs: [0, 0, 0]
After a hit at (2, 6), the boat's properties would be:
name: 'submarine'
x: 2
y: 6
orientation: 'horizontal'
pegs: [1, 0, 0]
I'd start off by creating an array (or two, one for each side) to hold the boats. This can be pretty simple, and just use the boat number as the array entry for "filled" positions.
My boat model would have a length (n "pegs"), a position (x, y), an orientation (vertical or horizontal), and a hit counter. Another option would be to just store each array position the boat occupies, which would make some stuff a little easier.

Categories

Resources