
TO snake
;setup:
reset
;empties (erases) all containers (variables),
;returns the turtles to their default positions,
;restores their states to defaults
release
;sets all the turtles, except for the default ones
;(myrtle, snappy, libby) 'free' - erasing them
penup
;don't draw
setbackgroundcolor lightblue
hideturtle
;don't show the executing turtle (typically myrtle)
stealth
;don't show up in output from the near primitive
noaudiowait
;don't delay when playing sounds (the default)]
print |Use keys J and K to turn snake, eat apples!|
;print instructions to the screen
;create the 'ground'
setfillcolor brown
setpos [-185 -115]
lower 5
quad 185 * 2 115 * 2
make "score 0
make "length 8
;the default snake length, in segments
make "queue empty
;this snake implementation uses a push / pull queue
;to keep track of the snake's turns
repeat :length [push "fd "queue]
;all segments start moving forward
newmodel "ball [setfillcolor 12 icosphere 5]
newmodel "head [setfillcolor 4 icosphere 6]
newmodel "apple [setfillcolor red icosphere 4]
;create turtle models, where ball is a non-head segment
;create the turtles that make up the starting snake:
repeat :length [
nameturtle repcount
;create a new turtle
;each segment has the number of its segment as its name
control repcount {"wrap "penup "back repcount "* 10 "showturtle}
;initialize the new turtle and move it into position
;{} denotes a 'softlist', a list whose contents are evaluated
;at execution
control repcount [
setbounds [-185 -115 185 115]
]
;sets the bounds used for wrapping. [] denotes a 'hardlist', a
;list whose contents are fixed and not evaluated
if repcount = 1 [control 1 [setmodel "head]]
;if this is the first segment, give it the 'head' model
else [control repcount [setmodel "ball]]
;otherwise, give it the 'ball' (segment) model
]
;set up the 'apple' turtle:
nameturtle "apple
control "apple [
penup setmodel "apple showturtle
dountil (count near position 5) = 1 [
setpos {-180 + (10 * random 36) -100 + (10 * random 20)}
]
;set a random position until such times as the apple is not near
;any snake segments (turtles)
;leading parameters used in comparison operators (=, >, < etc) ;must be enclosed in {}'s if more than a single value or primitive ;because the parser evaluates right to left ] ;the main loop: forever [ norender ;don't render while we shuffle the snake forward for {"i 1 :length} [ make "move item :i :queue ;set the move variable to the :i item in the :queue switch "move case "fd [ask :i [forward 10]] case "rt [ask :i [right 90 forward 10]] case "lt [ask :i [left 90 forward 10]] ;switch / case statements execute instructions based on the ;contents of the container (variable) provided to switch ;ask creates a new worker that executes the commands using ;the specified turtle (the turtle named by :i) ;so, in this for loop we are iterating through the :queue, ;moving the turtle segments based on the item present in ;each space in the queue (fd, rt or lt) ] while workers != [] [sleep 1] ;sleep execution until the ask workers finish moving their ;turtles (the snake). This way, when we enable the render, ;we won't catch the snake mid-move. render ;enable rendering again wait 40 - :score ;wait units are 60ths of a second. The higher the score, the ;shorter the wait will be ;user control: if keyp [ ;did the user press a key? If so, do this stuff: make "input readchar ;take the output from the readchar primitive and place it ;into the input container. keyp indicates if there is a character ;already in the keyboard buffer. If readchar is called when there ;is no character in the buffer, it waits. switch "input case "j [push "lt "queue] ;if the user pressed the j key, push lt to the front of the queue case "k [push "rt "queue] ;if the user pressed the k key, push rt to the front of the queue otherwise [push "fd "queue] ;if no case has been satisfied since switch (the user pressed a key ;other than j or k), push fd to the front of the queue ] else [push "fd "queue] ;similarly, if the user hasn't pressed any key, push fd to the front ;of the queue ignore dequeue "queue ;ignore (drop) the last value off the end of the :queue ;dequeue would ordinarily return this value, ignore sends it into ;the ether ;is there something occupying the same space as the head of the snake? if (count (near 1:position 5)) > 1 [
;near returns the names of the turtles within the specified distance (5)
;count returns the count of the number of turtles returned by near
;if there is more than one (the head itself), we will execute the following:
if memberp "apple near 1:position 5 [
;is it an apple?
playsound "chomp
inc "score
;increment the value in the score container by one
(print |Yum! Score:| :score)
;to pass more than the default number of parameters to a primitive, wrap
;it in parentheses (round brackets)
dountil (count near apple:position 5) = 1 [
control "apple [setpos {-180 + (10 * random 36) -100 + (10 * random 20)}]
]
;place a new apple
repeat 2 + int (:score / 10) [
queue "pa "queue
;a pa or 'pass' will cause these new segments not to move on the next
;iteration, until movement commands are 'shuffled' on to their places
;in the queue
inc "length
;increment the value of the length container by one
nameturtle :length
;create a new turtle with the name contained in :length (the new length)
(control :length {"hideturtle "setmodel ""ball "wrap "penup "setpos
"query :length - 1 leftbracket "position rightbracket "setvectors
"query :length - 1 leftbracket "vectors rightbracket})
;set up the new turtle and make sure it's positioned next to the
;previously last turtle, and is facing in the same direction
control :length [setbounds [-185 -115 185 115] showturtle]
;set the bounds of the new turtle and show it
wait 10
]
;add new segment to the snake
]
;and if it's not an apple?
else [
print "crash! playsound "crash finish
]
;game over, man! game over!
]
]
END