Javascript 2D array function - javascript

I have a 2D array, which contains either true or false.
I made a function that returns the number of neighbors (in all 8 directions) in that array, which are true.
But for some unknown reason, it does not work corectly (returns wrong number of neighbors).
(Yes, I'm making Conway's Game of Life.)
function neighbors(seq, x, y) {
var cnt = 0;
try {
if (seq[y-1][x]){
cnt++;
}
}
catch(err) {
}
try {
if (seq[y][x-1]){
cnt++;
}
}
catch(err) {
}
try {
if (seq[y][x+1]){
cnt++;
}
}
catch(err) {
}
try {
if (seq[y+1][x]){
cnt++;
}
}
catch(err) {
}
try {
if (seq[y-1][x+1]){
cnt++;
}
}
catch(err) {
}
try {
if (seq[y-1][x-1]){
cnt++;
}
}
catch(err) {
}
try {
if (seq[y+1][x-1]){
cnt++;
}
}
catch(err) {
}
try {
if (seq[y+1][x+1]){
cnt++;
}
}
catch(err) {
}
return cnt;
}
This code was basically translated from my Python code, that works.

You could take an object with all needed directions for counting.
Then check if the index is a key of the array and check the nested level. Add all and return that value.
function neighbors(seq, i, j) {
var directions = [{ x: 0, y: -1 }, { x: -1, y: -1 }, { x: -1, y: 0 }, { x: -1, y: 1 }, { x: 0, y: 1 }, { x: 1, y: 1 }, { x: 1, y: 0 }, { x: 1, y: -1 }];
return directions.reduce((r, { x, y }) => {
x += i;
y += j;
return r + (x in seq && seq[x][y] || 0);
}, 0);
}

You can simplify function like below.
var seq = [[false,false,false,false],
[false,true,false,false],
[false,true,false,false],
[false,true,false,false]]
function neighbors(seq, x, y) {
var cnt = 0;
var controls = [{x:0,y:1},{x:0,y:-1},
{x:1,y:0},{x:1,y:1},{x:1,y:-1},
{x:-1,y:0},{x:-1,y:1},{x:-1,y:-1}]
for (var index in controls) {
var newX = x+controls[index].x;
var newY = y+controls[index].y;
if(newX >= 0 && newY >= 0 && newX < seq.length && newY < seq[newX].length
&& seq[newX][newY]) {
cnt++;
}
}
return cnt;
}
console.log(neighbors(seq, 0,1))
console.log(neighbors(seq, 1,0))
console.log(neighbors(seq, 2,2))

Related

TensorflowJS model fails to learn

I built a TensorflowJS model that learns how to play a snake game that I made. I've followed everything I found online, but I can get my model to improve even after hundreds of games.
Can anyone show me what I'm doing wrong? Here's my code:
async function main(currentTime) {
window.requestAnimationFrame(main)
aIModelChoice(snakeBody, applePosition)
updateGameState()
draw(gameBoard)
checkIfLost()
if (gameOver) {
await updateAIModel(snakeBody, applePosition, -1, gameOver);
console.log("UPDATING MODEL WITH REWARD -1")
} else if (appleEaten){
await updateAIModel(snakeBody, applePosition, +1, gameOver);
console.log("UPDATING MODEL WITH REWARD +1")
} else {
await updateAIModel(snakeBody, applePosition, -0.1, gameOver);
console.log("UPDATING MODEL WITH REWARD 0")
}
appleEaten = false
if (gameOver) {
reset()
}
}
export function initiateAIModel() {
window.model = tf.sequential();
window.model.add(tf.layers.dense({ units: 64, inputShape: [100], activation: "relu" }));
window.model.add(tf.layers.dense({ units: 32, activation: "relu" }));
window.model.add(tf.layers.dense({ units: 4, activation: "softmax" }));
window.model.compile({
optimizer: tf.train.adam(),
loss: "categoricalCrossentropy",
metrics: ["accuracy"]
});
window.gamma = 0.9;
window.epsilon = 1.0;
let done = false;
console.log("Model initiated");
}
function getState(snakeBody, applePosition) {
const state = [];
let xValues = [];
let yValues = [];
for (let i = 0; i < snakeBody.length; i++) {
xValues.push(snakeBody[i].x);
yValues.push(snakeBody[i].y);
}
for (let i = 0; i < window.gridsize; i++) {
for (let j = 0; j < window.gridsize; j++) {
if (xValues.includes(j) && yValues.includes(i)) {
state.push(1);
} else if (applePosition.x === j && applePosition.y === i) {
state.push(2);
} else {
state.push(0);
}
}
}
return state;
}
export function aIModelChoice(snakeBody, applePosition) {
window.state = getState(snakeBody, applePosition);
let selectedAction
if (Math.random() < window.epsilon) {
// Choose a random action
selectedAction = ["UP", "DOWN", "LEFT", "RIGHT"][Math.floor(Math.random() * 4)];
} else {
// Choose the action with the highest predicted reward
const actionProbs = window.model.predict(tf.tensor2d([window.state])).dataSync();
selectedAction = ["UP", "DOWN", "LEFT", "RIGHT"][tf.argMax(actionProbs).dataSync()[0]];
}
if (selectedAction == "UP" && window.inputDirection.x !== 0) {
window.inputDirection = { x: 0, y: 1 }
} else if (selectedAction == "DOWN" && window.inputDirection.x !== 0) {
window.inputDirection = { x: 0, y: -1 }
} else if (selectedAction == "LEFT" && window.inputDirection.y !== 0) {
window.inputDirection = { x: -1, y: 0 }
} else if (selectedAction == "RIGHT" && window.inputDirection.y !== 0) {
window.inputDirection = { x: 1, y: 0 }
}
}
export async function updateAIModel(snakeBody, applePosition, reward, done) {
const nextState = getState(snakeBody, applePosition);
let target = reward;
if (!done) {
// Predict the future discounted reward
const futureReward = tf.max(window.model.predict(tf.tensor2d([window.state]))).dataSync()[0];
target = reward + window.gamma * futureReward;
}
// Update the model
const targetVec = window.model.predict(tf.tensor2d([window.state])).dataSync();
targetVec[["UP", "DOWN", "LEFT", "RIGHT"].indexOf(window.inputDirection)] = target;
await window.model.fit(tf.tensor2d([window.state]), tf.tensor2d([targetVec]));
if (!done) {
window.state = nextState;
}
window.epsilon *= 0.995;
}
You aren't updating which grid space it can go to, you're updating which direction it turns. This makes it spin in circles instead of checking different places for the apple.

phaser 3 reset the whole scene

Following the phaser documentation, the scene restart is not working.
What I'm doing is the following, there are three scenes (start, play and end.). When it gets to the end, when clicking on the screen it gives this.scene.start('play'). I tried several ways, researched a lot and tried several things but the ones that worked in my case, the game for some events and some settings are not reset.
For example, in this game you will create enemies and some packs. When it is restarted or started again, these builds are no longer executed.
Start
import Phaser from "phaser";
class Start extends Phaser.Scene{
constructor ()
{
super({ key: 'start', active: true });
}
init(){
console.log("- ./node_modules/babel-loader/lib??ref--2-0!./node_modules/vue-loader/lib??vue-loader-options!./pages/game/index.vue?vue&type=script&lang=js&")
console.log("initiated 0418971")
console.log("Unexpected console statement no-console")
console.log("bundle 'client' has 1 warnings")
console.log("Caching create all")
this.cameras.main.setBackgroundColor("#24252A");
}
preload(){
}
create() {
this.helloWorld = this.add.text(
this.cameras.main.centerX,
this.cameras.main.centerY,
"Clique para jogar",
{ font: "40px Arial", fill: "#ffffff" }
);
this.helloWorld.setOrigin(0.5);
this.input.on('pointerup', function (pointer) {
this.scene.start('play');
}, this);
}
update() {
}
}
export default Start;
Play
import Phaser from "phaser";
// import Pack from '~/assets/game/Components/Pack.js'
class Play extends Phaser.Scene {
constructor() {
super({
key: 'play',
active: false
});
}
index = 0;
packs = Phaser.GameObjects.Group;
enemys = Phaser.GameObjects.Group;
spriteLife = [];
config = {
speed: 0,
speedVelocity: 0.3,
maxSpeed: 15,
pack: {
created: 0,
limitcreate: 1,
value: 4.5,
},
enemy: {
speed: 5,
created: 0,
limitcreate: 1,
x: 0,
},
engine: {
temperature: 0,
}
};
init() {
}
preload() {
// Images
this.load.image('grama', require('~/assets/game/assets/grama.jpg'));
this.load.image('road', require('~/assets/game/assets/road-2.png'))
this.load.image('pack', require('~/assets/game/assets/pack.png'));
this.load.image('life', require('~/assets/game/assets/life.png'));
// Sprite sheet
this.load.spritesheet('player', require('~/assets/game/assets/car-sprite.png'), {
frameWidth: 404,
frameHeight: 1009
})
}
createGram() {
let a = 0;
let b = 0;
const totalA = 8;
const totalB = 4;
for (a = 0; a <= totalA; a++) {
for (b = 0; b <= totalB; b++) {
const grama = this.add.sprite(0, 0, 'grama').setScale(0.2);
grama.setPosition(grama.displayWidth * b, grama.displayHeight * a);
}
}
}
createScore() {
this.score = 0;
this.scoreText = this.add.text(15, 45,
`SCORE: 0`, {
font: "20px Arial",
fill: "#ffffff"
}
);
}
createLife() {
this.numberLifes = 3;
this.add.text(15, 15,
`LIFE:`, {
font: "20px Arial",
fill: "#ffffff"
}
);
for (let i = 0; i < this.numberLifes; i++) {
this.spriteLife.push(this.add.image(80 + 30 * i, 25, 'life').setScale(0.09))
}
}
createPlayer() {
this.player = this.physics.add.sprite(0, 0, 'player');
this.player.setOrigin(0.5)
this.player.setScale(0.2);
this.player.setPosition(this.cameras.main.centerX, this.cameras.main.displayHeight - 120);
this.player.setCollideWorldBounds(true);
}
createPack() {
if(this.config.pack.created < this.config.pack.limitcreate){
this.packs.create(Phaser.Math.Between(60, 330), 0, 'pack').setScale(.15).setOrigin(.5);
this.config.pack.created += 1;
}
}
createEnemy() {
if(this.config.enemy.created < this.config.enemy.limitcreate){
this.enemy = this.enemys.create(Phaser.Math.Between(70, 320), -200, 'player').setScale(.2).setOrigin(.5);
this.enemy.anims.play('default', true);
this.config.enemy.created += 1;
this.config.enemy.x = this.enemys.children.entries[0].x;
}
}
createEngineTemperatureBar() {
this.engineTemperatureBar = this.add.graphics();
this.engineTemperatureBar.fillStyle(0xffffff, 1);
this.engineTemperatureBar.fillRect(0, 0, 130, 30);
// Position
this.engineTemperatureBar.x = 350;
this.engineTemperatureBar.y = 830;
this.engineTemperatureBar.angle = -90;
this.engineTemperatureBar.scaleX = 0;
}
myAnims() {
this.anims.create({
key: 'left',
frames: this.anims.generateFrameNumbers('player', {
start: 4,
end: 4
}),
frameRate: 3,
repeat: 0
})
this.anims.create({
key: 'right',
frames: this.anims.generateFrameNumbers('player', {
start: 0,
end: 1
}),
frameRate: 3,
repeat: 0
})
this.anims.create({
key: 'default',
frames: [{
key: 'player',
frame: 2
}],
frameRate: 20
});
}
addPhysics(){
// Add
this.packs = this.physics.add.group();
this.enemys = this.physics.add.group();
// Collider
this.physics.add.collider(this.player, this.packs, this.collectPack, null, this);
this.physics.add.collider(this.player, this.enemys, this.collideEnemy, null, this, this.enemys);
}
create() {
// Camera
this.cameras.main.setBackgroundColor("#24252A");
// Gram
this.createGram();
// Roads
this.roads = this.add.tileSprite((this.game.canvas.clientWidth / 2), 0, 500, 1210, 'road').setOrigin(0.5, 0).setScale(0.7);
// Functions
this.createScore();
this.createLife();
this.createPlayer();
this.myAnims();
this.addPhysics();
this.createEngineTemperatureBar();
// Body
this.packs.enableBody = true;
this.enemys.enableBody = true;
// Cursors
this.cursors = this.input.keyboard.createCursorKeys();
}
update(time) {
this.roads.tilePositionY -= this.config.speed;
this.movePlayerManager();
this.createPack();
this.createEnemy();
this.movePacks();
this.moveEnemy()
this.updateEngineTemperatureBar()
}
movePlayerManager() {
// Left
if (this.cursors.left.isDown) {
this.player.x -= 5;
this.player.anims.play('left', false);
}
// Right
else if (this.cursors.right.isDown) {
this.player.x += 5;
this.player.anims.play('right', false);
}
else {
this.player.anims.play('default', true);
}
// Up
if (this.cursors.up.isDown) {
// Increase speed
if (this.config.speed <= this.config.maxSpeed){
this.config.speed += this.config.speedVelocity;
}
}
if (this.cursors.up.isUp) {
if (this.config.speed > 0){
this.config.speed -= 0.5;
}
else
this.config.speed = 0
}
}
movePacks() {
this.packs.children.each((e) => {
e.y += this.config.speed;
if(e.y > this.game.config.height){
e.destroy();
this.config.pack.created -= 1;
}
})
}
moveEnemy() {
this.enemys.children.each((e) => {
if(this.cursors.up.isDown){
if(this.config.speed < this.config.maxSpeed-1){
e.y -= this.config.maxSpeed;
return
};
e.y += this.config.enemy.speed;
// Move left or right
if(this.config.enemy.x < 150 && e.x < 290){
e.x += 1;
this.enemy.anims.play('right', true);
}
else if(e.x > 90){
e.x -= 1;
this.enemy.anims.play('left', true);
}else{
this.enemy.anims.play('default', true);
}
}else{
if(e.y < -1000) return;
e.y -= this.config.maxSpeed;
}
if(e.y - 200 > this.game.config.height){
e.destroy();
this.config.enemy.created -= 1;
}
})
}
updateEngineTemperatureBar(){
if(this.cursors.up.isDown && this.config.engine.temperature <= 100){
this.config.engine.temperature += 0.1;
}
else if(this.cursors.up.isUp && this.config.engine.temperature < 100){
this.config.engine.temperature += 0.2;
}else{
this.scene.start('end', { motiveText: "O motor do seu carro explodiu." });
}
// change the size of the bar
this.engineTemperatureBar.scaleX = this.config.engine.temperature/100;
}
updateLifes(){
if(this.numberLifes <= 1){
this.scene.start('end', { motiveText: "Suas vidas acabaram." });
}else{
this.spriteLife[this.numberLifes-1].destroy();
this.numberLifes = this.numberLifes -1;
}
}
collectPack(player, pack){
// Destroy pack
pack.disableBody(true, true);
pack.destroy();
// Config
this.config.pack.created -= 1;
// Score
this.score++;
this.scoreText.setText("SCORE: " + this.score);
// Engien temperature bar
if(this.config.engine.temperature > 1)
this.config.engine.temperature -= this.config.pack.value;
}
collideEnemy(player, enemy){
// Life
this.updateLifes();
// Destroy
enemy.destroy();
// Config
this.config.speed = 1;
this.config.enemy.created -= 1;
}
}
export default Play;
End
import Phaser from "phaser";
class End extends Phaser.Scene{
constructor ()
{
super({ key: 'end', active: false });
}
init(data){
this.motiveText = data.motiveText;
}
preload(){
}
create() {
this.helloWorld = this.add.text(
this.cameras.main.centerX,
this.cameras.main.centerY,
"Perdeu!",
{ font: "40px Arial", fill: "#ffffff" }
);
this.helloWorld.setOrigin(0.5);
this.helloWorld = this.add.text(
this.cameras.main.centerX,
this.cameras.main.centerY + 40,
this.motiveText,
{ font: "20px Arial", fill: "#ffffff" }
);
this.helloWorld.setOrigin(0.5);
this.helloWorld = this.add.text(
this.cameras.main.centerX,
this.cameras.main.centerY + 70,
"Clique para jogar novamente.",
{ font: "20px Arial", fill: "#ffffff" }
);
this.helloWorld.setOrigin(0.5);
this.input.on('pointerup', function (pointer) {
this.scene.start("start");
}, this);
}
update() {
}
}
export default End;
Again, the problem is that he doesn't completely rest. Even using the 'restart' methods, destroying the events and restarting them.
The issue is, that your are setting properties on the Scenes classes, like the property config on the Play class. Since these properties are not set in the create or the init function, they will never be reset, on start/
restart.
When the Scenes are restarted, scenes are not recreated, so properties are not reset and the constructor is also not called.
If you need to define / set properties on the Scenes, the best solution would be to always (re)set them in the init or create function, since these functions are called on start or restart.
I would recomend, simply moving the properties from the Play class into the init method like this:
class Play extends Phaser.Scene {
...
init() {
this.index = 0;
this.packs = Phaser.GameObjects.Group;
this.enemys = Phaser.GameObjects.Group;
this.spriteLife = [];
this.config = {
speed: 0,
speedVelocity: 0.3,
maxSpeed: 15,
pack: {
...
},
enemy: {
...
},
engine: {
temperature: 0,
}
};
}
...
}

How can set pointerDown for each of rectangle on Phaser 3

I’m trying to set up the Conway's Game of Life using Phaser.
My question is this: how can I make a Rectangular of the Phaser.geom class contain a click event?
Class Dots:
import 'phaser';
const COLOUR_ALIVE = 0xffffff;
const COLOUR_DEAD = 0x00000;
export class Dots extends Phaser.Geom.Rectangle {
public alive: number;
public fillColor: number;
public id: string;
constructor(scene, x, y, width, height, alive, id?) {
super(x, y, width, height);
this.alive = alive;
if(this.alive == 1){
this.fillColor = COLOUR_ALIVE;
} else {
this.fillColor = COLOUR_DEAD;
}
this.id = id;
console.log(id);
}
public isAlive():boolean{
return (this.alive == 1);
}
public returnAliveValue():number{
return this.alive;
}
public getFillColor(): number{
return this.fillColor;
}
public dead(){
this.alive = 0;
this.fillColor = COLOUR_DEAD;
}
public setAlive(){
this.alive = 1;
this.fillColor = COLOUR_ALIVE;
}
public click(pointer, gameobject){
console.log(pointer, gameobject);
}
}
Class Game:
import 'phaser';
import {Dots} from './classes/Dots'
const square_size = 10;
const pixel_height = 600;
const pixel_width = 800;
const DOTS = 100;
export default class Demo extends Phaser.Scene
{
private alives: Dots[] = [];
private graphics:Phaser.GameObjects.Graphics = null;
public timeElapsed: number;
public maxTime: number;
public cont = 0;
constructor ()
{
super('demo');
this.timeElapsed = 0;
this.maxTime = 1;
}
preload ()
{
}
draw () {
this.graphics = this.add.graphics();
//Afegim les fitxes vives
let pointer = this.input.activePointer;
this.alives.forEach((rectangle:Dots) => {
this.graphics.fillStyle(rectangle.getFillColor(), 1);
this.graphics.fillRectShape(rectangle);
this.graphics.setInteractive({
hitArea: new Phaser.Geom.Rectangle(0, 22, 27, 29),
hitAreaCallback: Phaser.Geom.Rectangle.Contains,
useHandCursor: true
}, (evt, geom) => {
if(pointer.isDown){
console.log(evt, geom);
}
});
});
}
destroy(obj:Phaser.GameObjects.Graphics) {
obj.destroy();
}
intersects(object1:Dots, object2:Dots){
let x = object1.x;
let y = object1.y;
let intersects = false;
if(object2.x == x - square_size && y == object2.y){
//Bloque izquierda
intersects = true;
} else if( object2.x == x + square_size && y == object2.y){
//Bloque derecha
intersects = true;
}
if(object2.y == y - square_size && x == object2.x){
//Bloque superior
intersects = true;
} else if(object2.y == y + square_size && x == object2.x){
//Bloque inferior
intersects = true;
}
if(object2.x == x - square_size && object2.y == y - square_size){
// Bloque izquierda superior
intersects = true;
} else if (object2.x == x - square_size && object2.y == y + square_size){
// Bloque izquierda inferior
intersects = true;
}
if(object2.x == x + square_size && object2.y == y - square_size){
// Bloque derecha superior
intersects = true;
} else if (object2.x == x + square_size && object2.y == y + square_size){
// Bloque derecha inferior
intersects = true;
}
return intersects;
}
searchArrIntersect(){
this.alives.forEach((x) => {
if(x.alive == 1){
let intersections = 0;
this.alives.forEach((y) => {
if(x != y){
let intersects = this.intersects(x, y);
if(intersects){
intersections ++;
}
}
});
if(intersections == 2 || intersections == 3){
x.isAlive();
}
if(intersections >= 3){
x.dead();
}
} else{
//fichas muertas
let intersections = 0;
this.alives.forEach((y) => {
if(x != y){
let intersects = this.intersects(x, y);
if(intersects){
intersections ++;
}
}
});
if(intersections == 3){
x.isAlive();
}
}
});
}
castObjectIntersects(object_search:Dots):Dots{
let dot_intersect = null;
this.alives.forEach((x, index) => {
if(x.x == object_search.x && x.y == object_search.y){
dot_intersect = x;
}
});
return dot_intersect;
}
create ()
{
let positions = [
//cross
// {x: pixel_width/2 - square_size, y: pixel_height/2, alive:1, id:'left'},
// {x: pixel_width/2 + square_size, y: pixel_height/2, alive:1, id:'right'},
// {x: pixel_width/2, y: pixel_height/2, alive:1, id:'center'},
// {x: pixel_width/2, y: pixel_height/2 - square_size, alive:1, id:'up'},
// {x: pixel_width/2, y: pixel_height/2 + square_size, alive:1, id:'down'},
// //borders
// //left
// {x: pixel_width/2 - square_size, y: pixel_height/2 - square_size, alive: 1, id:'left_up'},
// {x: pixel_width/2 - square_size, y: pixel_height/2 + square_size, alive: 1, id:'left_down'},
// //right
// {x: pixel_width/2 + square_size, y: pixel_height/2 - square_size, alive:0, id:'right_up'},
// {x: pixel_width/2 + square_size, y: pixel_height/2 + square_size, alive:0, id:'right_down'},
];
for(let i = 0; i < pixel_width; i+=10){
for(let j = 0; j < pixel_height; j+=10){
positions.push({x: i, y: j, alive:0, id:`${i}-${j}`});
}
}
positions.forEach((obj) => {
this.alives.push(new Dots(this, obj.x, obj.y, square_size, square_size, obj.alive, obj.id));
});
for(let i = 0; i <= DOTS; i++){
let random_length = Math.floor(Math.random() * (this.alives.length - 1 + 1) + 1);
let dot = this.alives[random_length];
dot.setAlive();
}
this.draw();
}
update(time: number, delta: number): void {
let deltaInSecond = delta/1000; // convert it to second
this.timeElapsed = this.timeElapsed + deltaInSecond;
if(this.timeElapsed >= this.maxTime) // if the time elapsed already more than 1 second
{
this.searchArrIntersect();
this.destroy(this.graphics);
this.draw();
// this.maxTime = 1200;
this.timeElapsed = 0;
}
}
}
const config = {
type: Phaser.AUTO,
backgroundColor: '#000000',
width: pixel_width,
height: pixel_height,
render: {
pixelArt: true
},
scale: {
mode: Phaser.Scale.FIT,
autoCenter: Phaser.Scale.CENTER_BOTH
},
scene: Demo
};
const game = new Phaser.Game(config);
You are setting interactive on the graphics serveral time, it the forEach-loop. I think this can be done only once, so you are overriding it, but I'm no expert.
I would set the interactivity once, for the whole region:
this.graphics.setInteractive({ useHandCursor: true,
hitArea: new Phaser.Geom.Rectangle(0, 0, pixel_width, pixel_height),
hitAreaCallback: Phaser.Geom.Rectangle.Contains,
})
And than in the "click event" select the Rectangle/Dot
this.graphics.on( 'pointerdown', function(){
// ...
});
To get the Rectangle/Dot clicked, there are many ways, here is one:
this.graphics.on( 'pointerdown', function(pointer){
let selected = this.alives.find( point => Phaser.Geom.Rectangle.Contains( new
Phaser.Geom.Rectangle(point.x, point.y, point.width, point.height), pointer.worldX, pointer.worldY
));
console.log('pointerover', pointer, selected);
}, this);
btw.:
I would add the graphics once in the create method:
create () {
// ...
this.graphics = this.add.graphics();
this.graphics.setInteractive({
// ...
});
this.graphics.on('pointerdown', () => {
// ...
});
this.draw();
}
and in the draw method just clear the graphics-object.
draw () {
this.graphics.clear();
// ...
}

js mapping function getting ignored

I have this following code in react. it gets called when i move an element. this is an example log when i move an element:
position2 {x: 275, y: 155}
checking collision
check collision finished
it doesnt execute the mapping function. no errors, no warning no crash, everything else runs fine, except this function ignoring the mapping part.
log before the mapping part "checking collision" is present, log after "check collision finished" is also there.
what could be the issue?
function moveItem(position, container) {
fixBounds(position, container);
console.log("position2", position);
if (!checkCollision(position, container)) {
Object.assign(container, {
position
});
}
}
function checkCollision(position, container) {
console.log("checking collision");
let col = false;
containers.map((c) => {
console.log("pos", c.position, position);
if (c.key !== container.key) {
const sizeA = getSize(c);
const sizeB = getSize(container);
const rect1 = {
l: c.position.x,
r: c.position.x + sizeA.w,
t: c.position.y,
b: c.position.y + sizeA.h,
};
const rect2 = {
l: position.x,
r: position.x + sizeB.w,
t: position.y,
b: position.y + sizeB.h,
};
if (checkOverlap(rect1, rect2)) {
col = true;
}
}
});
console.log("check collision finished");
return col;
}
function checkOverlap(rect1, rect2) {
console.log("1", rect1);
console.log("2", rect2);
if (rect1.l >= rect2.r || rect2.l >= rect1.r) {
console.log("no overlap");
return false;
} else if (rect1.t >= rect2.b || rect2.t >= rect1.b) {
console.log("no overlap");
return false;
}
console.log("overlap");
return true;
}
function getSize(container) {
const element = document.getElementById(container.key);
const w = element.offsetWidth;
const h = element.offsetHeight;
return {
w: w,
h: h
};
}

How would I add a level system for the score increasing?

I am making a slight Tetris remake and I wanted to add a leveling system for when my score reaches, for example, 100, the speed that the blocks go down will also increase. How would I go about doing this? I have the entirety of the javascript code right here so please let me know what I can do to fix it:
//-------------------------------------------------------------------------
// base helper methods
//-------------------------------------------------------------------------
function get(id) { return document.getElementById(id); }
function hide(id) { get(id).style.visibility = 'hidden'; }
function show(id) { get(id).style.visibility = null; }
function html(id, html) { get(id).innerHTML = html; }
function timestamp() { return new Date().getTime(); }
function random(min, max) { return (min + (Math.random() * (max - min))); }
function randomChoice(choices) { return choices[Math.round(random(0, choices.length-1))]; }
if (!window.requestAnimationFrame) { // http://paulirish.com/2011/requestanimationframe-for-smart-animating/
window.requestAnimationFrame = window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
window.msRequestAnimationFrame ||
function(callback, element) {
window.setTimeout(callback, 1000 / 60);
}
}
//-------------------------------------------------------------------------
// game constants
//-------------------------------------------------------------------------
var KEY = { ESC: 27, SPACE: 32, LEFT: 37, UP: 38, RIGHT: 39, DOWN: 40 },
DIR = { UP: 0, RIGHT: 1, DOWN: 2, LEFT: 3, MIN: 0, MAX: 3 },
stats = new Stats(),
canvas = get('canvas'),
ctx = canvas.getContext('2d'),
ucanvas = get('upcoming'),
uctx = ucanvas.getContext('2d'),
speed = { start: 0.6, decrement: 0.005, min: 0.1 }, // how long before piece drops by 1 row (seconds)
nx = 10, // width of tetris court (in blocks)
ny = 20, // height of tetris court (in blocks)
nu = 5; // width/height of upcoming preview (in blocks)
//-------------------------------------------------------------------------
// game variables (initialized during reset)
//-------------------------------------------------------------------------
var dx, dy, // pixel size of a single tetris block
blocks, // 2 dimensional array (nx*ny) representing tetris court - either empty block or occupied by a 'piece'
actions, // queue of user actions (inputs)
playing, // true|false - game is in progress
dt, // time since starting this game
current, // the current piece
next, // the next piece
score, // the current score
vscore, // the currently displayed score (it catches up to score in small chunks - like a spinning slot machine)
rows, // number of completed rows in the current game
step; // how long before current piece drops by 1 row
//-------------------------------------------------------------------------
// tetris pieces
//
// blocks: each element represents a rotation of the piece (0, 90, 180, 270)
// each element is a 16 bit integer where the 16 bits represent
// a 4x4 set of blocks, e.g. j.blocks[0] = 0x44C0
//
// 0100 = 0x4 << 3 = 0x4000
// 0100 = 0x4 << 2 = 0x0400
// 1100 = 0xC << 1 = 0x00C0
// 0000 = 0x0 << 0 = 0x0000
// ------
// 0x44C0
//
//-------------------------------------------------------------------------
var i = { size: 4, blocks: [0x0F00, 0x2222, 0x00F0, 0x4444], color: 'cyan' };
var j = { size: 3, blocks: [0x44C0, 0x8E00, 0x6440, 0x0E20], color: 'blue' };
var l = { size: 3, blocks: [0x4460, 0x0E80, 0xC440, 0x2E00], color: 'orange' };
var o = { size: 2, blocks: [0xCC00, 0xCC00, 0xCC00, 0xCC00], color: 'yellow' };
var s = { size: 3, blocks: [0x06C0, 0x8C40, 0x6C00, 0x4620], color: 'lime' };
var t = { size: 3, blocks: [0x0E40, 0x4C40, 0x4E00, 0x4640], color: 'purple' };
var z = { size: 3, blocks: [0x0C60, 0x4C80, 0xC600, 0x2640], color: 'red' };
var p = { size: 3, blocks: [0x0F00, 0x2222, 0x00F0,], color: 'maroon' };
//------------------------------------------------
// do the bit manipulation and iterate through each
// occupied block (x,y) for a given piece
//------------------------------------------------
function eachblock(type, x, y, dir, fn) {
var bit, result, row = 0, col = 0, blocks = type.blocks[dir];
for(bit = 0x8000 ; bit > 0 ; bit = bit >> 1) {
if (blocks & bit) {
fn(x + col, y + row);
}
if (++col === 4) {
col = 0;
++row;
}
}
}
//-----------------------------------------------------
// check if a piece can fit into a position in the grid
//-----------------------------------------------------
function occupied(type, x, y, dir) {
var result = false
eachblock(type, x, y, dir, function(x, y) {
if ((x < 0) || (x >= nx) || (y < 0) || (y >= ny) || getBlock(x,y))
result = true;
});
return result;
}
function unoccupied(type, x, y, dir) {
return !occupied(type, x, y, dir);
}
//-----------------------------------------
// start with 4 instances of each piece and
// pick randomly until the 'bag is empty'
//-----------------------------------------
var pieces = [];
function randomPiece() {
if (pieces.length == 0)
pieces = [i,i,i,i,j,j,j,j,l,l,l,l,o,o,o,o,s,s,s,s,t,t,t,t,z,z,z,z,p,p,p,p];
var type = pieces.splice(random(0, pieces.length-1), 1)[0];
return { type: type, dir: DIR.UP, x: Math.round(random(0, nx - type.size)), y: 0 };
}
//-------------------------------------------------------------------------
// GAME LOOP
//-------------------------------------------------------------------------
function run() {
showStats(); // initialize FPS counter
addEvents(); // attach keydown and resize events
var last = now = timestamp();
function frame() {
now = timestamp();
update(Math.min(1, (now - last) / 1000.0)); // using requestAnimationFrame have to be able to handle large delta's caused when it 'hibernates' in a background or non-visible tab
draw();
stats.update();
last = now;
requestAnimationFrame(frame, canvas);
}
resize(); // setup all our sizing information
reset(); // reset the per-game variables
frame(); // start the first frame
}
function showStats() {
stats.domElement.id = 'stats';
get('menu').appendChild(stats.domElement);
}
function addEvents() {
document.addEventListener('keydown', keydown, false);
window.addEventListener('resize', resize, false);
}
function resize(event) {
canvas.width = canvas.clientWidth; // set canvas logical size equal to its physical size
canvas.height = canvas.clientHeight; // (ditto)
ucanvas.width = ucanvas.clientWidth;
ucanvas.height = ucanvas.clientHeight;
dx = canvas.width / nx; // pixel size of a single tetris block
dy = canvas.height / ny; // (ditto)
invalidate();
invalidateNext();
}
function keydown(ev) {
var handled = false;
if (playing) {
switch(ev.keyCode) {
case KEY.LEFT: actions.push(DIR.LEFT); handled = true; break;
case KEY.RIGHT: actions.push(DIR.RIGHT); handled = true; break;
case KEY.UP: actions.push(DIR.UP); handled = true; break;
case KEY.DOWN: actions.push(DIR.DOWN); handled = true; break;
case KEY.ESC: lose(); handled = true; break;
}
}
else if (ev.keyCode == KEY.SPACE) {
play();
handled = true;
}
if (handled)
ev.preventDefault(); // prevent arrow keys from scrolling the page (supported in IE9+ and all other browsers)
}
//-------------------------------------------------------------------------
// GAME LOGIC
//-------------------------------------------------------------------------
function play() { hide('start'); reset(); playing = true; }
function lose() { show('start'); setVisualScore(); playing = false; }
function setVisualScore(n) { vscore = n || score; invalidateScore(); }
function setScore(n) { score = n; setVisualScore(n); }
function addScore(n) { score = score + n; }
function clearScore() { setScore(0); }
function clearRows() { setRows(0); }
function setRows(n) { rows = n; step = Math.max(speed.min, speed.start - (speed.decrement*rows)); invalidateRows(); }
function addRows(n) { setRows(rows + n); }
function getBlock(x,y) { return (blocks && blocks[x] ? blocks[x][y] : null); }
function setBlock(x,y,type) { blocks[x] = blocks[x] || []; blocks[x][y] = type; invalidate(); }
function clearBlocks() { blocks = []; invalidate(); }
function clearActions() { actions = []; }
function setCurrentPiece(piece) { current = piece || randomPiece(); invalidate(); }
function setNextPiece(piece) { next = piece || randomPiece(); invalidateNext(); }
function reset() {
dt = 0;
clearActions();
clearBlocks();
clearRows();
clearScore();
setCurrentPiece(next);
setNextPiece();
}
function update(idt) {
if (playing) {
if (vscore < score)
setVisualScore(vscore + 1);
handle(actions.shift());
dt = dt + idt;
if (dt > step) {
dt = dt - step;
drop();
}
}
}
function handle(action) {
switch(action) {
case DIR.LEFT: move(DIR.LEFT); break;
case DIR.RIGHT: move(DIR.RIGHT); break;
case DIR.UP: rotate(); break;
case DIR.DOWN: drop(); break;
}
}
function move(dir) {
var x = current.x, y = current.y;
switch(dir) {
case DIR.RIGHT: x = x + 1; break;
case DIR.LEFT: x = x - 1; break;
case DIR.DOWN: y = y + 1; break;
}
if (unoccupied(current.type, x, y, current.dir)) {
current.x = x;
current.y = y;
invalidate();
return true;
}
else {
return false;
}
}
function rotate() {
var newdir = (current.dir == DIR.MAX ? DIR.MIN : current.dir + 1);
if (unoccupied(current.type, current.x, current.y, newdir)) {
current.dir = newdir;
invalidate();
}
}
//This is how we make the piece drop down and place:
function drop() {
if (!move(DIR.DOWN)) {
addScore(10);
dropPiece();
removeLines();
setCurrentPiece(next);
setNextPiece(randomPiece());
clearActions();
if (occupied(current.type, current.x, current.y, current.dir)) {
lose();
}
}
}
function dropPiece() {
eachblock(current.type, current.x, current.y, current.dir, function(x, y) {
setBlock(x, y, current.type);
});
}
function removeLines() {
var x, y, complete, n = 0;
for(y = ny ; y > 0 ; --y) {
complete = true;
for(x = 0 ; x < nx ; ++x) {
if (!getBlock(x, y))
complete = false;
}
if (complete) {
removeLine(y);
y = y + 1; // recheck same line
n++;
}
}
if (n > 0) {
addRows(n);
addScore(100*Math.pow(2,n-1)); // 1: 100, 2: 200, 3: 400, 4: 800
}
}
function removeLine(n) {
var x, y;
for(y = n ; y >= 0 ; --y) {
for(x = 0 ; x < nx ; ++x)
setBlock(x, y, (y == 0) ? null : getBlock(x, y-1));
}
}
//-------------------------------------------------------------------------
// RENDERING
//-------------------------------------------------------------------------
var invalid = {};
function invalidate() { invalid.court = true; }
function invalidateNext() { invalid.next = true; }
function invalidateScore() { invalid.score = true; }
function invalidateRows() { invalid.rows = true; }
function draw() {
ctx.save();
ctx.lineWidth = 1;
ctx.translate(0.5, 0.5); // for crisp 1px black lines
drawCourt();
drawNext();
drawScore();
drawRows();
ctx.restore();
}
function drawCourt() {
if (invalid.court) {
ctx.clearRect(0, 0, canvas.width, canvas.height);
if (playing)
drawPiece(ctx, current.type, current.x, current.y, current.dir);
var x, y, block;
for(y = 0 ; y < ny ; y++) {
for (x = 0 ; x < nx ; x++) {
if (block = getBlock(x,y))
drawBlock(ctx, x, y, block.color);
}
}
ctx.strokeRect(0, 0, nx*dx - 1, ny*dy - 1); // court boundary
invalid.court = false;
}
}
function drawNext() {
if (invalid.next) {
var padding = (nu - next.type.size) / 2; // half-complete attempt at centering next piece display
uctx.save();
uctx.translate(0.5, 0.5);
uctx.clearRect(0, 0, nu*dx, nu*dy);
drawPiece(uctx, next.type, padding, padding, next.dir);
uctx.strokeStyle = 'black';
uctx.strokeRect(0, 0, nu*dx - 1, nu*dy - 1);
uctx.restore();
invalid.next = false;
}
}
function drawScore() {
if (invalid.score) {
html('score', ("00000" + Math.floor(vscore)).slice(-5));
invalid.score = false;
}
}
function drawRows() {
if (invalid.rows) {
html('rows', rows);
invalid.rows = false;
}
}
function drawPiece(ctx, type, x, y, dir) {
eachblock(type, x, y, dir, function(x, y) {
drawBlock(ctx, x, y, type.color);
});
}
function drawBlock(ctx, x, y, color) {
ctx.fillStyle = color;
ctx.fillRect(x*dx, y*dy, dx, dy);
ctx.strokeRect(x*dx, y*dy, dx, dy)
}
//-------------------------------------------------------------------------
// FINALLY, lets run the game
//-------------------------------------------------------------------------
run();
You have a function called addScore that seems to be used to update the user's score. You could add a conditional block inside this function that will update the game speed when the new score is above the threshold you choose. Alternatively, if you would prefer to keep addScore purely focused on updating the value of the score, you could add this conditional block wherever you're calling addScore.

Categories

Resources