fix bomb and explode position

This commit is contained in:
Tomas Krejci 2023-07-14 02:05:25 +02:00
parent 2f7df02f61
commit 78b94d300c
15 changed files with 120107 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
node_modules

BIN
favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.0 KiB

28
index.html Normal file
View File

@ -0,0 +1,28 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>ReactGirls Akademie Algoritmy</title>
<link rel="icon" type="image/x-icon" href="/favicon.ico">
<script src="https://cdn.tailwindcss.com"></script>
<script src="p5.js"></script>
<script src="p5.sound.js"></script>
<script src="src/index.js"></script>
<script src="src/ai.js"></script>
<script src="src/game.js"></script>
<script src="src/player.js"></script>
<script src="src/physics.js"></script>
<script src="src/obstacle.js"></script>
<script src="src/bomb.js"></script>
<script src="src/utils.js"></script>
</head>
<body
class="bg-slate-900 min-h-screen max-w-screen max-h-screen overflow-hidden m-0 p-0"
margin: 0;
padding: 0;
overflow: hidden;
></body>
</html>

107125
p5.js Normal file

File diff suppressed because one or more lines are too long

12268
p5.sound.js Normal file

File diff suppressed because one or more lines are too long

6
package-lock.json generated Normal file
View File

@ -0,0 +1,6 @@
{
"name": "OctopusBomb",
"lockfileVersion": 3,
"requires": true,
"packages": {}
}

BIN
sound/explosion.mp3 Normal file

Binary file not shown.

274
src/ai.js Normal file
View File

@ -0,0 +1,274 @@
function makeEnemy(x, y) {
return {
t: "enemy",
x,
y,
lastMove: millis(),
isAlive: true,
};
}
function drawEnemies() {
for (let enemi of enemies) {
let dampingX = 0;
let dampingY = 0;
if (enemi.isMoving || !enemi.isAlive) {
dampingX = Math.sin(millis() / (1800 / gameFrameRate)) * damping;
dampingY = Math.cos(millis() / (1800 / gameFrameRate)) * damping;
}
textSize(cellSize / 1.2);
if (enemi.isAlive) {
text(
"🦈",
enemi.x + dampingX - halfCellSize,
enemi.y + dampingY + yOffsetDraw - halfCellSize
);
if(debug){fill(255, 0, 0);
circle(enemi.x + dampingX, enemi.y + dampingY, 8);}
} else {
text(
"💀",
enemi.x - halfCellSize,
enemi.y + dampingY + yOffsetDraw - halfCellSize
);
}
}
}
function moveEnemies() {
const playerPosition = positionsToCellsIdx({
x: player1.x,
y: player1.y,
});
const playerReversPosition = [playerPosition.yi, playerPosition.xi];
for (let enemi of enemies) {
const enemiPosition = positionsToCellsIdx({
x: enemi.x,
y: enemi.y,
});
const enemiReversPosition = [enemiPosition.yi, enemiPosition.xi];
let pathFindTimer = Math.floor(millis() / 100);
pathFindTimer = pathFindTimer % 2;
if (pathFindTimer === 0) {
//const path = bfs(grid, enemiReversPosition, playerReversPosition);
// fix this
////const path = findPath(grid, enemiReversPosition, playerReversPosition);
}
}
}
//function to judge if a cell is safe or not
safeNeighbor = function (r, c) {
if (r < 0 || r >= rows) return false;
if (c < 0 || c >= cols) return false;
if (grid[r][c] !== freePlace) return false;
return true;
};
//function to identify neighbors of current location
exploreLocation = function (location) {
let r = location.r;
let c = location.c;
let allNeighbors = [];
//left
if (safeNeighbor(r, c - 1)) allNeighbors.push({ r: r, c: c - 1 });
//right
if (safeNeighbor(r, c + 1)) allNeighbors.push({ r: r, c: c + 1 });
//top
if (safeNeighbor(r - 1, c)) allNeighbors.push({ r: r - 1, c: c });
//bottom
if (safeNeighbor(r + 1, c)) allNeighbors.push({ r: r + 1, c: c });
return allNeighbors;
};
//find path between two cells
findPath = function (grid, start, end) {
let gridMap = [];
//var row = grid.length;
//var col = grid[0].length;
var location = {
r: start[0],
c: start[1],
};
var queue = [];
queue.push(location);
while (queue.length) {
var currentLocation = queue.shift();
if (currentLocation.r == end[0] && currentLocation.c == end[1]) {
//return currentLocation;
let paths = [currentLocation];
while (true) {
let r = currentLocation.r;
let c = currentLocation.c;
let parent = grid[r][c].parent;
if (parent == undefined) break;
paths.push(parent);
currentLocation = { r: parent.r, c: parent.c };
}
return paths;
}
grid[currentLocation.r][currentLocation.c].state = "visited";
var neighbors = exploreLocation(currentLocation);
for (neighbor of neighbors) {
if (grid[neighbor.r][neighbor.c].state != "visited") {
queue.push(neighbor);
grid[neighbor.r][neighbor.c]["parent"] = currentLocation;
}
}
}
return false;
};
//Code to traverse, and print path of route
printPath = function (path) {
let paths = [path];
while (true) {
let r = path.r;
let c = path.c;
let parent = grid[r][c].parent;
if (parent == undefined) break;
paths.push(parent);
path = { r: parent.r, c: parent.c };
}
console.log(paths);
};
/*
// openAI:
// Sure, here's an implementation of the breadth-first search algorithm in JavaScript for finding a
// path between two points in a grid:
// */
// function bfs(grid, start, end) {
// //debugger;
// const queue = [start]; // Initialize the queue with the starting point
// const visited = new Set(); // Keep track of visited cells
// const parent = new Map(); // Map each cell to its parent in the search tree
// while (queue.length > 0) {
// let curr = queue.shift(); // Take the first cell from the queue
// visited.add(curr); // Mark it as visited
// // Check if we've reached the end
// if (curr[0] === end[0] && curr[1] === end[1]) {
// // Trace back the path from the end to the start by following the parent pointers
// let path = [];
// let currParent = curr;
// while (currParent !== undefined && currParent !== null) {
// // test on undefined?
// path.unshift(currParent);
// currParent = parent.get(currParent);
// }
// return path;
// }
// // Add unvisited neighbors to the queue
// let neighbors = getNeighbors(grid, curr[0], curr[1]);
// for (const neighbor of neighbors) {
// if (!visited.has(neighbor)) {
// queue.push(neighbor);
// parent.set(neighbor, curr);
// }
// }
// }
// // If we didn't find a path, return null
// return null;
// }
// // Returns an array of unblocked neighbor cells of the given cell
// function getNeighbors(grid, row, col) {
// const numRows = grid.length;
// const numCols = grid[0].length;
// let neighbors = [];
// if (row > 0 && grid[row - 1][col] === freePlace) {
// neighbors.push([row - 1, col]);
// }
// if (col > 0 && grid[row][col - 1] === freePlace) {
// neighbors.push([row, col - 1]);
// }
// if (row < numRows - 1 && grid[row + 1][col] === freePlace) {
// neighbors.push([row + 1, col]);
// }
// if (col < numCols - 1 && grid[row][col + 1] === freePlace) {
// neighbors.push([row, col + 1]);
// }
// return neighbors;
// }
/*
Here, `grid` is a 2D array of objects representing the cells in the grid.
Each object has a `blocked` property that is `true` if the cell is blocked and `false` otherwise.
`start` and `end` are arrays representing the coordinates of the starting and ending cells, respectively.
The function returns an array of cell coordinates representing the shortest path between the starting
and ending cells, or `null` if no path exists.
*/
/*
To use the breadth-first search algorithm to move an object in a grid to a target, you can follow these steps:
1. Create a grid object that represents the state of the grid. Each cell in the grid can be either empty or blocked. You can represent the grid as a 2D array of objects, where each object has a `blocked` property that is `true` if the cell is blocked and `false` otherwise.
2. Create an object that represents the state of the moving object. The object should have a `position` property that is a two-element array representing the current position of the object in the grid.
3. Define a target position that the object needs to reach.
4. Call the `bfs` function with the grid, the starting position of the object, and the target position. This will return an array of cell coordinates representing the shortest path between the starting position and the target position.
5. Move the object one step along the path at a time. For example, you can use `setInterval` to move the object every few milliseconds. The object should move to the next cell in the path until it reaches the target position.
Here's some example code that demonstrates these steps:
javascript
*/
// Define the grid
// const grid = [
// [{ blocked: false }, { blocked: false }, { blocked: false }],
// [{ blocked: false }, { blocked: true }, { blocked: false }],
// [{ blocked: false }, { blocked: false }, { blocked: false }],
// ];
// // Define the moving object
// const object = {
// position: [0, 0], // Starting position
// };
// // Define the target position
// const target = [2, 2];
// Find the shortest path from the object's position to the target
//const path = bfs(grid, object.position, target);
// Move the object along the path
/*
let i = 0;
const interval = setInterval(() => {
if (i < path.length - 1) {
// Move the object to the next cell in the path
object.position = path[i + 1];
i++;
} else {
// Stop the interval if the object has reached the target
clearInterval(interval);
}
}, 1000);
/*
In this example, the `setInterval` function is called every 1000 milliseconds, or 1 second. You can adjust this value to make the object move faster or slower.
Also, note that this example assumes that the object can only move to adjacent cells that are not blocked.
If you want to allow the object to move diagonally or through blocked cells, you'll need to modify the `getNeighbors` function to include those cases.
*/

87
src/bomb.js Normal file
View File

@ -0,0 +1,87 @@
let bombs = [];
let explosions = [];
//let bomb = {x: 0, y: 0}
function placeBomb(x, y) {
let lastBomb = bombs.slice(-1); // return last element
if (lastBomb.length === 0 || lastBomb[0].placeAt < millis() - nextBombTime) {
//snap bomb on grid
if (snapBomb) {
x = Math.round(x / cellSize) * cellSize;
y = Math.round(y / cellSize) * cellSize;
}
let bomb = { x, y, placeAt: millis() };
bombs.push(bomb);
}
}
function drawBombs() {
textSize(cellSize / 1.6);
for (let bomb of bombs) {
text("💣", bomb.x - halfCellSize, bomb.y + yOffsetDraw - halfCellSize);
if(debug){
fill(255, 0, 0);
circle(bomb.x, bomb.y, 8);}
}
}
function detonateBomb() {
for (let bomb of bombs) {
if (bomb && bomb.placeAt < millis() - bombDelay) {
//console.log(bombs.length);
placeMultiExplosion(bomb.x, bomb.y);
bombs.splice(bombs.indexOf(bomb), 1);
explosionSound.play();
}
}
}
function placeMultiExplosion(x, y) {
for (let ix = -explodeSize; ix <= explodeSize; ix++) {
for (let iy = -explodeSize; iy <= explodeSize; iy++) {
const exlodesOverBorder = overBorder({ x: x, y: y });
x = exlodesOverBorder.x;
y = exlodesOverBorder.y;
placeExplosion(ix * cellSize + x, iy * cellSize + y);
}
}
}
function placeExplosion(x, y) {
const exlodeOverBorder = overBorder({ x: x, y: y });
console.log(exlodeOverBorder);
console.log(x);
x = exlodeOverBorder.x;
y = exlodeOverBorder.y;
let explosion = { x, y, placeAt: millis() };
explosions.push(explosion);
const explodePosition = positionsToCellsIdx({
x: x,
y: y,
});
if (grid[explodePosition.yi][explodePosition.xi] === softItem) {
grid[explodePosition.yi][explodePosition.xi] = freePlace;
}
}
function drawExplosions() {
textSize(cellSize / 1);
for (let explosion of explosions) {
text(
"💥",
explosion.x - halfCellSize - 4,
explosion.y + yOffsetDraw - halfCellSize + 4
);
if(debug){fill(255, 0, 0);
circle(explosion.x, explosion.y, 8);}/* */
if (explosion && explosion.placeAt < millis() - expodeDelay) {
//console.log(explosions.lengh);
explosions.splice(explosions.indexOf(explosion), 1);
}
}
}

120
src/game.js Normal file
View File

@ -0,0 +1,120 @@
const debug = false;
const cellSize = 40;
const halfCellSize = cellSize / 2;
const rows = Math.floor(window.innerHeight / cellSize - 1);
const cols = Math.floor(window.innerWidth / cellSize - 1);
const ypx = rows * cellSize;
const xpx = cols * cellSize;
const bombDelay = 1500;
const expodeDelay = 500;
const nextBombTime = 500;
//const overlappedBorder = -Math.floor(halfCellSize); // px
const gameFrameRate = 60;
const steppOnStart = 1;
const yOffsetDraw = cellSize - cellSize / 4;
const offsetCollision = cellSize / 4;
const grid = []; // grid[iy][ix]
const explodeSize = 2;
const softItemRandomFactor = 0.8;
const hardItemRandomFactor = 0.95;
const softItem = 2;
const hardItem = 3;
const freePlace = 0;
const enemies = [];
const damping = 2;
const enemiSpeed = 4;
const snapBomb = false;
let explosionSound;
function setup() {
createCanvas(windowWidth, windowHeight);
//createCanvas(cols * cellSize, rows * cellSize);
explosionSound = loadSound("sound/explosion.mp3");
frameRate(gameFrameRate); // Attempt to refresh at starting FPS
for (let iy = 0; iy < rows; iy++) {
const row = [];
for (let ix = 0; ix < cols; ix++) {
rect(ix * cellSize, iy * cellSize, cellSize);
if (Math.random() > softItemRandomFactor) {
row.push(softItem); // push soft item
//text("🌿", ix * cellSize, iy * cellSize + cellSize - cellSize / 4);
} else if (Math.random() > hardItemRandomFactor) {
row.push(hardItem);
} else {
row.push(freePlace);
}
}
grid.push(row);
}
grid.push([]); // add one free row
// put player on free place
let randPosition; // proc nelze deklarovat uvnitr while???
do {
player1.x = (Math.floor(Math.random() * cols) * cellSize) + cellSize / 2;
player1.y = (Math.floor(Math.random() * rows) * cellSize) + cellSize / 2;
randPosition = positionsToCellsIdx({
x: player1.x,
y: player1.y,
});
console.log(randPosition);
} while (grid[randPosition.yi][randPosition.xi] !== freePlace);
// put enemy on free place
let enemi = { x: 0, y: 0 };
do {
enemi.x = (Math.floor(Math.random() * cols) * cellSize) + cellSize / 2;
enemi.y = (Math.floor(Math.random() * rows) * cellSize) + cellSize / 2;
randPosition = positionsToCellsIdx({
x: enemi.x,
y: enemi.y,
});
console.log(randPosition);
} while (grid[randPosition.yi][randPosition.xi] !== freePlace);
enemies.push(makeEnemy(enemi.x, enemi.y));
}
function draw() {
background(0, 255);
fill(0, 90, 255);
textSize(cellSize / 1.2);
for (let iy = 0; iy < rows; iy++) {
for (let ix = 0; ix < cols; ix++) {
rect(ix * cellSize, iy * cellSize, cellSize);
text(iy, ix);
if (grid[iy][ix] === softItem) {
text("🌿", ix * cellSize, iy * cellSize + yOffsetDraw);
} else if (grid[iy][ix] === hardItem) {
text("🪨", ix * cellSize, iy * cellSize + yOffsetDraw);
}
}
}
//debugger;
// draw bomb
drawBombs();
detonateBomb();
drawExplosions();
// draw
movePlayer();
moveEnemies();
drawPlayer();
drawEnemies();
}

0
src/index.js Normal file
View File

0
src/obstacle.js Normal file
View File

26
src/physics.js Normal file
View File

@ -0,0 +1,26 @@
function isCollidingWithObstractle(destination) {
let xi = destination.xi;
let yi = destination.yi;
const modDestination = overBordFix({x: xi,y: yi});
xi = modDestination.x;
yi = modDestination.y;
//overBorder({x: xi, y: yi}); // error - pixel vs grid input
//console.log(yi, xi);
const content = grid[yi][xi];
if (content === freePlace || content === null) {
return false;
}
return true;
}

118
src/player.js Normal file
View File

@ -0,0 +1,118 @@
const player1 = {
x: 0,
y: 0,
isMoving: false,
isAlive: true,
};
function movePlayer() {
let isMoving = false;
let stepp = Math.floor(deltaTime / 16 + steppOnStart - 1) + 1;
let dx = 0;
let dy = 0;
// 88 - x key - kill player
if (keyIsDown(88)) {
player1.isAlive = false;
let isMoving = true;
}
if (player1.isAlive) {
// ENTER key - drop bomb - space malfunction on any, dell, keyboard
if (keyIsDown(ENTER)) {
placeBomb(player1.x, player1.y);
}
if (keyIsDown(RIGHT_ARROW)) {
dx += 1;
isMoving = true;
}
if (keyIsDown(LEFT_ARROW)) {
dx -= 1;
isMoving = true;
}
if (keyIsDown(DOWN_ARROW)) {
dy += 1;
isMoving = true;
}
if (keyIsDown(UP_ARROW)) {
dy -= 1;
isMoving = true;
}
for (let i = 0; i < stepp; i++) {
const destinationLeftTop = positionsToCellsIdx({
x: player1.x + dx + offsetCollision - halfCellSize,
y: player1.y + dy + offsetCollision - halfCellSize,
});
const destinationRightTop = positionsToCellsIdx({
x: player1.x + dx - offsetCollision + halfCellSize,
y: player1.y + dy + offsetCollision - halfCellSize,
});
const destinationLeftBottom = positionsToCellsIdx({
x: player1.x + dx + offsetCollision - halfCellSize,
y: player1.y + dy - offsetCollision + halfCellSize,
});
const destinationRightBottom = positionsToCellsIdx({
x: player1.x + dx - offsetCollision + halfCellSize,
y: player1.y + dy - offsetCollision + halfCellSize,
});
player1.isMoving = isMoving;
//console.log(destinationLeftTop);
if (
//true
isCollidingWithObstractle(destinationLeftTop) ||
isCollidingWithObstractle(destinationRightTop) ||
isCollidingWithObstractle(destinationLeftBottom) ||
isCollidingWithObstractle(destinationRightBottom)
) {
return;
//console.log(positionsToCellsIdx(player1));
}
player1.x += dx;
player1.y += dy;
const player1overBorder = overBorder({ x: player1.x, y: player1.y });
player1.x = player1overBorder.x;
player1.y = player1overBorder.y;
//console.log(positionsToCellsIdx({
// x: player1.x,
// y: player1.y,
//}))
//console.log(player1overBorder);
console.log([player1.x, player1.y]);
}
}
}
function drawPlayer() {
let dampingX = 0;
let dampingY = 0;
if (player1.isMoving || !player1.isAlive) {
dampingX = Math.sin(millis() / (1800 / gameFrameRate)) * damping;
dampingY = Math.cos(millis() / (1800 / gameFrameRate)) * damping;
}
textSize(cellSize / 1.2);
if (player1.isAlive) {
text("🐙", player1.x + dampingX - halfCellSize, player1.y + dampingY + yOffsetDraw - halfCellSize);
if(debug){fill(0, 255, 0);
circle(player1.x + dampingX, player1.y + dampingY, 8);}
} else {
text("💀", player1.x - halfCellSize, player1.y + dampingY + yOffsetDraw - halfCellSize);
}
}

54
src/utils.js Normal file
View File

@ -0,0 +1,54 @@
function positionsToCellsIdx(position) {
const colIdx = Math.floor(position.x / cellSize);
const rowIdx = Math.floor(position.y / cellSize);
return { xi: colIdx, yi: rowIdx };
}
function overBorder(position) {
let xi = position.x;
let yi = position.y;
if (xi >= xpx) {
xi = xi - xpx;
}
if (xi < 0) {
xi = xpx + xi - 1;
}
if (yi >= ypx) {
yi = yi - ypx;
}
if (yi < 0) {
yi = ypx + yi - 1;
}
return { x: xi, y: yi };
}
function overBordFix(position){
let xi = position.x;
let yi = position.y;
if (yi < 0) {
yi = rows - 1;
}
if (xi < 0) {
xi = cols - 1;
}
if (yi >= rows) {
yi = 0;
}
if (xi >= cols) {
xi = 0;
}
return { x: xi, y: yi };
}