Im coding Snake from JS via this tutorial: https://medium.com/free-code-camp/think-like-a-programmer-how-to-build-snake-using-only-javascript-html-and-css-7b1479c3339e
But Im not that familiar with Canvas.
In two functions, one for collision and one for creating the food there's the following code that I need explained:
function didGameEnd() {
for (let i = 4; i < snake.length; i++) {
if (snake[i].x === snake[0].x && snake[i].y === snake[0].y) return true
}
const hitLeftWall = snake[0].x < 0;
const hitRightWall = snake[0].x > gameCanvas.width - 10;
const hitToptWall = snake[0].y < 0;
const hitBottomWall = snake[0].y > gameCanvas.height - 10;
return hitLeftWall || hitRightWall || hitToptWall || hitBottomWall
}
In the function didGameEnd Im wondering what this stands for:
"< 0;" & "> gameCanvas.width - 10;" ? Im thinking the coordinates for the canvas but I can't really find a clear answer.
and also:
function createFood() {
foodX = randomTen(15, gameCanvas.width - 10);
foodY = randomTen(15, gameCanvas.height - 10);
generate a new food location
snake.forEach(function isFoodOnSnake(part) {
const foodIsoNsnake = part.x == foodX && part.y == foodY;
if (foodIsoNsnake) createFood();
});
}
And for createFood its also "gameCanvas.width - 10" and why it says 15 before ?
Related
I'm trying to generate random maze using DFS algorithm, and I after watching several videos, I still can't get my head over it. I know how DFS algorithm works, but I have no idea, how to use this algorithm to actually create the maze.
There is the code that I'm using (Recursive Implementation):
const dirRow = [-1,+1,0,0];
const dirCol = [0,0,+1,-1];
// Given a current cell as a parameter
async function recursiveBacktracking(x,y){
// While the current cell has any unvisited neighbour cells
for (let i = 0; i < 4; i++){
let newX = x + dirRow[i];
let newY = y + dirCol[i];
if (newX >= 20 || newX < 0) continue;
if (newY >= 20 || newY < 0) continue;
if (values[newY][newX].visited === true) continue;
// Mark the current cell as visited
values[newY][newX].visited = true;
// Remove the wall between the current cell and the chosen cell
if (values[newY][newX].neighbours[i] != null)
values[newY][newX].neighbours[i].val = -1
// Animation
await renderPath(0,newX + newY*20);
// Choose one of the unvisited neighbours
await recursiveBacktracking(newX,newY);
}
}
I'm trying to follow the steps from the wiki page,
and the part that I don't get, is that I have no idea which wall to remove on my 2d grid.
For the actual grid, I'm using 2d array with Node class on each X.
function Node(val, neighbours){
this.val = val;
this.neighbours = neighbours;
this.visited = false;
}
function createNodeMap(w,h){
let grid = []
for (let y = 0; y < h; y++){
grid.push([]);
for (let x = 0; x < w; x++){
grid[y].push(new Node(0, []));
}
}
for (let y = 0; y < h; y++){
for (let x = 0; x < w; x++){
let neighbours= new Array(4).fill(null);
if (x-1 >= 0) neighbours[0] = grid[y][x-1];
if (x+1 < 20) neighbours[1] = grid[y][x+1];
if (y+1 < 20) neighbours[2] = grid[y+1][x];
if (y-1 >= 0) neighbours[3] = grid[y-1][x];
grid[y][x].neighbours = neighbours;
}
}
return grid;
}
Grid that is created (Grey columns are the walls):
EDIT:
Well the part where I'm marking the cell as part of the maze is wrong, because I'm not actually making any space for the walls to be in.
I have implement JavaScript applying alpha-beta minimax. It works well with 3x3 board, but when I change the board to 4x4 or higher, the program seems to hang.
UPDATE: The program is not efficient when available moves are more than 10
Here is the alpha-beta minimax function:
function AlphaBetaMinMax(game_board, depth, alpha, beta) {
if (CheckForWinner(game_board) === 1 || CheckForWinner(game_board) === 2 ||
CheckForWinner(game_board) === 3)
return GameScore(game_board, depth);
depth += 1;
var availableMoves = GetAvailableMoves(game_board);
var move, result, possible_game;
if (active_turn === "COM") {
for (var i = 0; i < availableMoves.length; i++) {
move = availableMoves[i];
possible_game = GetNewState(move, game_board);
result = AlphaBetaMinMax(possible_game, depth, alpha, beta);
game_board = UndoMove(game_board, move);
if (result > alpha) {
alpha = result;
if (depth == 1)
choice = move;
} else if (alpha >= beta) {
return alpha;
}
}
return alpha;
} else {
for (var i = 0; i < availableMoves.length; i++) {
move = availableMoves[i];
possible_game = GetNewState(move, game_board);
result = AlphaBetaMinMax(possible_game, depth, alpha, beta);
game_board = UndoMove(game_board, move);
if (result < beta) {
beta = result;
if (depth == 1)
choice = move;
} else if (beta <= alpha) {
return beta;
}
}
return beta;
}
}
function GameScore(game_board, depth) {
var score = CheckForWinner(game_board);
var t = (board_size + 1);
if (score === 1)
return 0;
else if (score === 2)
return depth - t;
else if (score === 3)
return t - depth;
}
function UndoMove(game_board, move) {
game_board[move] = UNOCCUPIED;
ChangeTurn();
return game_board;
}
function GetNewState(move, game_board) {
var piece = ChangeTurn();
game_board[move] = piece;
return game_board;
}
function ChangeTurn() {
var piece;
if (active_turn === "COM") {
piece = 'X';
active_turn = "PLAYER";
} else {
piece = 'O';
active_turn = "COM";
}
return piece;
}
function GetAvailableMoves(game_board) {
var AvailableMoves = new Array();
for (var i = 0; i < board_size; i++) {
if (game_board[i] === UNOCCUPIED) {
AvailableMoves.push(i);
}
}
return AvailableMoves;
}
CheckForWinner() returns:
0 for not a tie or not a win
1 for a tie
2 for Player win
3 for Computer win
Thanks for your help
I have successful built a game named Othello (It's almost like GO games) and it same what you are doing but my main language is Java (and you are using JavaScript). So, I will show my pseudocode for you (because I don't know JavaScript). I hope it can help you in this case:
function minimax(turn, max_depth ,depth, alpha, beta):
if isFinishState() or depth==max_depth :
return value of the node
if turn == "computer" :
best = -INFINITY
turn = "Human"
for each move in available moves :
possible_game = GetNewState(move, game_board);
value = minimax(turn, depth+1,max_depth, alpha, beta)
//insert some code to undo if you have changed the game board
best = max( best, value)
alpha = max( alpha, best)
if beta <= alpha:
break
return best
else : //human turn
best = +INFINITY
turn = "Computer"
for each move in available_moves:
possible_game = GetNewState(move, game_board);
value = minimax(turn, depth+1, max_depth, alpha, beta)
//insert some code to undo if you have changed the game board
best = min( best, value)
beta = min( beta, best)
if beta <= alpha:
break
return best
Then, you need a function like findBestMove() to return the best next position for computer:
int findBestMove(int max_depth) :
alpha = -9999999; //-INFINITY
beta = 9999999; //+INFINITY
choice = null;
best =-9999999;//-INFINITY
for each move in available_moves:
possible_game = GetNewState(move, game_board);
moveVal = minimax("computer", 0, max_depth, alpha, beta);
if (best < moveVal):
best = moveVal;
choice = move;
//insert code here to undo game_board
return choice;
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)
}
I am doing my first game snake, but I have a problem which appears sometime when I eat a rectangle and try to generate another my script crashes and I have an infinite loop.
var coincide = false;
var cmpt;
do {
mangey = Math.random();
mangey *= canvas.height;
t = mangey % 20;
mangey -= t;
mangex = Math.random();
mangex *= canvas.width;
t = mangex % 20;
mangex = mangex - t;
for (cmpt = 0; cmpt < snake.length; cmpt++) {
if ((snake[cmpt][0] == mangex) && (snake[cmpt][1] == mangey)) {
coincide = true;
alert(snake);
console.log(mangex,mangey);
}
}
}
while ((coincide) || ((mangex > 480) || (mangex < 0)) || ((mangey > 380) || (mangey < 0)));
When coincide = true;, it never changes, so it is constantly true, hence the infinite loop. So I'd add an else statement:
if ((snake[cmpt][0] == mangex) && (snake[cmpt][1] == mangey))
{
coincide = true;
alert(snake);
console.log(mangex,mangey);
}
else
{
coincide = false;
}
Problem Solved , Thank you for views .
In the previous code : When we have coincide=true it will not change even when the generated rectangle do not have an intersection .
Solution is simple it's just a we suppose that coincide =false before the for loop .
Great :D
Alright so I just tried to cut down on lines of code by changing manually writing out everything into an array.. My problem is that he now teleports and gravity doesnt work...
first of all I have a grid of cell objects which basically are a 32x32 grid "640X480".
These objects are all passed onto an array like so-
var gridcellarray = [750];
gridcellarray[0] = cell0;
gridcellarray[1] = cell1;
gridcellarray[2] = cell2;
and so on for 750 32x32 cells...
Now as for the collision script I have this...
function collisioncheck(obj) {
obj = obj;
for(var i = 0; i < gridcellarray.length; i++){
//really long if statement// sorry...
if ((gridcellarray[i].solid == true) && ((obj.PosY >= gridcellarray[i].y - obj.maskImg.height) && !(obj.PosY >= gridcellarray[i].y ) && !((obj.PosX > gridcellarray[i].x + solidOriginX + solidImg.width/2-5) || (obj.PosX < gridcellarray[i].x - solidOriginX - solidImg.width/2)))){
if(obj.onground == 0){
obj.PosY = gridcellarray[i].y - obj.maskImg.height;
obj.VelY = 0;
obj.onground = 1;
obj.jump = 0;
}
}
else if (obj.PosY >= canvas.height - obj.maskImg.height){
if(obj.onground == 0){
obj.VelY = 0;
obj.onground = 1;
obj.jump = 0;
obj.PosY = canvas.height - obj.maskImg.height;
}
}
else {
obj.VelY += 1;
obj.onground = 0;
}
}
}
now this code worked just fine before If I had manually copied it 750 times for each cell
and the problem is that Now that I have one iteration of it cycling through them as an array it gets confused and teleports me to the bottom and If I try to jump even when not below or on a block it teleports me back to the bottom.
//edit
I think all the variables should explain their purpose by name but if you have any questions please ask.
//edit
All Variables apply the object your checking collision for such as collisioncheck(player)
here is a link to a running demo of the code... Zack Bloom's code is in it and it works great applied to the unposted horizontal collision scripts but vertically, it wont reset and acknowledge your standing on a block ie ongroud = true;
Demo link
//edit
Ok now that Zack pointed out resetting the y to and x amount it helped alot but he is still not able to jump, as the onground variable doesnt want to reset when the collision is detected... oh and the teleporting is due to my upwards script -
//Upwards Collision//
if ((cell.solid == true) && ((obj.PosY >= cell.y - 32) && !(obj.PosY > cell.y+32) && !((obj.PosX > cell.x + solidOriginX + solidImg.width/2-5) || (obj.PosX < cell.x - solidOriginX - solidImg.width/2)))){
if (obj.onground == 0){
obj.VelY += 1;
obj.onground = 0;
obj.PosY = cell.y + obj.maskImg.height-13;
}
}
Any Ideas on how to fix THIS mess above? to stop him from teleporting? It is only meant to check if the top of the collision mask(red rectangle) is touching the block as if trying to jump through it, but it is meant to stop that from happening so you hit your head and fall back down.
Thanks in Advance!
The else if / else really don't belong in the loop at all, they don't evaluate the cell being looped over, but will be triggered many times each time collisioncheck is called.
function collisioncheck(obj) {
for(var i = 0; i < gridcellarray.length; i++){
var cell = gridcellarray[i];
if (cell.solid && ((obj.PosY >= cell.y - obj.maskImg.height) && !(obj.PosY >= cell.y ) && !((obj.PosX > cell.x + solidOriginX + solidImg.width/2-5) || (obj.PosX < cell.x - solidOriginX - solidImg.width/2)))){
if(!obj.onground){
obj.PosY = cell.x - obj.maskImg.height;
obj.VelY = 0;
obj.onground = 1;
obj.jump = 0;
break;
}
}
}
if (obj.PosY >= canvas.height - obj.maskImg.height){
if(!obj.onground){
obj.VelY = 0;
obj.onground = 1;
obj.jump = 0;
obj.PosY = canvas.height - obj.maskImg.height;
}
} else {
obj.VelY += 1;
obj.onground = 0;
}
}
But an even better way of doing it would be to store each gridcell based on where it was, so you just have to look up the gridcells near the ship.
// You only have to do this once to build the structure, don't do it every time you
// need to check a collision.
var gridcells = {};
for (var i=0; i < gridcellarray.length; i++){
var cell = gridcellarray[i];
var row_num = Math.floor(cell.PosX / 32);
var col_num = Math.floor(cell.PosY / 32);
if (!gridcells[row_num])
gridcells[row_num] = {};
gridcells[row_num][col_num] = cell;
}
// Then to check a collision:
function collisioncheck(obj){
// I'm not sure exactly what your variables mean, so confirm that this will equal
// the width of the object:
var obj_width = solidImg.width;
var obj_height = obj.maskImg.height;
var collided = false;
var left_col = Math.floor(obj.PosX / 32),
right_col = Math.floor((obj.PosX + obj_width) / 32),
top_row = Math.floor(obj.PosY / 32),
bottom_row = Math.floor((obj.PosY + obj_height) / 32);
for (var row=top_row; row <= bottom_row; row++){
for (var col=left_col; col <= right_col; col++){
var cell = gridcells[row][col];
if (cell.solid){
collided = true;
if (row == top_row){
// We collided into something above us
obj.VelY = 0;
obj.PosY = cell.PosY + 32;
} else if (row == bottom_row && !obj.onground){
// We collided into the ground
obj.PosY = cell.x - obj_height;
obj.VelY = 0;
obj.onground = 1;
obj.jump = 0;
}
if (col == left_col){
// We collided left
obj.VelX = 0;
obj.PosX = cell.PosX + 32;
} else if (col == right_col){
// We collided right
obj.VelX = 0;
obj.PosX = cell.PosX - obj_width;
}
}
}
}
if (obj.PosY >= canvas.height - obj_height){
if (!obj.onground){
obj.VelY = 0;
obj.onground = 1;
obj.jump = 0;
obj.PosY = canvas.height - obj_height;
}
}
if (!collided){
obj.VelY += 1;
obj.onground = 0;
}
}
Rather than looping through 720 cells each frame, we are only looking at the cells we know we are overlapping. This is much more efficient and easier to read than all the position calculations.
Some comments on your code:
var gridcellarray = [750];
That creates an array with a single member that has a value of 750. I think you are assuming that it is equivalent to:
var gridcellarray = new Array(750);
which creates an array with a length of 750. There is no need to set the size of the array, just initialise it as an empty array and assign values.
var gridcellarray = [];
gridcellarray[0] = cell0;
This replaces the value of the first member with whatever the value of cell0 is.
function collisioncheck(obj) {
obj = obj;
There is no point in the second line, it just assigns the value of obj to obj (so it's redundant).
for(var i = 0; i < gridcellarray.length; i++){
It is much more efficient in most browsers to save the value of gridcellarray.length, otherwise it must be looked up every time (the compiler may not be able to work whether it can cache it), so:
for (var i = 0, iLen = gridcellarray.length; i < iLen; i++) {
It is more efficient to store a reference to gridcellarray[i] rather than look it up every time. Also, you can use a short name since it's only used within the loop so its purpose is easily found:
var c = gridcellarray[i];
so not only will the code run faster, but the if statement has fewer characters (and you might prefer to format it differently):
if ((c.solid == true) &&
((obj.PosY >= c.y - obj.maskImg.height) && !(obj.PosY >= c.y ) &&
!((obj.PosX > c.x + solidOriginX + solidImg.width/2-5) ||
(obj.PosX < c.x - solidOriginX - solidImg.width/2)))) {
Wow, that really is some if statement. Can you break it down into simpler, logical steps?
Ah, Zack has posted an answer too so I'll leave it here.