Posted: Tue Jan 11, 2005 6:31 pm Post subject: Help with Snake Game
Hey, I am making a snake game for my FP with my friend. I have gotten it so that the snake moves continuoulsy in the direction last hit by the user, it can eat an orange, and get bigger, however it does not get bigger properly and I can't get it so that when you turn just he front of the snake bends and hte back follows the path.
Me and my compsci teacher are stumped as to what we need to do. Here is the code:
code:
%declare variables here
var move : array char of boolean %what direction the user wants to move in
var snakeX : array 1 .. 15 of int
var snakeY : array 1 .. 15 of int
var snakeSizeX : array 1 .. 15 of int
var snakeSizeY : array 1 .. 15 of int
var snakeDirection : array 1 .. 15 of string
var segmentCount : int
var ox, oy : int
var sleft, sright, stop, sbottom : int
var oleft, oright, otop, obottom : int
var collisionOrange : boolean := false
var ans : string (1)
var totalsize : int := 20 %size of the snake
var speed : int := 1
Draw.FillBox (ox, oy, ox + 20, oy + 20, 4)
%get the direction
Input.KeyDown (move)
if move (KEY_UP_ARROW) then
for i : 1 .. segmentCount - 1
snakeDirection (i + 1) := snakeDirection (i)
end for
snakeDirection (1) := "up"
if snakeSizeY (1) < totalsize then
snakeSizeY (1) += 1
if snakeDirection (segmentCount) = "up" or snakeDirection (segmentCount) = "down" then
snakeSizeY (segmentCount) -= 1
else
snakeSizeX (segmentCount) -= 1
end if
end if
elsif move (KEY_DOWN_ARROW) then
for i : 1 .. segmentCount - 1
snakeDirection (i + 1) := snakeDirection (i)
end for
snakeDirection (1) := "down"
if snakeSizeY (1) < totalsize then
snakeSizeY (1) += 1
if snakeDirection (segmentCount) = "up" or snakeDirection (segmentCount) = "down" then
snakeSizeY (segmentCount) -= 1
else
snakeSizeX (segmentCount) -= 1
end if
end if
elsif move (KEY_RIGHT_ARROW) then
for i : 1 .. segmentCount - 1
snakeDirection (i + 1) := snakeDirection (i)
end for
snakeDirection (1) := "right"
if snakeSizeX (1) < totalsize then
snakeSizeX (1) += 1
if snakeDirection (segmentCount) = "up" or snakeDirection (segmentCount) = "down" then
snakeSizeY (segmentCount) -= 1
else
snakeSizeX (segmentCount) -= 1
end if
end if
elsif move (KEY_LEFT_ARROW) then
for i : 1 .. segmentCount - 1
snakeDirection (i + 1) := snakeDirection (i)
end for
snakeDirection (1) := "left"
if snakeSizeX (1) < totalsize then
snakeSizeX (1) += 1
if snakeDirection (segmentCount) = "up" or snakeDirection (segmentCount) = "down" then
snakeSizeY (segmentCount) -= 1
else
snakeSizeX (segmentCount) -= 1
end if
end if
end if
%allow snake to continue in direction
if snakeDirection (segmentCount) = "up" then
for i : 1 .. segmentCount - 1
snakeY (i + 1) := snakeY (i)
snakeX (i + 1) := snakeX (i)
end for
if segmentCount = 2 then
snakeY (2) := snakeY (1)
snakeX (2) := snakeX (1)
end if
snakeY (1) += speed
elsif snakeDirection (segmentCount) = "right" then
for i : 1 .. segmentCount - 1
snakeX (i + 1) := snakeX (i)
snakeY (i + 1) := snakeY (i)
end for
if segmentCount = 2 then
snakeX (2) := snakeX (1)
snakeY (2) := snakeY (1)
end if
snakeX (1) += speed
elsif snakeDirection (segmentCount) = "down" then
for i : 1 .. segmentCount - 1
snakeX (i + 1) := snakeX (i)
snakeY (i + 1) := snakeY (i)
end for
if segmentCount = 2 then
snakeY (2) := snakeY (1)
snakeX (2) := snakeX (1)
end if
snakeY (1) -= speed
elsif snakeDirection (segmentCount) = "left" then
for i : 1 .. segmentCount - 1
snakeX (i + 1) := snakeX (i)
snakeY (i + 1) := snakeY (i)
end for
if segmentCount = 2 then
snakeX (2) := snakeX (1)
snakeY (2) := snakeY (1)
end if
snakeX (1) -= speed
end if
%set the right left top and bottom
sleft := snakeX (1)
sright := snakeX (1) + totalsize
sbottom := snakeY (1)
stop := snakeY (1) + 20
oleft := ox
oright := ox + 20
obottom := oy
otop := oy + 20
if stop > obottom and sbottom < otop then
if sright > oleft and sleft < oright then
collisionOrange := true
end if
end if
for i : 1 .. segmentCount
Draw.FillBox (snakeX (i), snakeY (i), snakeX (i) + snakeSizeX (i), snakeY (i) + snakeSizeY (i), 10)
end for
delay (10)
cls
exit when collisionOrange = true
end loop
totalsize += 10
a few things my teacher helped me with I don't understand 100% but most of it I do.
Sponsor Sponsor
Cervantes
Posted: Tue Jan 11, 2005 8:13 pm Post subject: (No subject)
I hope you realize that's not what Snake is supposed to look like. Are you trying to go for this, new, style of Snake? Also, Snake is supposed to work on a grid, such that each "body part" of the snake is the size of one grid square. Right now, you seem to be doing it without a grid (I guess you could say you're grid size is 1).
There's some Snake games on compsci.ca, search for them. There's also a ton of help threads regarding snake right now, they might contain useful inforamtion. If not, let us know
josh
Posted: Tue Jan 11, 2005 8:16 pm Post subject: (No subject)
It does not look like snake because It is not working yet.
it can't get the snake to bend properly and follow the head, and I can't figure out why both sides of the square get larger
Cervantes
Posted: Tue Jan 11, 2005 9:09 pm Post subject: (No subject)
When in doubt, refer to Zylum's source.
zylum:
proc moveSnake
var tempx, tempy : int
if snake (1).x = foodx and snake (1).y = foody then
score += scoreInc
moveFood
new snake, upper (snake) + 1
end if
tempx := snake (1).x
tempy := snake (1).y
for decreasing i : upper (snake) .. 2
snake (i).x := snake (i - 1).x
snake (i).y := snake (i - 1).y
end for
snake (1).x := tempx + snkData.dx
snake (1).y := tempy + snkData.dy
end moveSnake
Also, note the use of new. He's using a flexible array (in conjunction with records) to get the job done. You should probably do the same, because, otherwise, you'll get an error when the snake gets more than 15 parts (you've got your arrays 1 .. 15, currently).
Posted: Tue Jan 11, 2005 9:19 pm Post subject: (No subject)
I actually just finished looking at that and I have sent him a PM.
I also just read the tutorial on records a few hours ago and was considering htem using them, adnd I also read the one on flexibl arrays.
Tomoorow I am gunna try and start from scratch to get one that works on a grid system. I just don't undersntandt he part in zylums codenear the end witht the three drawfillbox's.
Amreen
Posted: Wed Jan 12, 2005 9:32 am Post subject: (No subject)
kool
josh
Posted: Wed Jan 12, 2005 2:09 pm Post subject: (No subject)
uh...not really but thanx n e way.
I have totally re-wrote it with my partner, it still does not look like the traditional snake (it is not on a grid) but it will be o.k. for handing in. I am definnatly gunna try and make an actual good version of snake when i don't have the time constraints imposed by school.
The only thing I am having trouble with is the collision detection because they are circles and they are not on a grid.
Cervantes
Posted: Wed Jan 12, 2005 6:45 pm Post subject: (No subject)
Circle collision is easy.
code:
if Math.Distance (x1, y1, x2, y2) < radius1 + radius 2 then
collision := true
end if
As for the three drawfillboxes in zylums code:
the first is for drawing that green background;
the second is for drawing the food;
the third is for drawing the snake.
What's probably confusing you is all the variables inside the draw commands. Really, it's rather simple. Just understand that the variables for the locations of the snake / food etc. are very small, like 1 to 20 or something. So, he multiplies it by a constant (gridData.width) to, essentially, enlarge it.
var grid : array 0 .. gridX - 1, 0 .. gridY - 1 of boolean % - 1 because the array starts from 0, not 1
for ix : 0 .. upper (grid, 1)
for iy : 0 .. upper (grid, 1)
grid (ix, iy) := false
end for
end for
%just to show the grid in action, make some squares "true". Later, "true" squares will be filled in.
for i : 1 .. 10
grid (Rand.Int (0, upper (grid, 1)), Rand.Int (0, upper (grid, 1))) := true
end for
%draw the lines of the grid
for x : 0 .. upper (grid, 1) + 1 %add one to draw the last line. Try taking it away.
drawline (x * gridWidth + gridOffsetX1, gridOffsetY1, x * gridWidth + gridOffsetX1, gridOffsetY2, black)
end for
for y : 0 .. upper (grid, 2) + 1
drawline (gridOffsetX1, y * gridHeight + gridOffsetY1, gridOffsetX2, y * gridHeight + gridOffsetY1, black)
end for
%colour the "true" squares black
for x : 0 .. upper (grid, 1)
for y : 0 .. upper (grid, 1)
if grid (x, y) then
drawfillbox (x * gridWidth + gridOffsetX1, y * gridHeight + gridOffsetY1, x * gridWidth + gridWidth + gridOffsetX1, y * gridHeight + gridHeight + gridOffsetY1, black)
end if
end for
end for
Sponsor Sponsor
josh
Posted: Wed Jan 12, 2005 6:48 pm Post subject: (No subject)
thanks for the help cervantes. It was the variables that where confusing me, what is the advantage though of using smaller variables and multiplying by a constant?
I have never used the Math functions before so I was going to use the method that tony wrote about in a tutorial in the turing section, but this looks alot easier
Cervantes
Posted: Wed Jan 12, 2005 7:14 pm Post subject: (No subject)
The advantage of using smaller variables and multiplying by a constant:
1.) It's easier to understand, for yourself. Would you rather be thinking about 175 being one grid more than 150, or 7 being one grid space more than 6.
2.) It's easier for comparasin's, for collision. Same reason as above.
If you are going for the grid style, as opposed to the grid size one style (that you had at first), you're going to either store information in terms of large numbers and divide by a constant to compare them (or compare them as is, but that's annoying too), or store information as small numbers, compare them easily, and then multiply by a constant to draw them to the screen.
It's somewhat hard to explain. I hope I did a semi-decent job
-Cervantes
cool dude
Posted: Wed Jan 12, 2005 7:15 pm Post subject: (No subject)
well, well, well, always wanting to go your way josh (lol). i told u thats not how snake looks and we should go my way.
code:
% Declare variables
type snakeDat :
record
x : int
y : int
end record
var snake : flexible array 1 .. 2 of snakeDat
var key : string (1) %the users key button variable
var move : string := "MOVE" %variable for the way the snake is moving
var ox, oy : int %holds the orange
var orangeGood : boolean := false
var score : int := 0
var orangeCollision : boolean := false
% initialize variables
snake (1).x := maxx div 2
snake (1).y := maxy div 2
%procedure for the snake going up
proc snake_up
snake (1).y += 20
drawfilloval (snake (1).x, snake (1).y, 10, 10, 41) %snake head
drawfilloval (snake (1).x - 4, snake (1).y + 4, 2, 2, black) %eyes
drawfilloval (snake (1).x + 4, snake (1).y + 4, 2, 2, black) %eyes
%drawfilloval (snake (1).x, snake (1).y - 20, 10, 10, 12) %Snake following
View.Update %updates the graphics on screen
move := "UP"
end snake_up
%procedure for the snake going down
proc snake_down
snake (1).y -= 20
drawfilloval (snake (1).x, snake (1).y, 10, 10, 41) %snake head
drawfilloval (snake (1).x - 4, snake (1).y - 4, 2, 2, black) %eyes
drawfilloval (snake (1).x + 4, snake (1).y - 4, 2, 2, black) %eyes
%drawfilloval (snake (1).x, snake (1).y + 20, 10, 10, 12) %Snake following
View.Update %updates the graphics on screen
move := "DOWN"
end snake_down
%procedure for the snake going left
proc snake_left
snake (1).x -= 20
drawfilloval (snake (1).x, snake (1).y, 10, 10, 41) %snake head
drawfilloval (snake (1).x - 4, snake (1).y + 4, 2, 2, black) %eyes
drawfilloval (snake (1).x - 4, snake (1).y - 4, 2, 2, black) %eyes
%drawfilloval (snake (1).x + 20, snake (1).y, 10, 10, 12) %Snake following
View.Update %updates the graphics on screen
move := "LEFT"
end snake_left
%procedure for the snake going right
proc snake_right
snake (1).x += 20
drawfilloval (snake (1).x, snake (1).y, 10, 10, 41) %snake head
drawfilloval (snake (1).x + 4, snake (1).y + 4, 2, 2, black) %eyes
drawfilloval (snake (1).x + 4, snake (1).y - 4, 2, 2, black) %eyes
%drawfilloval (snake (1).x - 20, snake (1).y, 10, 10, 12) %Snake following
View.Update %updates the graphics on screen
move := "RIGHT"
end snake_right
%procedure to move the snake according to the user's input
proc move_snake
if key = KEY_UP_ARROW and move not= "DOWN" then
snake_up
elsif key = KEY_UP_ARROW and move = "DOWN" then
snake_down
elsif key = KEY_DOWN_ARROW and move not= "UP" then
snake_down
elsif key = KEY_DOWN_ARROW and move = "UP" then
snake_up
elsif key = KEY_LEFT_ARROW and move not= "RIGHT" then
snake_left
elsif key = KEY_LEFT_ARROW and move = "RIGHT" then
snake_right
elsif key = KEY_RIGHT_ARROW and move not= "LEFT" then
snake_right
elsif key = KEY_RIGHT_ARROW and move = "LEFT" then
snake_left
end if
end move_snake
proc update_snake
for i : 2 .. upper (snake)
snake (i).x := snake (i - 1).x
snake (i).y := snake (i - 1).y
end for
end update_snake
proc make_orange
loop
orangeGood := true
randint (ox, 20, 790)
randint (oy, 20, 590)
for i : 1 .. upper (snake)
if ox = snake (i).x or ox < snake (i).x + 11 or ox > snake (i).x + 11 then
if oy = snake (i).y or oy < snake (i).y + 11 or oy > snake (i).y + 11 then
orangeGood := false
end if
end if
end for
exit when orangeGood = true
end loop
end make_orange
proc draw_orange
Draw.FillOval (ox, oy, 10, 10, 42)
end draw_orange
proc draw_snake
for i : 2 .. upper (snake)
Draw.FillOval (snake (i).x, snake (i).y, 10, 10, 12)
end for
end draw_snake
% proc check_collision
% if snake(1).x + 10 < ox - 10 or snake(1).x - 10 > ox + 10 then
% if snake(1).y + 10 < oy - 10 or snake(1).y - 10 > oy +10 then
% orangeCollision := true
% make_orange
% score += 10
% new snake, upper (snake) + 1
% end if
% end if
% end check_collision
update_snake
draw_snake
make_orange
loop
getch (key)
loop
update_snake
% check_collision
draw_orange
move_snake
draw_snake
delay (100)
cls
exit when hasch
end loop
end loop
the only problem with this is we can't make it eat the actual orange. i know we have to use a grid but what would be better to use whatdotcolour or collision detection. josh and i are arguing about that. as well how would you use those because we tried both but we can't really get it to work.
Neo
Posted: Wed Jan 12, 2005 7:24 pm Post subject: (No subject)
Whatdotcolour is a form of collision detection. If your going to use circles I'd suggest using circle collision detection using Math.Distance(). Get the distance from the food to the snake's head. If the disttance is less than their radiuses combined then eat the food.
Cervantes
Posted: Wed Jan 12, 2005 7:34 pm Post subject: (No subject)
Neo wrote:
If your going to use circles I'd suggest using circle collision detection using Math.Distance()
Actually, I would recommend using location comparisons. It's just as easy, and if you ever decide to use squares instead of ovals, it'll be just a matter of changing the draw commands.
josh
Posted: Wed Jan 12, 2005 8:02 pm Post subject: (No subject)
as you probobly guessed cooldude is my partner.
I tried just changing the draw command but it really messed it up, so there is more to it than that. I like using hte math.function as it seems really simple and it will work.
I would use a gird, but we don't have much time to do this and I don't want to go and re-write the whole code again....
Cervantes what do you mena by location comparisons? i
you mean like this:
code:
sleft := sx
sright := sx + size
sbottom := sy
stop := sy + 20
oleft := ox
oright := ox + 20
obottom := oy
otop := oy + 20
if stop > obottom and sbottom < otop then
if sright > oleft and sleft < oright then
collision := true
end if
end if
josh
Posted: Wed Jan 12, 2005 9:25 pm Post subject: (No subject)
o.k. so I got the collisoin detectoin working and now the snake grows also but for some reason after it eats an orange the screen sometimes just randomly goes white.
Can n e 1 figure out why? here is the new code:
Turing:
% Declare variables type snakeDat : record
x :int
y :int endrecord
var snake :flexiblearray1.. 2of snakeDat
var key :string(1)%the users key button variable var move :string:="MOVE"%variable for the way the snake is moving var ox, oy :int%holds the orange var orangeGood :boolean:=false var score :int:=0 var orangeCollision :boolean:=false
EDIT: I have been looking at this some more and I am wonderng, is it that the new orange is being generated ontop of the new circle being added to the snake?