DRAFT

David Bau

2013

Visit http://turtlebits.net/ to run your programs.

Copyright © 2013 David Bau.

18. Motion

Bounce

speed Infinity
pen purple
vy = 10
tick 20, ->
  slide 1, vy
  if inside(window)
    vy -= 1
  else
    vy = Math.abs(vy) * 0.9

Tag

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

Orbit

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.

Newtonian Simulations

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.

Motion and Hit Testing Functions

16. Sets

Scatter

turtle.remove()
s = hatch 15, orange
s.pen gold
s.plan ->
  this.rt random 360
  this.fd Math.abs(20 * random normal)

Turtle Race

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

Rescue Class

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.

JQuery Set Methods

Methods operating on a jQuery set s can:

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.

Giving Turtles Individualized Plans

When s.plan (j) -> action runs, The action is done for each element with the following arguments:

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.

Using and Selecting Classes

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.

24. Intelligence

Tic Tac Toe

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}