Repro steps:
console.log(SG.snake.links.length);
to see that there are indeed 3 links on the snake. If you look at the grid coordinates of each of the three individually with SG.snake.links[k].pos.x
, SG.snake.links[k].pos.y
for k=1,2,3
you will see that they live on the same coordinate. Source of the problem:
The source has to be my implementation of the function that handles the resizing of the board and the elements on the board. It is
this.rescale = function ( newWidth )
{
// newWidth: new width of the div containing
this.board.setSize(newWidth, newWidth); // set the size of the board to be that of the containing div
var blockWidth = this.board.getSize().width / this.numBlocks; // width in pixels of a block on the board
this.food.elem.remove(); // remove old food element
this.food.elem = this.board.circle(this.food.pos.x * blockWidth + blockWidth / 2, this.food.pos.y * blockWidth + blockWidth / 2, blockWidth / 2).attr('fill', '#cf6a4c'); // create food element to replace old one, in same grid location (see http://raphaeljs.com/reference.html#Paper.circle)
for (var i in this.snake.links)
{
var thisLink = this.snake.links[i];
thisLink.elem.remove(); // remove old link element
thisLink.elem = this.board.rect(thisLink.pos.x * blockWidth, thisLink.pos.y * blockWidth, blockWidth, blockWidth).attr('fill', '#19FF19'); // creata new link to replace old one http://raphaeljs.com/reference.html#Paper.circle
}
}
Code dump:
If you want to see the full logic of the object representing the game, it is below.
// define game object
function Game ( board, numBlocks ) {
// board: Raphael object that the snake will live on
// numBlocks: Number of blocks both horizontally AND vertically -- the grid structure should be squares
this.board = board;
this.numBlocks = numBlocks;
this.snake; // Snake object on the board
this.coords = []; // map whose key-value pairs represent whether a coordinate is open or taken
this.food = null; // food element on board
this.getCoords = function ( )
{
// returns a nested list gridList of all grid coordinates on the canvas,
// acting like a map so that gridList[i,j]=true if the coordinate i,j is
// occupied, and gridList[i,j]=false if the coordinate is not occupied
var gridList = [];
for (var i = 0; i < this.numBlocks; ++i)
{
var innerList = [];
for (var j = 0; j < this.numBlocks; ++j) innerList.push(true);
gridList.push(innerList);
}
return gridList;
}
this.elementOnGrid = function (elem, xpos, ypos) {
// elem: Rapael element (see: http://raphaeljs.com/reference.html#Element)
// xpos, ypos: x and y grid coordinates of the current position of the element
return { elem: elem, pos: { x: xpos, y: ypos } };
}
this.rescale = function ( newWidth )
{
// newWidth: new width of the div containing
this.board.setSize(newWidth, newWidth); // set the size of the board to be that of the containing div
var blockWidth = this.board.getSize().width / this.numBlocks; // width in pixels of a block on the board
this.food.elem.remove(); // remove old food element
this.food.elem = this.board.circle(this.food.pos.x * blockWidth + blockWidth / 2, this.food.pos.y * blockWidth + blockWidth / 2, blockWidth / 2).attr('fill', '#cf6a4c'); // create food element to replace old one, in same grid location (see http://raphaeljs.com/reference.html#Paper.circle)
for (var i in this.snake.links)
{
var thisLink = this.snake.links[i];
thisLink.elem.remove(); // remove old link element
thisLink.elem = this.board.rect(thisLink.pos.x * blockWidth, thisLink.pos.y * blockWidth, blockWidth, blockWidth).attr('fill', '#19FF19'); // creata new link to replace old one http://raphaeljs.com/reference.html#Paper.circle
}
}
this.Snake = function ( game )
{
// game: the Game function/object containing this function
this.links; // list of
this.createNew = function ( )
{
this.links = [];
var blockWidth = game.board.getSize().width / game.numBlocks; // width in pixels of a block on the board
var centerCoordXY = Math.round(game.numBlocks / 2); // x-y grid coordinate of center
for (var i = 0; i < 3; ++i) // start with 3 blocks in the center-ish
{
var newX = centerCoordXY + i;
this.links.push(new game.elementOnGrid(
game.board.rect(newX * blockWidth, centerCoordXY * blockWidth, blockWidth, blockWidth).attr('fill', '#19FF19'), // http://raphaeljs.com/reference.html#Paper.circle
centerCoordXY,
centerCoordXY
) // add element of type elementOnGrid to the links
);
game.coords[newX][centerCoordXY] = false; // indicate that coordinates of element just added to snake is no longer open
}
}
}
this.placeFood = function ( )
{
do {
var randXCoord = randInt(0, this.coords.length), randYCoord = randInt(0, this.coords.length);
}
while (this.coords[randXCoord][randYCoord] === false); // get random unused x-y coordinate
var blockWidth = this.board.getSize().width / this.numBlocks; // width in pixels of a block on the board
if (this.food == null) // if food element hasn't been initialized
{
// initialize the food element
this.food = new this.elementOnGrid(
this.board.circle(randXCoord * blockWidth + blockWidth / 2, randYCoord * blockWidth + blockWidth / 2, blockWidth / 2).attr('fill', '#cf6a4c'), // place circle in random location on the board (see http://raphaeljs.com/reference.html#Paper.circle)
randXCoord,
randYCoord
); // set food to be new element of type elementOnGrid
}
else // food element has been initialized (game is in play)
{
// move the food element
// ...
}
this.coords[randXCoord][randYCoord] = false; // indicate that coordinates of the good element is not open
}
this.startNew = function ( ) {
this.coords = this.getCoords();
this.snake = new this.Snake(this);
this.snake.createNew();
this.placeFood();
}
}
$(function () { // equivalent to $(document).ready(function() {
// div that holds the game area
var snakeBoardHolder = $('#snake-board-holder');
// make it have the same height as width
snakeBoardHolder.height(snakeBoardHolder.width());
// draw canvas for the snake to live on
// http://raphaeljs.com/reference.html#Raphael
if (!Raphael.svg) throw new Error("Your browser does not support SVG elements! Game won't work.");
snakeBoard = Raphael("snake-board-holder", snakeBoardHolder.width(), snakeBoardHolder.height());
// start new snake game
SG = new Game(snakeBoard, 16);
SG.startNew();
// make the game area (div) have height always equal to width,
// and make the Raphel object (canvas) inside it and all its elements
// to be resized proportionally
$(window).resize(function () {
var w = snakeBoardHolder.width();
snakeBoardHolder.height(w);
SG.rescale(w);
});
});
Any help in determing the piece of logic that is causing the bug would be greatly appreciated!
Inside this.Snake, I believe createNew() should be:
this.createNew = function ( )
{
this.links = [];
var blockWidth = game.board.getSize().width / game.numBlocks; // width in pixels of a block on the board
var centerCoordXY = Math.round(game.numBlocks / 2); // x-y grid coordinate of center
for (var i = 0; i < 3; ++i) // start with 3 blocks in the center-ish
{
var newX = centerCoordXY + i;
this.links.push(new game.elementOnGrid(
game.board.rect(newX * blockWidth, centerCoordXY * blockWidth, blockWidth, blockWidth).attr('fill', '#19FF19'), // http://raphaeljs.com/reference.html#Paper.circle
newX,
centerCoordXY
) // add element of type elementOnGrid to the links
);
game.coords[newX][centerCoordXY] = false; // indicate that coordinates of element just added to snake is no longer open
}
}
Inside this.links.push I've replaced an instance of centerCoordXY with newX.
You've got a lot of duplicated data (positions stored in 3 different places and in two different formats?) which is likely to cause issues like this if you fail to keep them all in sync. It may be better to use Canvas rather than SVG. If you're set on SVG, I'd recommend more helper functions. For example, instead of
new game.elementOnGrid(
game.board.rect(newX * blockWidth, centerCoordXY * blockWidth, blockWidth, blockWidth).attr('fill', '#19FF19'), // http://raphaeljs.com/reference.html#Paper.circle
newX,
centerCoordXY
)
consider a function that would allow you to do something like
newGridElement(newX, centerCoordXy, "#19FF19");
Anyway, good luck!
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.