Add method on a list of object - javascript

Ok guys, object developer newbie here. I try to do an animation of falling cube as explain here : Falling animation to fill a webpage
I have some algorithmic issues. I follow the model of a tetris game but I want multiple pixels falling at the same time. So I have a constructor with some methods to move my pixel.
But now I use my constructor to create an array of object like :
var a_player = [];
function addPlayer(pos){
var player = new Player(pos);
a_player.push(player);
}
addPlayer({x: 3, y: 3});
addPlayer({x: 0, y: 0});
And I want to use some public methods like a collide() method :
function collide(arena, player) {
const [m, o] = [player.matrix, player.pos];
for (let y = 0; y < m.length; ++y) {
for (let x = 0; x < m[y].length; ++x) {
if (m[y][x] !== 00 &&
(arena[y + o.y] &&
arena[y + o.y][x + o.x]) !== 0) {
return true;
}
}
}
return false;
}
But I don't know what's the best way to do it. I can use a "for" like
for (i = 0; i < a_player.length; i++){
console.log(a_player[i].pos);
}
but I have to apply it on all my methods, or I can duplicate my method by the number of player I have in my array (but in the ends I want more than 20k players...). So can you help me with that kind of problematic ?

I think this what you are looking for:
function collide(arena) {
const [m, o] = [this.matrix, this.pos];
for (let y = 0; y < m.length; ++y) {
for (let x = 0; x < m[y].length; ++x) {
if (m[y][x] !== 00 &&
(arena[y + o.y] &&
arena[y + o.y][x + o.x]) !== 0) {
return true;
}
}
}
return false;
}
Player.prototype.collide=collide;
for (i = 0; i < a_player.length; i++){
a_player[i].collide(arena)
}

Related

Fast Algorithm That Can Create Ulam Sequence of n Elements

I've been trying to solve this kata on codewars.
I've got an algorithm, but it's apparently too slow to pass the test. It can create sequence of 2450 numbers in just under 1.6 seconds. I don't need the solution but the hint or something to help me to make my algorithm faster.
function ulamSequence(u0, u1, n) {
// create an array with first two elements in it
const seq = [u0, u1];
// create a loop that checks if next number is valid and if it is, push it in seq
num: for (let i = u1 + 1; seq.length < n; i++) {
let sumCount = 0;
for (let k = 0; k < seq.length - 1; k++) {
if (seq.indexOf(i - seq[k]) > k && ++sumCount === 2) { continue num; }
}
sumCount === 1 ? seq.push(i) : "";
}
return seq;
}
Here's an idea: have an array sums so that sums[N] keeps the number of possible summations for N. For example, for U(1,2) sums[3] will be 1 and sums[5] will be 2 (1+4, 2+3). On each step, locate the minimal N so that N > last item and sums[N] == 1. Add N to the result, then sum it with all previous items and update sums accordingly.
function ulam(u0, u1, len) {
let seq = [u0, u1]
let sums = []
let last = u1
sums[u0 + u1] = 1
while (seq.length < len) {
last += 1
while (sums[last] !== 1) {
last += 1
}
for (let n of seq) {
let s = n + last
sums[s] = (sums[s] || 0) + 1
}
seq.push(last)
}
return seq
}
console.time()
ulam(1, 2, 2450)
console.timeEnd()
function ulamSequence(u0, u1, n) {
const seq = [u0, u1];
const set = new Set(seq);
for (let i = u1 + 2; seq.length < n; i++) {
let sumCount = 0;
for (let k = 0; k < seq.length - 1; k++) {
if (set.has(i - seq[k])) {
sumCount++;
if (sumCount === 2) {
continue;
}
}
}
if (sumCount === 1) {
seq.push(i);
set.add(i);
}
}
return seq;
}

Game of Life p5.js trouble updating cells

I've implemented Conway's game of life in p5.js.
I have a function which counts the number of alive neighbours of a given cell, it appears from the tests I did that it works perfectly. That being said I'm having unexpected behaviour when it comes to simulating : it's not following the correct patterns. I start here with 3 aligned squares (a blinker) and it's supposed to rotate but for some reason they just all die.
Wikipedia shows how a blinker should behave : https://en.wikipedia.org/wiki/Conway%27s_Game_of_Life#Examples_of_patterns
You can copy paste the source below into https://editor.p5js.org/ and see for yourself.
I'm suspecting the issue has got something to do with copying of the board (matrice of cells), because an update has to be simultaneous for all cells, hence we keep in memory the previous board and update the current board based on the previous board.
Source :
//size of cell in pixels.
const size = 20;
const canvasWidth = 400;
const canvasHeight = 400;
const n = canvasWidth / size;
const m = canvasHeight / size;
class Cell {
constructor(status, position) {
this.status = status;
this.position = position;
}
}
let board = [];
function setup() {
//initialise matrix.
for(let i=0; i < m; i++) {
board[i] = new Array(n);
}
//fill matrix of cells.
for(let i=0; i < m; i++ ) {
for(let j=0; j < n; j++ ) {
board[i][j] = new Cell(false, [i*size, j*size]);
}
}
console.log("start", floor(m /2), floor(n / 2))
//[x, y] positions.
board[floor(m /2)][floor(n / 2)].status = true;
board[floor(m /2)+1][floor(n / 2)].status = true;
board[floor(m /2)+2][floor(n / 2)].status = true;
createCanvas(canvasWidth, canvasHeight);
frameRate( 1 )
}
//I'm 99.99% sure this function works, all the tests shows it does.
function updateCell(i, j, previousBoard) {
let count = 0;
//check neighbourgh cells.
for(let k=-1; k < 2; k++) {
for(let p=-1; p < 2; p++) {
if( !(i + k < 0 || j + p < 0 || j + p >= n || i + k >= m) ) {
if(k != 0 || p != 0) {
if(previousBoard[i+k][j+p].status === true) {
count++;
}
}
}
}
}
console.log(count)
if((previousBoard[i][j].status === true) && (count < 2 || count > 3)) {
//console.log("false", i, j, count)
board[i][j].status = false;
} else if((count === 3) && (previousBoard[i][j].status === false)) { //if dead but 3 alive neighbours.
//alive
console.log("true", i, j, count)
board[i][j].status = true;
} else if((previousBoard[i][j].status === true) && (count === 2 || count === 3)) {
console.log("true", i, j, count)
board[i][j].status = true;
}
}
function copyMatrix() {
let newMatrix = []
for(let i=0; i < m; i++) {
//console.log(board[i])
newMatrix[i] = board[i].slice()
}
//console.log(newMatrix)
return newMatrix
}
function draw() {
background(220);
//draw rectangles.
for(let i=0; i < m; i++) {
for(let j=0; j < n; j++) {
if (board[i][j].status === true) {
fill('black');
} else {
fill('white');
}
rect(board[i][j].position[0], board[i][j].position[1], size, size);
}
}
//slice copies previous array into a new reference, previousBoard and board are
//now independent.
let previousBoard = copyMatrix();
//updateCell(11, 9, previousBoard) //uncommenting this line creates weird results...
//console.log(previousBoard)
//update all cells based on previousBoard. (we'll modify board based on previous)
for(let i=0; i < m; i++) {
for(let j=0; j < n; j++) {
updateCell(i, j, previousBoard);
}
}
}
<html>
<head>
<Title>Gordon's Game of Life</Title>
<script src = "p5.js"></script>
<script src = "sketch.js"></script>
</head>
<body>
<h1>Gordon's Game of Life</h1>
</body>
</html>
The issue is that copyMatrix needs to do a deep copy.
Change copy matrix to this:
function copyMatrix() {
let newMatrix = []
for(let i=0; i < m; i++) {
newMatrix[i] = [];
for (let j=0;j< n;j++){
newMatrix[i][j] = new Cell(board[i][j].status, board[i][j].position);
}
}
return newMatrix
}
Here is your code with the copyMatrix change and an added glider to help demonstrate behavior.
//size of cell in pixels.
const size = 20;
const canvasWidth = 400;
const canvasHeight = 400;
const n = canvasWidth / size;
const m = canvasHeight / size;
class Cell {
constructor(status, position) {
this.status = status;
this.position = position;
}
}
let board = [];
function setup() {
//initialise matrix.
for(let i=0; i < m; i++) {
board[i] = new Array(n);
}
//fill matrix of cells.
for(let i=0; i < m; i++ ) {
for(let j=0; j < n; j++ ) {
board[i][j] = new Cell(false, [i*size, j*size]);
}
}
board[floor(m /2)][floor(n / 2)].status = true;
board[floor(m /2)+1][floor(n / 2)].status = true;
board[floor(m /2)+2][floor(n / 2)].status = true;
// glider
board[0][0].status = true;
board[0][2].status = true;
board[1][1].status = true;
board[1][2].status = true;
board[2][1].status = true;
createCanvas(canvasWidth, canvasHeight);
frameRate( 1 )
}
function updateCell(i, j, previousBoard) {
let count = 0;
//check neighbourgh cells.
for(let k=-1; k < 2; k++) {
for(let p=-1; p < 2; p++) {
if( !(i + k < 0 || j + p < 0 || j + p >= n || i + k >= m) ) {
if(k != 0 || p != 0) {
if(previousBoard[i+k][j+p].status === true) {
count++;
}
}
}
}
}
if((previousBoard[i][j].status) && (count < 2 || count > 3)) {
board[i][j].status = false;
} else if((count === 3) && (!previousBoard[i][j].status)) { //if dead but 3 alive neighbours.
board[i][j].status = true;
} else if((previousBoard[i][j].status) && (count === 2 || count === 3)) {
board[i][j].status = true;
}
}
function copyMatrix() {
let newMatrix = []
for(let i=0; i < m; i++) {
newMatrix[i] = [];
for (let j=0;j< n;j++){
newMatrix[i][j] = new Cell(board[i][j].status, board[i][j].position);
}
}
return newMatrix
}
function draw() {
background(220);
//draw rectangles.
for(let i=0; i < m; i++) {
for(let j=0; j < n; j++) {
if (board[i][j].status === true) {
fill('black');
} else {
fill('white');
}
rect(board[i][j].position[0], board[i][j].position[1], size, size);
}
}
let previousBoard = copyMatrix();
//updateCell(11, 9, previousBoard) //uncommenting this line creates weird results...
//console.log(previousBoard)
//update all cells based on previousBoard. (we'll modify board based on previous)
for(let i=0; i < m; i++) {
for(let j=0; j < n; j++) {
updateCell(i, j, previousBoard);
}
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.8.0/p5.min.js"></script>
With the glider this setup demonstrates all three possible patterns. We start with a moving spaceship glider and an oscillator and when the glider runs into the oscillator we have a few iterations that break both patterns and then it settles down into a 2x2 still life.

My Code has high complexity. How can I reduce it?

I'm tryting to code a simple game called 'Atom'. It works really fine but I have a little problem. VisualStudio says that the first code below has a complexity of 10 and the 2nd one a complexity of 13. How can I reduce the complexity? Thank y'all in advance :)
checkForWin(){
let winner = true;
for(let i = 0; i < this.fieldSize; i++){
for(let j = 0; j < this.fieldSize; j++){
if(this.htmlBoard[j][i].classList.contains("atom")){
if(this.htmlBoard[j][i].classList.contains("suspectAtom")){
this.setField(j, i, "correct");
}
else{
this.setField(j, i, "wrong");
winner = false
}
}
else if(this.htmlBoard[j][i].classList.contains("suspectAtom")){
if(!this.htmlBoard[j][i].classList.contains("atom")){
this.setField(j, i, "wrong");
winner = false
}
}
}
}
return winner;
},
setBorder() {
for (let y = 0; y < this.fieldSize; y++) {
for (let x = 0; x < this.fieldSize; x++) {
if (
x == y ||
(x === 0 && y === this.fieldSize - 1) ||
(y === 0 && x === this.fieldSize - 1)
) {
continue;
}
if (
y == 0 ||
x == 0 ||
y == this.fieldSize - 1 ||
x == this.fieldSize - 1
) {
this.board[x][y] = "borderField";
this.htmlBoard[x][y].classList.add("borderField");
}
}
}
},
One way to simplify would be by separating the iteration from the conditionals; Visual Studio Code's automatic refactoring will let you extract the function out very easily, so your checkForWin function could end up looking like
checkForWin() {
let winner = true;
for (let i = 0; i < this.fieldSize; i++) {
for (let j = 0; j < this.fieldSize; j++) {
winner = this.checkForWinningCondition(j, i, winner);
}
}
return winner;
}
which now has a cyclomatic complexity of 4. (The extracted function would have an 8)
Another step you can take is to unroll some of those conditionals and see if we can eliminate any extra code. In this case, notice that the line if(!this.htmlBoard[j][i].classList.contains("atom")){ is extraneous because it is already handled by the initial if statement; removing this will bring the extracted function down to a 7.
Performing the same refactoring approach on your 2nd function will yield similar results, and you should end up with a lower total complexity score

Why does a tetris piece fall all at once instead of one at a time?

I am making tetris in JS. When making a block fall, it makes the block reach the bottom of the screen in one draw instead of slowly approaching the bottom. I tried creating a variable that stores the changes to be made so that it only looks at the current board, but no luck. After checking whether the output variable is == to the board, it seems like the board is changing after all, as it returns true. What's going on?
EDIT: I have successfully made a shallow copy of the array. It still falls to the bottom immediately, though. What's going on?
var data = [];
function array(x, text) {
var y = [];
for (var i = 0; i < x-1; i++) {y.push(text);}
return y;
}
for (var i=0; i<20; i++){data.push(array(10, "b"));}
function draw(){
var j;
var i;
var dataOut = [...data];
for (i = 0; i < data.length - 1; i++){
for (j = 0; j < data[i].length; j++){
if (data[i][j] == "a" && data[i + 1][j] == "b" && i < data.length - 1) {
dataOut[i][j] = "b";
dataOut[i + 1][j] = "a";
}
}
}
data = dataOut;
}
data[0][4] = 'a';
draw();
console.log(data);
In JavaScript, Arrays and Objects are passed by reference. So when you do this:
var dataOut = data;
Both of these references point to the same Array. You could clone the Array every time:
var dataOut = JSON.parse(JSON.stringify(data));
Or simply revert your loop, to go from the bottom to the top. I took the liberty of renaming the variables to make this more clear. Try it below:
var chars = {empty: '.', block: '#'},
grid = createEmptyGrid(10, 20);
function createEmptyGrid(width, height) {
var result = [], x, y;
for (y = 0; y < height; y++) {
var row = [];
for (x = 0; x < width; x++) {
row.push(chars.empty);
}
result.push(row);
}
return result;
}
function draw() {
var x, y;
for (y = grid.length - 1; y > 0; y--) {
for (x = 0; x < grid[y].length; x++) {
if (grid[y][x] === chars.empty && grid[y - 1][x] === chars.block) {
grid[y][x] = chars.block;
grid[y - 1][x] = chars.empty;
}
}
}
}
// Just for the demo
var t = 0, loop = setInterval(function () {
draw();
if (grid[0].includes(chars.block)) {
clearInterval(loop);
grid[9] = 'GAME OVER!'.split('');
}
document.body.innerHTML = '<pre style="font-size:.6em">'
+ grid.map(row => row.join(' ')).join('\n')
+ '</pre>';
if (t % 20 === 0) {
grid[0][Math.floor(Math.random() * 10)] = chars.block;
}
t++;
}, 20);

Randomly generate objects in canvas without duplicate or overlap

How do I generate objects on a map, without them occupying the same space or overlapping on a HTML5 Canvas?
X coordinate is randomly generated, to an extent. I thought checking inside the array to see if it's there already, and the next 20 values after that (to account for the width), with no luck.
var nrOfPlatforms = 14,
platforms = [],
platformWidth = 20,
platformHeight = 20;
var generatePlatforms = function(){
var positiony = 0, type;
for (var i = 0; i < nrOfPlatforms; i++) {
type = ~~(Math.random()*5);
if (type == 0) type = 1;
else type = 0;
var positionx = (Math.random() * 4000) + 500 - (points/100);
var duplicatetest = 21;
for (var d = 0; d < duplicatetest; d++) {
var duplicate = $(jQuery.inArray((positionx + d), platforms));
if (duplicate > 0) {
var duplicateconfirmed = true;
}
}
if (duplicateconfirmed) {
var positionx = positionx + 20;
}
var duplicateconfirmed = false;
platforms[i] = new Platform(positionx,positiony,type);
}
}();
I originally made a cheat fix by having them generate in an area roughly 4000 big, decreasing the odds, but I want to increase the difficulty as the game progresses, by making them appear more together, to make it harder. But then they overlap.
In crude picture form, I want this
....[]....[].....[]..[]..[][]...
not this
......[]...[[]]...[[]]....[]....
I hope that makes sense.
For reference, here is the code before the array check and difficulty, just the cheap distance hack.
var nrOfPlatforms = 14,
platforms = [],
platformWidth = 20,
platformHeight = 20;
var generatePlatforms = function(){
var position = 0, type;
for (var i = 0; i < nrOfPlatforms; i++) {
type = ~~(Math.random()*5);
if (type == 0) type = 1;
else type = 0;
platforms[i] = new Platform((Math.random() * 4000) + 500,position,type);
}
}();
EDIT 1
after some debugging, duplicate is returning as [object Object] instead of the index number, not sure why though
EDIT 2
the problem is the objects are in the array platforms, and x is in the array object, so how can I search inside again ? , that's why it was failing before.
Thanks to firebug and console.log(platforms);
platforms = [Object { image=img, x=1128, y=260, more...}, Object { image=img, x=1640, y=260, more...} etc
You could implement a while loop that tries to insert an object and silently fails if it collides. Then add a counter and exit the while loop after a desired number of successful objects have been placed. If the objects are close together this loop might run longer so you might also want to give it a maximum life span. Or you could implement a 'is it even possible to place z objects on a map of x and y' to prevent it from running forever.
Here is an example of this (demo):
//Fill an array with 20x20 points at random locations without overlap
var platforms = [],
platformSize = 20,
platformWidth = 200,
platformHeight = 200;
function generatePlatforms(k) {
var placed = 0,
maxAttempts = k*10;
while(placed < k && maxAttempts > 0) {
var x = Math.floor(Math.random()*platformWidth),
y = Math.floor(Math.random()*platformHeight),
available = true;
for(var point in platforms) {
if(Math.abs(point.x-x) < platformSize && Math.abs(point.y-y) < platformSize) {
available = false;
break;
}
}
if(available) {
platforms.push({
x: x,
y: y
});
placed += 1;
}
maxAttempts -= 1;
}
}
generatePlatforms(14);
console.log(platforms);
Here's how you would implement a grid-snapped hash: http://jsfiddle.net/tqFuy/1/
var can = document.getElementById("can"),
ctx = can.getContext('2d'),
wid = can.width,
hei = can.height,
numPlatforms = 14,
platWid = 20,
platHei = 20,
platforms = [],
hash = {};
for(var i = 0; i < numPlatforms; i++){
// get x/y values snapped to platform width/height increments
var posX = Math.floor(Math.random()*(wid-platWid)/platWid)*platWid,
posY = Math.floor(Math.random()*(hei-platHei)/platHei)*platHei;
while (hash[posX + 'x' + posY]){
posX = Math.floor(Math.random()*wid/platWid)*platWid;
posY = Math.floor(Math.random()*hei/platHei)*platHei;
}
hash[posX + 'x' + posY] = 1;
platforms.push(new Platform(/* your arguments */));
}
Note that I'm concatenating the x and y values and using that as the hash key. This is to simplify the check, and is only a feasible solution because we are snapping the x/y coordinates to specific increments. The collision check would be more complicated if we weren't snapping.
For large sets (seems unlikely from your criteria), it'd probably be better to use an exclusion method: Generate an array of all possible positions, then for each "platform", pick an item from the array at random, then remove it from the array. This is similar to how you might go about shuffling a deck of cards.
Edit — One thing to note is that numPlatforms <= (wid*hei)/(platWid*platHei) must evaluate to true, otherwise the while loop will never end.
I found the answer on another question ( Searching for objects in JavaScript arrays ) using this bit of code to search the objects in the array
function search(array, value){
var j, k;
for (j = 0; j < array.length; j++) {
for (k in array[j]) {
if (array[j][k] === value) return j;
}
}
}
I also ended up rewriting a bunch of the code to speed it up elsewhere and recycle platforms better.
it works, but downside is I have fewer platforms, as it really starts to slow down. In the end this is what I wanted, but its no longer feasible to do it this way.
var platforms = new Array();
var nrOfPlatforms = 7;
platformWidth = 20,
platformHeight = 20;
var positionx = 0;
var positiony = 0;
var arrayneedle = 0;
var duplicatetest = 21;
function search(array, value){
var j, k;
for (j = 0; j < array.length; j++) {
for (k in array[j]) {
if (array[j][k] === value) return j;
}
}
}
function generatePlatforms(ind){
roughx = Math.round((Math.random() * 2000) + 500);
type = ~~(Math.random()*5);
if (type == 0) type = 1;
else type = 0;
var duplicate = false;
for (var d = 0; d < duplicatetest; d++) {
arrayneedle = roughx + d;
var result = search(platforms, arrayneedle);
if (result >= 0) {
duplicate = true;
}
}
if (duplicate = true) {
positionx = roughx + 20;
}
if (duplicate = false) {
positionx = roughx;
}
platforms[ind] = new Platform(positionx,positiony,type);
}
var generatedplatforms = function(){
for (var i = 0; i < nrOfPlatforms; i++) {
generatePlatforms(i);
};
}();
you go big data, generate all possibilities, store each in an array, shuffle the array,
trim the first X items, this is your non heuristic algorithm.

Categories