Visit http://turtlebits.net/ to run your programs.
Copyright © 2013 David Bau.
speed Infinity pen purple vy = 10 tick 20, -> slide 1, vy if inside(window) vy -= 1 else vy = Math.abs(vy) * 0.9
speed Infinity write "Catch blue!" b = hatch blue bk 100 tick 10, -> turnto lastmousemove fd 5 b.turnto 45 + bearing b b.fd 6 if b.touches(turtle) write "You win!" tick off else if not b.touches(window) write "Blue got away!" tick off
speed Infinity; pen orange G = 100 v = [0, 1] sun = hatch(gold) sun.slide G, 0 tick 100, -> sun.moveto lastclick s = sun.getxy() p = getxy() d = distance(sun) d3 = d * d * d if d3 > 0 then for i in [0..1] v[i] += G * (s[i] - p[i]) / d3 slide v[0], v[1]
The examples on this page show three classical examples that simulate motion: a bouncing turtle, a game of tag, and an orbit simulator.
When Newton worked out his famous laws of motion, he discovered that the speed and direction of an object - its velocity - remains unchanged as long as no forces act on the object. And he discovered that forces do not directly change the position of an object: forces alter an object's velocity.
When simulating motion, the velocity of an object can be represented by a small change in position for each tick in time. An undisturbed object moves the same distance and direction on each tick, and a forced object will alter its velocity on each tick.
In Bounce, the two variables vx
and vy
are the x and y components of velocity. The
gentle accelleration due to gravity is simulated by a slight
change in velocity on each tick: vy -= 1
.
The sudden accelleration of a bounce off the floor
(with some loss in energy) is represented by a sign change in velocity:
vy = Math.abs(vy) * 0.9
.
In Tag, velocity is simulated by
moving each turtle forward 5 or 6 on each tick. The physics
of this game are designed for fun: the main
turtle picks its direction by pointing at the last position of
the mouse. The blue turtle runs away by adding 45 degrees to the
bearing
from the main turtle to itself.
Orbit is a representation of Newton's
most profound discovery: that the gravity makes objects fall
to the ground is the same force that governs the motions of
the planets. In the orbital simulator, the x and y components
of velocity are in the array v
, and the velocity
is accellerated on each tick using the formula
v[i] += G * (s[i] - p[i]) / d3
, where
s is the position of the sun, p is the position of the planet,
and d3 is the cube of the distance between them.
Click to move the sun. Experiment with elliptical and hyperbolic orbits. Notice the planet moves more quickly when it is near the sun.
slide x, y
slides right by x and forward by y.
getxy()
returns the absolute [x, y] position of the turtle.
b.touches(turtle)
true if b
touches the main turtle.
inside(window)
true if the main turtle is fully inside the window.
bearing(b)
the direction from the turtle to b
.
distance(sun)
the distance from the turtle to sun
.
turtle.remove() s = hatch 15, orange s.pen gold s.plan -> this.rt random 360 this.fd Math.abs(20 * random normal)
fd 200; pen red; slide 200, 0 finished = 0 racers = hatch 7 racers.plan (j) -> @wear random color @speed 5 + random normal @slide j * 25 + 25, 0 while not @touches red @fd random 5 await @done defer() @label ++finished
turtle.remove() speed 100 randpos = -> [50 * random(normal), 50 * random(normal)] hatch(20, green).scale(0.75).plan -> this.moveto randpos() this.addClass 'kid' hatch(3, red).plan (num) -> hero = this count = 0 hero.moveto randpos() hero.pen red while true await hero.done defer() kid = $('.kid').nearest(hero).eq(0) if kid.length is 0 write "hero ##{num} got #{count}" return else if hero.touches(kid) count += 1 kid.label num kid.remove() else hero.turnto(kid).fd(5)
Turtles are jQuery sets. Although most sets
we have worked with contain a single turtle, a set
can contain any number of elements.
hatch 15
makes a set of 15 new turtles,
and $('.turtle')
is the set of all turtles.
Methods operating on a jQuery set s
can:
s.nearest [0, 0]
is the subset nearest 0, 0.
s.fd 100
advances the elements by 100.
s.touches red
tests pixels under the first element.
Generally a manipulation method like
s.fd 100
will do the same operation
on every element of the set. However, the method
s.plan
applies a function that can
run a distinct operation on each element.
When s.plan (j) -> action
runs,
The action is done for each
element with the following arguments:
this
(aka @
) is
a jQuery set with the single element.
j
is the element index, ranging from
0
to crowd.length - 1
.
For example, Scatter uses plan
to direct each turtle to turn and move a different random
amount. Note that random normal
returns a
normally distributed random number with mean 0 and
variance 1.
Turtle Race is similar, but it
also uses an await
loop to run the seven
turtles in a parallel race. On each iteration, the turtles
individually check if they have crossed the red line.
The shared variable finished
tracks the order in which the turtles finish.
The loop in Rescue Class finds the nearest kid to each hero and removes that kid if the hero touches it. Otherwise the hero turns and moves towards the nearest kid and repeats the process.
At the beginning of that program, all the kids are marked
with a class
using this.addClass('kid')
. On the hero
thread, the jQuery selector $('.kid')
obtains the set of all current elements in the
kid
class that have not yet been removed.
jQuery methods that return sets can be chained:
$('.kid').nearest(hero).eq(0)
filters the set
of kids to the subset nearest hero
, and then
filters that subset to its first element, if any.
There are a wide range of jQuery methods for finding and manipulating sets: much about jQuery has been written on the web.
grid = table 3, 3, {width: 48, height: 48, font: "32px Arial Black", background: "wheat"} grid.home() board = [0, 0, 0, 0, 0, 0, 0, 0, 0] grid.cell().click -> move = grid.cell().index this return unless winner() is 0 and board[move] is 0 board[move] = 1 $(this).text 'X' setTimeout respond, 500 respond = -> response = bestmove(-1).move if response? board[response] = -1; grid.cell().eq(response).text 'O' colorwinner() bestmove = (player) -> win = winner() if win isnt 0 then return {move: null, advantage: win} choices = {'-1': [], '0': [], '1': []} for think in [0..8] when board[think] is 0 board[think] = player outcome = bestmove(-player).advantage choices[outcome].push {move: think, advantage: outcome} board[think] = 0 for favorite in [player, 0, -player] when choices[favorite].length return random choices[favorite] return {move: null, advantage: 0} rules = [[0,1,2],[3,4,5],[6,7,8],[0,3,6],[1,4,7],[2,5,8],[0,4,8],[2,4,6]] winner = -> for row in rules if board[row[0]] and board[row[0]] is board[row[1]] is board[row[2]] return board[row[0]] return 0 colorwinner = -> for row in rules if board[row[0]] and board[row[0]] is board[row[1]] is board[row[2]] for n in row grid.cell().eq(n).css {color: red}