How do I create bounds of a 2darray tilemap in Javascript? - javascript

I am creating a 2D ASCII game. Currently I have the world (leveldata) stored in a 2D array. Example:
"test2": [
["╔","═","═","═","═","═","═","═","═","═","╗","´","´","´","´","`","´","´","´","´","´"],
["║","▓","▓","▓","▓","▓","▓","▓","▓","▓","║","´","`","`","´","`","´","´","`","´","´"],
["║","▓","▓","▓","▓","▓","▓","▓","▓","▓","║","´","´","´","´","`","´","´","´","´","´"],
["║","▓","▓","▓","▓","▓","▓","▓","▓","▓","║","´","`","´","´","´","´","´","`","´","´"],
["╚","═","═","▓","▓","▓","▓","═","═","═","╝","´","´","´","´","´","`","´","´","´","´"],
["´","´","´","´","´","`","´","´","´","´","`","´","´","`","´","´","`","´","´","´","´"],
["´","´","`","´","´","´","´","´","´","╔","╗","´","´","´","´","´","´","´","`","´","´"],
["´","´","´","´","´","´","´","´","`","║","║","´","`","´","´","`","´","´","`","`","´"],
["´","´","´","´","´","´","´","`","´","║","║","´","´","´","´","`","´","`","`","´","´"],
["´","´","`","´","´","´","´","´","´","║","║","´","´","`","´","´","`","`","´","´","´"],
["´","´","´","´","´","´","`","´","`","║","║","`","´","´","´","´","`","´","´","`","´"]
]
I take this data, replace the tile where the player is and then paste it to the screen, like so:
I want to take this array that I paste to the screen, and crop it around the player with variable
bounds. Essentially remove any tiles that are a certain distance from a box around the player, so that the camera "follows" the player.
The problem is, tiles aren't removing the way I want them to and I am stuck on what I am supposed to do. Here is all I have so far, it takes in map data, bounds, and player position, and dumps out a custom array. I am trying just the upper bounds so far...
// Cropping the game screen using a camera.
function crop(width=5, height=5, x=1, y=1, map=[]) {
let topBound = y-height;
let bottomBound = y + height;
let rowsRemoved = [];
// Loop through and remove rows that are above or below the max.
for (let row = 0; row < map.length; row++) {
if (row < topBound) {
rowsRemoved.push(row);
}
}
for (let i = 0; i < rowsRemoved.length; i++) {
console.log(rowsRemoved[i]);
map.splice(rowsRemoved[i], 1);
}
console.log(rowsRemoved)
// Loop through and remove columns that are above or below the max.
// for (let row = 0; row < map.length; row++) {
// for (let column = 0; column < map[row].length; column++) {
// if (column < x-width || column > x+width) {
// map[row].splice(map[row].indexOf(map[row][column]), 1);
// }
// }
// }
console.log("----")
return map;
}

It turned out it was to slice the array instead of splice it, and take a totally different approach.
// Cropping the game screen using a camera.
function crop(width=5, height=5, x=1, y=1, map=[]) {
// Get the bounds and make sure they stay within the map's bounds.
let topBound = y - height;
if (topBound < 0) {
topBound = 0;
}
let bottomBound = y + height + 1;
let leftBound = x - width;
if (leftBound < 0) {
leftBound = 0;
}
let rightBound = x + width + 1;
let rowsRemoved = [];
// Remove rows that are above or below the max.
map = map.slice(topBound, bottomBound);
console.log(map)
// Remove columns that are above or below the max.
for (let row = 0; row < map.length; row++) {
map[row] = map[row].slice(leftBound, rightBound);
}
console.log("----");
return map;
}

Related

Why is this coodinate-returning function sometimes returning a coordinate outside its supposed restrictions?

As an exercise I'm building a clone of the classic Nibbles/Snake game.
The game area consists of a two-dimensional array (rows and columns), and the player snake consists of another two-dimensional array containing coordinates for each cell in the game area that the snake is currently occupying.
I then have a function to create the apples that the snake is supposed to eat. This function returns a simple array containing an x- and a y-coordinate specifying the game area cell to put the apple. This cell should of course never be one of the cells that the player snake is occupying at the moment when the apple is created.
However, the function I've constructed sometimes creates an apple in a cell that the player snake IS currently occupying.
Can anyone spot the bug?
This is the relevant code:
const rows = 20;
const cols = 26;
const cells = createCells(rows, cols);
let player = [[1,1], [1,2], [1,3], [1,4]];
let apple = createApple(cells, player);
function createCells(rows, cols) {
let cells = new Array(rows);
for (let r = 0; r < cells.length; r++) {
cells[r] = new Array(cols);
}
return cells;
}
function createApple(cells, player) {
let positions = new Array();
for (let r = 0; r < cells.length; r++) {
for (let c = 0; c < cells[r].length; c++) {
for (let p = 0; p < player.length; p++) {
if (!(player[p][0] === r && player[p][1] === c)) {
positions.push([r, c]);
}
}
}
}
let random = Math.floor(Math.random() * positions.length - 1);
let apple = positions[random];
return apple;
}
The bug is in this part of the code:
for (let p = 0; p < player.length; p++) {
if (!(player[p][0] === r && player[p][1] === c)) {
positions.push([r, c]);
}
}
Even for the cells that are occupied by the snake, you are pushing them (player.length - 1) times. So, for the initial setup where player.length is 4, you are pushing every unoccupied cell 4 times, and every occupied cell 3 times. Do you see the bug now?
What you should change in the code? For a given cell (r,c), you must check it with ALL the cells of the player, and push it to positions only and only if there is no match with any of them. Take some time, and try to code it yourself without seeing further below.
bool free=true;
for (let p = 0; p < player.length; p++) {
if (player[p][0] === r && player[p][1] === c) {
free = false;
break;
}
}
if (free) {
positions.push([r, c]);
}

Simulation of mouses moving, don't work

I'm trying to create a simulation of 150 mouses moving inside a 20x20 grid in p5.js (A processing like libary). First I'm spawning 150 mouses random places and everything goes fine. But after I have spawned the mouses I am trying to make them move to one of their neighbors. Instead of moving to one of the neighbors and make the current square empty it stays on the one that it already was one + it moves to the next one so instead of having 150 mouses i suddenly have 300... I have tried changing the code for hours but I can't find the proplem... Here is my code:
var w = 40;
var grid = [];
var mouses = 10;
var mouseAmount = [];
var Mouse;
var current;
function setup() {
createCanvas(800, 800);
cols = floor(width/w)
rows = floor(height/w)
// frameRate(60);
for (var j = 0; j < rows; j++) {
for ( var i = 0; i < cols; i++) {
var cell = new Cells(i,j);
grid.push(cell);
}
}
amount = new Amount;
}
function draw() {
background(51);
for ( var i = 0; i < grid.length; i++) {
grid[i].show();
}
amount.run();
}
function index(i, j) {
if (i < 0 || j < 0 || i > cols-1 || j > rows-1 ) {
return -1;
}
return i + j * cols;
}
function Cells(i, j) {
this.i = i;
this.j = j;
this.active = false;
this.moveCell = function() {
var neighbors = [];
var top = grid[index(i, j -1)];
var right = grid[index(i+1, j)];
var bottom = grid[index(i, j+1)];
var left = grid[index(i-1, j)];
if (top) {
neighbors.push(top)
}
if (right) {
neighbors.push(right)
}
if (bottom) {
neighbors.push(bottom)
}
if (left) {
neighbors.push(left)
}
if(neighbors.length > 0) {
var r = floor(random(0, neighbors.length));
return neighbors[r];
} else {
return undefined;
}
}
this.show = function() {
var x = this.i*w;
var y = this.j*w;
stroke(255);
noFill();
rect(x,y,w,w);
if(this.active == true) {
fill(155, 0, 255, 100)
rect(x, y, w, w)
}
}
}
function Amount() {
this.run = function() {
var r = floor(random(grid.length))
for (var i = 0; i < mouses; i++) {
var mouse = grid[r];
mouseAmount.push(mouse)
}
if (mouseAmount.length < 1499) {
for (var i = 0; i < mouseAmount.length; i++) {
mouseAmount[i].active = true;
}
}
if (mouseAmount.length > 1499) {
Next();
}
}
}
function Next(i,j) {
for (var i = 0; i < mouseAmount.length; i++) {
current = mouseAmount[i];
var nextCell = current.moveCell();
if (nextCell) {
nextCell.active = true;
current.active = false;
current = nextCell;
}
}
}
Thank you in advance :)
I don't really understand exactly what your code is supposed to do, but a few things stand out to me about your code:
Problem One: I don't understand how you're iterating through your grid array. You seem to be iterating over mouseAmount, which seems to hold random cells from the grid for some reason? That doesn't make a lot of sense to me. Why don't you just iterate over the grid array directly?
Problem Two: You then move the cells randomly to a neighbor, but you don't take into account whether the neighbor is already active or not. I'm not sure what you want to happen, but this seems a bit strange.
Problem Three: Usually with simulations like this, you have to copy the next generation into a new data structure instead of modifying the data structure as you step through it.
The biggest problem is that you haven't really explained what you want your code to do, or what this code does instead, or how those two things are different. But if I were you, I'd make the following changes:
Step One: Iterate over your grid array in a more reasonable way. Just iterate over every index and take the appropriate action for every cell. If I were you I would just use a 2D array and use a nested for loop to iterate over it.
Step Two: Make sure your logic for moving to a neighbor is correct. Do you want cells to move to already active cells?
Step Three: Make a copy of the grid before you modify it. Think about it this way: as you iterate over the grid, let's say you move a cell down one row. Then you continue iterating, you'll reach the newly active cell again. In other words, you'll touch the same active cell twice in one generation, which is definitely going to mess you up.
A word of advice: get this working for a single active cell first. It's really hard to tell what's going on since you have so many things going on at one time. Take a step back and make sure it works for one active cell before moving up to having a whole grid.

Can someone explain me this function for creating grid with JS/jQuery?

I am learning jquery/js and I want to create grid made of divs. This is script doing just that, but I cant fully understand it..
function displayGrid (n) {
var size = 960;
var boxSize = (960 - 4*n)/n;
var wrap = $(".wrap").html("");
for (var j = 0; j < n; j++) {
for (var i = 0; i < n; i++) {
wrap.append( $("<div></div>").addClass("square").height(boxSize).width(boxSize) );
}
wrap.append($("<div></div>").css("clear", "both"));
}
}
In html I have empty wrap class.
Also, there is this function which I understand :)
function setGrid() {
var col = prompt("Enter number of columns: ");
displayGrid(col);
clean();
}
Thanks
Here is a basic line by line explanation..
function displayGrid (n) {
var size = 960; //MAX SIZE TO BE COVERED BY ALL DIVS ,EVEN IF THERE ARE N NO OF DIV'S
var boxSize = (960 - 4*n)/n; //FORMULA TO DECIDE WIDTH OF EACH DIV,CAN BE DIFFERENT TOO.
var wrap = $(".wrap").html(""); //THIS IS A DIV PROBABLY WHERE YOU ARE ADDING THE NEW SMALLER DIVS
for (var j = 0; j < n; j++) {
for (var i = 0; i < n; i++) {
//TWO FOR LOOPS ,1 IS FOR LOOP THROUGH ROWS , OTHER FOR COLUMNS.
wrap.append( $("<div></div>").addClass("square").height(boxSize).width(boxSize) );
//THIS APPENDS A BLANK DIV TO DIV WITH CLASS .WRAP, AND ADDS A CLASS SQAURE, AND ALSO WITH WIDTH AND HEIGHT PROPERTY SETS EACH DIVS PROPERTY OF WIDTH AND HEIGHT.
}
wrap.append($("<div></div>").css("clear", "both"));
//THIS SHOULD BE TO MOVE TO NEXT COLUMN.
} }
Here is commented code:
//n-> seems to be the number of times to divide the grid
//1-> will get 1 square
//2-> will get 4 square
//3-> will get 9 square and so on... n^2
function displayGrid (n) {
var size = 960;
//This seems to calculate size of the squares to fit inside the give area
// of 960: 960/4
//-4*n I guess is the border size to remove from each square in order to have an exact match
var boxSize = (960 - 4*n)/n;
//this get the grit container, empties it
var wrap = $(".wrap").html("");
//now print each square on the document
for (var j = 0; j < n; j++) { //columns
for (var i = 0; i < n; i++) { //rows
wrap.append( $("<div></div>").addClass("square").height(boxSize).width(boxSize) );
}
//this is done to go in the next row, since we are using divs...
wrap.append($("<div></div>").css("clear", "both"));
}
}

How can I group and animate individual rows on this raphael canvas?

The following code creates a 10 x 15 grid of 40 pixel squares. I would like to be able to group the squares into rows for animating later. Specifically, I want to animate the rows dropping to the bottom and then apply animated matrix transforms to them. Eventually they will just replicate the same grid at a new location.
<script type="text/javascript">
var squares = [];
var paperHeight = 600;
var paperWidth = 400;
var squareSize = 40;
var cols = paperWidth / squareSize;
var rows = paperHeight / squareSize;
var paper = Raphael($("#grid-test")[0],paperWidth,paperHeight);
for (var i = 0; i < cols; i++) {
// Begin loop for rows
for (var j = 0; j < rows; j++) {
// Scaling up to draw a rectangle at (x,y)
var x = i * squareSize;
var y = j * squareSize;
// For every column and row, a square is drawn at an (x,y) location
paper.rect(x,y,squareSize,squareSize);
}
}
</script>
You can keep track of the elements with a 2 dimensional array [row][column]. Then iterate over each column in a row to get the appropriate elements; then just animate them. Note that you need to swap i/j when bullding; currently you're building [column][row].
http://jsfiddle.net/7ncHM/
var rowsArr = [];
for (var i = 0; i < cols; i++) {
// Begin loop for rows
rowsArr[i] = [];
for (var j = 0; j < rows; j++) {
// Scaling up to draw a rectangle at (x,y)
var x = j * squareSize; // swap i/j
var y = i * squareSize;
// For every column and row, a square is drawn at an (x,y) location
rowsArr[i][j] = paper.rect(x,y,squareSize,squareSize);
// draw and store directly, isn't that elegant? :)
}
}
function animateRow(i) {
var row = rowsArr[i];
for(var j = 0; j < row.length; j++) { // iterate elements in row
row[j].animate({y: 400}, 2000);
}
}
animateRow(2);
You'll have difficulty animating individual rows if you don't store a reference to the squares which make up a row (presumably this is what the unused squares[] array is for...)
Each time you call paper.rect assign the result to an element in squares[]
You could then have an function which takes a given row of your array and applies Raphael's .animate to each square - giving the impression of the whole row being animated.
http://raphaeljs.com/reference.html#Element.animate
In response to your comment - you can add elements to a Raphael set and then animate the set -eg...
var squares = [];
var paperHeight = 600;
var paperWidth = 400;
var squareSize = 40;
var cols = paperWidth / squareSize;
var rows = paperHeight / squareSize;
var paper = Raphael($("#grid-test")[0],paperWidth,paperHeight);
var rowSets = [];
// *** GRID CONSTRUCTION CODE STARTS ***///
// I have swapped the usage of i and j within the loop so that cells
// in a row are drawn consecutively.
// Previously, it was column cells which were drawn
// consecutively
for (var i = 0; i < rows; i++) {
//create a new set which will hold this row
rowSets[i] = paper.set();
squares[i] = [];
// Begin loop for cells in row
for (var j = 0; j < cols; j++) {
// Scaling up to draw a rectangle at (x,y)
var x = j * squareSize;
var y = i * squareSize;
// For every column and row, a square is drawn at an (x,y) location
var newRect = paper.rect(x,y,squareSize,squareSize);
// add the new cell to the row
rowSets[i].push(newRect);
squares[i][j] = newRect;
}
}
// *** GRID CONSTRUCTION CODE ENDS ***
squares[5][5].attr({fill : '#f00'}); // colour an individual cell
rowSets[6].attr({fill : '#00f'}); // colour an entire row
rowSets[6].animate({y : 10 * squareSize},400); // animate an enitre row
This means you could build a set of rows (and of columns) and animate them as individual units.
UPDATE
As a Raphael set is just a way of associating elements together to provide an easy way to apply operations en masse. It does not work in the same way as "Group" in Visio for example - ie They are still individual elements and will be animated independently.
Raphael will not find the centre of the set to do a rotation around. Each element of the set will be rotated around its own centre.
This problem can be solved by using a path to define an entire row at once as an SVG path. However this means that rows are now "one entity" and you will have difficulty addressing one cell, or one column. However you could switch the indiviual cells for a "row path" before performing the transform. The user/player hopefully wouldn't notice.
You can replace the // *** GRID CONSTRUCTION *** code with the following code to build rows as paths... (JSFiddle Demo)
//Build a row path - this path will be used as the basis of all the rows
var sPath = "lpw 0 l0 sq l-pw 0 l0 -sq " //outline of the whole row
for (var cell = 1; cell < cols; cell++){
sPath += "msq 0 l0 sq Z "; //insert an "upright" cell separator
};
sPath = sPath.replace(/sq/g,squareSize).replace(/pw/g,paperWidth);
//we have no 'for' loop for columns now as we are creating entire row .paths
for (var j = 0; j < rows; j++) {
//we apply the 'M' (Move) offset here to the beginning of the path
//This is simply to adjust the Y-offset of the start of the path
//ie to put the the path in a new row position
rowSets[j] = paper.path("M0 " + j * squareSize + sPath);
}

increasing the row number using JavaScript?

I'm trying to increase the row each time around the loop, but it's not increasing is there something wrong with my JavaScript code?
var image= [];
for (var i = 0; i < test.length; i++) {
var avatar = test[i].image; // The profile image
var row =5;
if(i % 2 === 0){
image[i]= Titanium.UI.createImageView({
top:row,
image:avatar
});
win.add(image[i]);
//trying to increase the image
row =row+200;
} else if(i % 2 === 1) {
image[i]= Titanium.UI.createImageView({
top:row,
image:avatar
});
win.add(image[i]);
}
}
Can't say I'm certain of what you're attempting to achieve, but at the beginning of your for iteration you're resting row to 5. You should move your var row=5; declaration to the top with var image[];
You might also consider the short form row+=200;
try to move this line upper outside of the loop :
var image= [];
var row =5;
for (var i = 0; i < test.length; i++) {
...
}
You are initializing row in each start of loop. Try taking your var row = 5 outside of the loop:
var image= [];
var row =5;
for (var i = 0; i < test.length; i++) {
var avatar = test[i].image; // The profile image
...
}

Categories

Resources