Visit http://pencilcode.net/ to run your programs.
The free website is made possible by your purchase of this book.
Second Edition. Copyright © 2013 David Bau.
Pencil Code is an open-source system
that unites the CoffeeScript language by Jeremy Ashkenas in 2009,
and Iced await/defer extensions created by Maxwell Krohn in 2012,
with the jQuery-turtle plugin developed by the author in 2011,
using the jQuery library invented by John Resig in 2006.
This work is inspired by the beloved LOGO language
created by Seymour Papert and Wally Feurzeig in 1967.
Special thanks to the students in Lincoln Massachusetts,
Beaver Country Day School, and Dorchester McCormack School
who vetted this material.
Post questions, ideas, and bug reports to http://pencilcode.net/group
Cover image by Phil Clements. Back cover image by Vinod Velayudhan.
This book is typeset in Łukasz Dziedzic's 2010 open font Lato
and Paul D. Hunt's 2012 Adobe Source Code Pro.
The aim of this book is to teach you to write programs as you would use a pencil: as an outlet for creativity and as a tool for understanding.
These pages follow a fifty-year tradition of using programming as a liberating educational tool, with no thresholds for beginners, and no limits for experts. Seymour Papert's LOGO is the inspiration. Start with a few lines of code, and progress to writing programs to explore art, mathematics, language, algorithms, simulation, and thought.
The language is CoffeeScript. Although CoffeeScript is a production programming language used by pros, it was chosen here because it has an elegance and simplicity well-suited for beginners. While the first examples make the language look trivial, CoffeeScript has a good notation for all the important ideas: algebraic expressions, lists, loops, functions, objects, and concurrency. As you learn the language, remember that the goal should be not mastery of the syntax, but mastery of the underlying concepts.
Edit and run your programs on pencilcode.net. The site is a live experiment in community learning: everything posted is public, so write programs that would be interesting to others. Accounts are free.
As you experiment by building your own ideas, you will find that at first your programs will behave in ways that you do not intend. Details matter, and persistence pays off. If you are patient in adjusting and perfecting your work, you will be rewarded with insight.
Read, think, play, and strive to create something beautiful.
David Bau, 2013
pen red fd 50
pen blue fd 50; rt 90 fd 50; rt 90 fd 50; rt 90 fd 50; rt 90
pen black fd 70; rt 120 fd 70; rt 120 fd 70; rt 120
speed 5 pen green fd 30; lt 90 fd 10; rt 120 fd 80; rt 120 fd 80; rt 120 fd 10; lt 90 fd 30; rt 90 fd 60; rt 90
pen gold fd 100; rt 144 fd 100; rt 144 fd 100; rt 144 fd 100; rt 144 fd 100; rt 144
A simple computer program is called a script, because the computer performs it like reading lines like a play. Each command is followed, one at a time, from beginning to end.
The scripts on this page use four basic functions to move a turtle:
fd 100
moves the turtle forward 100 pixels.
bk 100
goes backward.
rt 90
turns right 90 degrees.
lt 90
turns left.
In CoffeeScript, fd
is different from FD
(and FD is not defined),
so all these function names should be typed in lowercase.
It is important to put a space between the function name
and the number. Do not indent the code for now,
because indenting has special meaning in the language.
Notice that a small turn traces out an obtuse angle. An acute angle requires a turn more than 90. Turtles measure turns in exterior angles, so a complete circuit always adds to a multiple of 360.
On pencilcode.net, you can try single commands and ask for help in the console on the bottom of the right panel. It is a good way to experiment.
The turtle draws a line by selecting a pen.
pen red
traces
out a line in red.
Common color names such as red, black, white, blue, green, yellow, orange, and purple all work. There are 140 standard color names that are listed at the end of this book.
Unselect the pen by using pen null
.
Use pen erase
for an eraser.
The turtle takes about a second to trace out any movement, but its speed can be changed.
speed 10
sets the speed to 10 moves
per second.
speed Infinity
moves instantly.
The semicolon (;) that appears in the examples is just used for combining two commands on the same line. These programs would behave the same if all the commands separated by semicolons were written on separate lines.
rt 90; dot lightgray fd 30; dot gray fd 30; dot() fd 30
message = 'Hello You.' see 'message' see message
pen crimson fd 60; label 'GO' rt 30 fd 40; rt 120; dot gold, 30 fd 40; rt 30 fd 60; rt 90 fd 40; rt 90
speed 10 dot yellow, 160 fd 20 rt 90 fd 25 dot black, 20 bk 50 dot black, 20 bk 5 rt 90 fd 40 pen black, 7 lt 30 lt 120, 35 ht()
x = 18 see x * 5 dot black, x * 5 dot white, x * 4 dot black, x * 3 dot white, x * 2
Some new functions on this page:
dot black, 20
draws a black dot of diameter
20 under the turtle.
label 'GO'
draws the text GO under the turtle.
see x * 5
shows the value of x * 5 in the
test console.
ht()
hides the turtle. Show it again
with st()
.
lt 120, 35
traces an arc of radius 35
while turning left 120 degrees.
If you are lost in a long program, add
dot red
or label 'A'
or
see x
to understand a specific point
in the code.
These three functions are useful for debugging because they make a visible record of the current state of the program without otherwise changing things.
Most of the words in our programs
(including fd
, rt
, speed
and red
) are predefined in Pencil Code, but you can
define your own words using the =
equals assignment
symbol.
The assignment message = 'Hello You.'
defines
the word message
to stand for the text "Hello You."
inside the program Message.
Bullseye defines x = 18
.
After the definition, x means 18. For example,
if we were to write see x
or label x
,
it would not draw the letter x on the screen. It
would write out the number 18.
Words like x
without quotes
are are called
variables. Variables can stand for numbers, functions,
text, or other objects.
To literally write the letter "x" on the
screen, put it in quotes: label 'x'
will show the letter x, and see 'message'
will
write out the word "message". Quoted text in a program is
called a string.
Mathematical operations are written as you would expect:
x + y
addition.
x - y
subtraction.
x * y
multiplication.
x / y
division.
Parentheses and order-of-operations work as
taught in math class. When x is 18,
see x + x * x / (7 + x)
will do the computation
and show 30.96.
pen green for d in [50, 100, 50, 100] fd d rt 90
for c in [ red orange yellow green blue violet ] pen c rt 360, 50 fd 10
see [1..5] see [1...5]
pen blue for [1..4] fd 50 rt 90
speed 100 pen red for [1..360] fd 1 rt 1
pen purple for x in [50..1] by -1 rt 30, x
To draw a rectangle, we could write the following.
fd 50; rt 90; fd 100; rt 90; fd 50; rt 90; fd 100; rt 90
But that is wordy and repetitive.
The program Rectangle is clearer
because it uses a for
loop to repeat the fd
and rt 90
commands.
Look closely at Rectangle.
The for
loop has three parts:
d
.
[50, 100, 50, 100]
.
fd d; rt 90
.
The prepositions for
and in
are special words in the language: they introduce a loop
variable and its loop list.
Since the list contains four numbers,
the loop repeats the body four times: once with d
set to 50; then once with d
as 100; then again
as 50; then finally as 100 again.
A list is written by surrounding items with square brackets
[
]
.
If you write list items on a single line, separate them with commas. Longer lists like the list of colors in Rainbow can be written on multiple lines for clarity; commas are not needed at linebreaks.
A range of numbers can be listed by putting
two dots ..
between the lowest and highest numbers.
If you use three dots ...
, the effect is similar,
but the last number will not be included in the list.
The commands in the loop are indented underneath the
for
line to show that they are inside the loop. It is important to
indent lines inside the loop body evenly with each other.
List items should also be indented evenly with each other when written on separate lines.
Notice that the loop variable does not need
to be used inside
the body of the loop. In
Square Loop and
360 Loop, the variable
x
is
not used except to count the number of repetitions.
In Descending Loop, the
word by
after the list denotes
a stride, which is how much to skip
forward when looping through the list. Looping
by 2
would skip every other number.
Looping by -1
counts down.
pen blueviolet for [1..5] rt 72 for [1..3] fd 50 rt 120
for outside in [skyblue, violet, pink] for inside in [palegreen, orange, red] dot outside, 21 dot inside, 7 fd 25 rt 36
pen turquoise for [1..10] dot blue for [1..4] fd 50 rt 90 lt 36 bk 50
speed 100 rt 90 for color in [red, gold, green, blue] jump 40, -160 for sides in [3..6] pen path for [1..sides] fd 100 / sides lt 360 / sides fill color fd 40
Any code can be put in a loop, including another loop.
Nesting loops within loops can create beautiful effects.
Violet arranges five triangles around
a point by nesting a loop of 3 within a loop of 5. The single
line fd 50
is repeated 15 times with
perfect symmetry.
When loops are nested, the inner loop is the one that repeats most quickly. Consider Combinations.
A single pass through a loop is called an iteration.
On each iteration, the program draws a small dot within a big dot,
then moves the turtle a bit. The color of the small dot comes
from the variable inside
, which is the loop variable
of the inner loop. The large dot color comes from the outer loop
variable outside
.
Because the inner loop repeats most quickly, the small dot colors palegreen, orange, and red change on every iteration.
The outer loop repeats only after the inner loop has made
a full set of iterations, so the outside
dot colors
change only after 3 inner iterations have been made.
The level of indent indicates whether code is within an inner loop or an outer loop, or not within a loop at all.
In Decorated Nest, fd 50
is indented twice to be in the innermost loop. It runs 40
times in total. However,
dot blue
is only indented once, so it is in the
outer loop and done only 10 times. Lines that are not indented,
such as pen turquoise
, are not looped,
and they are done only once.
Loops can be nested as deeply as you like.
Catalog shows a triply-nested loop. Its
innermost loop repeats by a number that varies (sides
)
because the loop range comes from the second level loop variable.
Some new functions:
jump 40, -160
jumps right 40 and back 160.
pen path
traces with a special invisible path pen.
fill color
fills the invisible path with color.
Note that jump
jumps right and up relative to the
current direction and position of the turtle, and it does not draw
with the pen or turn the turtle. To jump to an absolute
Cartesian coordinate, use jumpto
.
pen purple scoot = (x) -> fd 10 * x rt 90 scoot 7
spike = (x) -> fd x label x bk x pen crimson for n in [1..6] spike n * 10 rt 60
square = (size) -> for y in [1..4] fd size rt 90 pen red square 80 jump 15, 15 pen firebrick square 50
tee = -> fd 50 rt 90 bk 25 fd 50 pen green tee() pen gold tee() pen black tee()
The most important idea in this book:
A function is a miniature program. In CoffeeScript, a function
is written with an arrow ->
typed as
two symbols next to each other (the minus and
the greater-than) like this:
(input) -> something to do
A function that advances the turtle by ten times a distance is
(x) -> fd 10 * x
Name a function like any variable,
using =
.
scoot = (x) -> fd 10 * x
After the definition, we can write
scoot 7
or scoot 5 + 2
.
In other words, scoot
can be used just
like predefined functions like fd
or rt
.
The variable x
in parentheses in the function
definition is called a parameter. Parameters may use
any name. When the function is run, the
parameter takes on the value passed to the function.
When spike n * 10
is called, the code within the
function binds parameter name x
to the current
value of n * 10
, which is 10 during the
first iteration of the loop.
Each time a function is called, its parameters can have
different values. The last time spike
is called,
n * 10
has advanced to 60, so the value of
x
during the last function call is 60.
The level of indenting is important for determining the scope of a line. If a line is indented under an arrow, that line is inside the function.
If the function itself contains loops, those should be indented further. There is no limit to the depth of nested indenting, but indenting must be done neatly. Each level of indenting indicates a particular function, loop, or nested scope.
Functions like tee
that have no parameters
are written specially:
tee = -> ...
omits the parentheses.
tee()
requires empty parentheses.
polygon = (c, s, n) -> pen c for [1..n] fd s rt 360 / n pen null polygon blue, 70, 5 bk 50 polygon(orange, 25, 6)
rule = (sizes) -> for x in sizes fd x bk x rt 90; fd 10; lt 90 pen black rule [50, 10, 20, 10, 50, 10, 20, 10, 50]
starburst = (x, shape) -> for z in [1..x] shape() rt 360 / x stick = -> fd 30; bk 30 pen deeppink starburst 3, stick jump 0, -60 starburst 20, stick jump 0, -90 starburst 10, -> fd 30; dot blue; bk 30 jump 0, -100 starburst 5, -> fd 30 starburst 7, -> fd 10 bk 10 bk 30
Multiple parameters can be listed in a function
definition with commas. The declaration
polygon = (c, s, n) ->
sets up three parameters:
a color c
, a side length s
, and a
number n
.
The value passed to a parameter when using a function is called
an argument. When calling a function with
several parameters, the arguments are listed with commas.
For clarity, you can put parentheses around the argument list,
like polygon(orange, 25, 6)
.
When using parentheses around function arguments, do not put any space between the function name and the first parentheses, or else the parentheses will be interpreted as enclosing only the first argument.
An argument may be a complex object such as a list. That is the approach taken in Rule.
The parameter named sizes
is used as the
loop list in a for
loop. When rule
is called, the whole list is passed as one argument.
An argument may itself be another function. That is done in Starburst. The technique allows one mini-program to be attached to another.
The call to starburst 3, stick
passes
the function stick
as the last argument.
Inside starburst
,
n
now stands for 3, and shape
stands for the stick
function.
When shape()
is written,
stick()
is called. In the end
stick
is called three times,
drawing three symmetric sticks.
Calling starburst 30, stick
calls stick
30 times, making a circular starburst of 30 sticks.
Calling starburst n, something
means
"Do something n times in a star." We can
provide any code as something, even if unnamed.
The call starburst 10, -> fd 30; dot blue; bk 30
passes a lollipop-like function to starburst
. The
function has no name &endash; it is defined inline to draw line
with a blue dot at the far end. The starburst function binds
this unnamed function to its local parameter name
shape
and calls it 10 times. The result is a
starburst with blue dots.
The last starburst
call passes unnamed code that
does another starburst
. The result is a starburst
made out of starbursts!
speed 100 pen red for x in [1..20] fd 80 rt 100 if x is 10 pause 2
speed Infinity advance = -> pen lightgray bk 100 rt 5 pen red fd 100 tick advance
seconds = 5 tick -> if seconds is 0 write "Time's up!" tick null else write seconds seconds = seconds - 1
speed Infinity pen green tick -> moveto lastclick
speed Infinity pen orange tick 100, -> turnto lastmousemove fd 1
There are two techniques for organizing time in a program:
speed
Works with QueuesIn Pencil Code, each turtle has its own animation queue
that is used if you set speed
to any number less than
Infinity. (The default speed is one.)
Each movement command like fd 100
adds
the motion to the turtle's animation queue. When the program is
finished running, the turtle has the whole plan, and it runs
through its animation queue after your program is done.
The animation queue works well for timed motions that your program can plan ahead of time. But if you are writing a game or simulation that needs to respond to events in real time, then you may find it more sensible to to draw frames.
tick
Works with FramesThe tick
command is used for frames: it calls the
passed function at a regular rate. The optional first
argument is the frame rate (the default rate is one frame per
second).
The Countdown example writes a
number on each tick callback. It also shows how to clear
the callback once you are done: call tick null
.
The Move Draw example is a very simple
interactive program that uses tick
. 100 times per
second, it runs a function that turns the turtle toward the position
on the screen where the mouse last moved, then advances the turtle
by one pixel. Because each frame should be drawn instantaneously,
it sets speed Infinity
.
Several new built-in names are used in these examples.
pause 2
adds a 2-second pause to the animation queue.
tick 100, fn
calls fn 100 times per second.
write "Time's up"
writes a message on the screen.
moveto lastclick
moves the turtle to the position
of the last click.
turnto lastmousemove
turns the turtle toward the last mouse position.
The moveto
can be used with any Cartesian coordinate
or any object that has a position - it happens to be used here
with the special variable lastclick
. Similarly,
turnto
can be used with any absolute direction or
coordinate. The special variable lastmousemove
happens to keep the most recent mouse position.
cry = (who, query) -> write "Oh #{who}, #{who}!" write "#{query} #{who}?" cry "Romeo", "Wherefore art thou" cry "kitty", "What did you eat" play "fc/c/dcz"
url = "http://upload.wikimedia.org/wikipedia" + "/commons/6/61/Baby_Gopher_Tortoise.jpg" write """<center><img src="#{url}" width=100> </center>"""
n = write "<h1>Notice</h1>" write """ <p>This long paragraph has <b>bold</b>, <i>italic</i>, and <u>underlined</u> text. Horizontal rule below.</p> """ write "<hr>" write """ <p><a href="//pencilcode.net/"> Link</a> with an <a>. </p> """ n.css background: pink
n = write "<h1>Notice</h1>" write """ <p>This long paragraph has <b>bold</b>, <i>italic</i>, and <u>underlined</u> text. </p>""" n.css background: pink display: 'inline-block' n.pen purple, 10 n.bk 80 n.rt 45 n.fd 50
A string written with double quotes "..."
can interpolate values written as
#{something}
, which means the value
of something is inserted into the string.
A multiline string can be written by tripling the quotes (either double or single) around the string, as is done in the last string of Imagery.
Codes like <b>
are called
HTML tags.
They set off text for special formatting:
<b>
and </b>
mark bold text; <h1>
and </h1>
mark a first-level
heading; the <hr>
tag is a "horizontal rule".
A matching tag pair and its contents (or singleton tag,
for tags like <hr>
or
<img>
that are not paired)
make up an HTML element.
HTML Elements can have attributes with special meanings such
as the href
attribute on the <a>
element, which sets the URL for a hyperlink.
The other attributes seen on this page are the src
and width
attributes on the <img>
element, which specify the location from which to load the
image data, and the scaling width to use.
Programs can use jQuery objects to alter HTML elements on the screen.
The code n = write "<h1>Notice</h1>"
returns a jQuery object for the <h1>
element,
and stores it in the variable n
. Then
the jQuery function n.css
is used to alter its CSS.
In Pencil Code, all the turtle methods such as fd
and pen
are available as jQuery methods. Any
element can be moved like a turtle.
CSS properties can alter many of the details of HTML formatting
such as such as an element's background
(set here to
pink
). The css
function can set more
than one property at once, and the property list under
n.css
should be indented.
HTML is a rich subject. There are more than 100 types of HTML elements, more than 100 HTML attributes, more than 100 jQuery methods, and more than 100 CSS properties. The best way to explore all these options is to search for and consult the many resources on the Internet about these technologies.
And experiment.
pen sienna button 'R', -> rt 10 button 'F', -> fd 10 button 'D', -> dot 'darkslateblue'
await read "Color?", defer color await read "Sides?", defer sides pen color for [1..sides] fd 30 rt 360 / sides
secret = random [1..100] turns = 5 write "Guess my number." while turns > 0 await readnum defer pick if pick is secret write "You got it!" break if 1 <= pick < secret write "Too small! " turns = turns - 1 else if secret < pick <= 100 write "Too big! " turns = turns - 1 if turns > 1 write "#{turns} left." else if turns is 1 write "Last guess!" else write "Game over." write "It was #{secret}." break
read "Color?", (color) -> read "Sides?", (sides) -> pen color for [1..sides] fd 30 rt 360 / sides
The examples on this page gather input using callbacks:
button
sets up a function to be called
whenever a button is pressed.
read
calls a function once
after the user answers a prompt.
readnum
is like read, but for numbers only.
If a program needs to wait until a callback is received,
the await
keyword: inside the await
,
the defer
statement stands for the callback.
After this callback receives a value, the program continues
on the line after the await
The Guess My Number example uses
the random
function to pick an unpredictable
number from 1 to 100. (The argument to random
is a list of numbers to choose from.)
The game allows five turns to guess the number, tracked in
the variable turns
.
The assignment turns = turns - 1
means "set the value of turns
to be
one less than the old value of
turns
".
A true or false value is called a boolean.
The expression turns > 1
is a boolean that is
true when turns
exceeds 1. When used with
conditional words if
and else
,
booleans control program flow. Other examples:
pick is secret
, true when the two variables have the same value.
pick isnt secret
, true when the two variables are unequal.
secret < pick <= 100
, true when pick
exceeds secret
but not 100.
secret < pick and pick <= 100
, the same thing
written using and
.
not (secret >= pick or pick > 100)
, again
with not
and or
.
Statements to be be run conditionally should be
indented underneath the if
or else
line that controls the condition.
It is worth thinking about how await
and
defer
work. defer
creates
a callback function that, when called, continues
the program after the await
block.
The defer
function is called a
continuation because it continues the program
where it left off.
Chained callbacks to do multistep interactions
can be done explicitly.
Polygon Revisited does
exactly the same thing as
Polygon To Order: the
nested function is called when readnum
completes, and the innermost function is run
after the second readnum
is done.
Think about how a loop such as in Guess
could be done without await
.
write '5' + '3' write Number('5') + Number('3')
counter = 0 write ++counter + 'a' write (counter += 1) + 'b' write (counter = counter + 1) + 'c'
area = (radius) -> Math.PI * radius * radius circumference = (radius) -> 2 * Math.PI * radius for r in [1, 5, 10] write 'radius ' + r write 'a ' + area r write 'c ' + circumference r
hypotenuse = (a, b) -> Math.sqrt(a * a + b * b) write hypotenuse 3, 4 write hypotenuse 5, 12 write hypotenuse 10, 10 write Math.floor(hypotenuse(10, 10))
gcf = (a, b) -> if a > b return gcf b, a remainder = b % a if remainder is 0 return a gcf remainder, a for x in [80..88] write "gcf(120,#{x})=" + gcf(120, x)
In CoffeeScript, numbers are unquoted.
The language treats numbers and strings differently:
5 + 3
is 8, while '5' + '3'
is "53".
Strings can be parsed to numbers using the Number
function; the String
function does the opposite.
CoffeeScript allows numbers and strings to be mixed, but you should be careful when doing it. Adding a number to a string will convert the number to a string and attach it. Multiplying a number by a string will convert the string to a number and do the numerical product.
There are three types of statements that change the value of a variable.
++counter
the increment operator.
Putting ++
before the variable name increments
the value before it is used, and putting ++
after
the variable increments it after it is used. The --
decrement is similar.
counter += 1
the sum assignment operator, which
changes a variable by adding a value. There are also -=
,
*=
, and /=
operators.
counter = counter + 1
the ordinary assignment operator.
Notice that the right hand side is computed
before the left hand side is changed.
CoffeeScript uses IEEE 754
"double-precision" floating-point numbers,
which means numbers are stored using 64 bits.
Scientific notation is written with an e+
or e-
followed by a power of 10:
1e+6
is one million
and 1e-9
is one billionth.
There are 15 digits of precision, and
every integer up to 9,007,199,254,740,992
can be written exactly.
There are also special Infinity
and NaN
("Not a Number") values.
However, not every
real number can be represented exactly:
the next number after zero is
5e-324
and the largest number
is 1.79e+308
.
The limits are expansive, so for most practical purposes, you can treat CoffeeScript numbers as if they were real numbers.
The modulo operator x % y
computes
the remainder of x
when divided by y
.
In other words, it removes the largest integer multiple of
y
from x
and returns the remainder.
The modulo operator is useful for divisibility tests:
x % y
is zero if x
is divisible
by y
. Euclid's famous algorithm uses
the modulo operator to efficiently compute greatest
common factors.
power = (x, p) -> answer = 1 answer *= x for i in [0...p] return answer for n in [1..5] write power(2, n)
write Math.pow(2, 5) write Math.pow(2, 0.5)
factorial = (x) -> if x < 1 then 1 else x * factorial(x - 1) for x in [1..4] write factorial x
fib = (n) -> if n <= 2 1 else fib(n - 1) + fib(n - 2) for x in [3..8] write fib x
mandelbrot = (n, c, z) -> if n is 0 or z.r*z.r + z.i*z.i > 4 return n else return mandelbrot n - 1, c, r: c.r + z.r*z.r - z.i*z.i i: c.i + 2*z.r*z.i speed 100 ht() scale 150 s = 0.05 for x in [-2..1] by s for y in [-1.5..1.5] by s n = mandelbrot 20, {r:x,i:y}, {r:x,i:y} moveto x, y dot hsl(100, 1, n/20), s
The Math
object provides constants and functions
you would find on a scientific calculator. A partial list:
Math.E
the natural logarithm base, 2.71828...
Math.PI
the circular ratio, 3.14159...
Math.abs(x)
absolute value of x.
Math.round(x)
round x to the nearest integer.
Math.floor(x)
round x down.
Math.ceil(x)
round x up.
Math.max(x, y)
the greater of x and y.
Math.min(x, y)
the lesser of x and y.
Math.sqrt(x)
the square root of x.
Math.pow(x, y)
x raised to the power y.
Math.log(x)
the natural logarithm of x.
Math.sin(x)
the sine of x (in radians).
Math.cos(x)
the cosine of x (in radians).
Math.atan(x)
the arctangent of x (in radians).
Other mathematical functions can be built yourself.
The output, or return value, of a CoffeeScript
function is the last value computed in the function.
The statement return n
ends a function
with the return value n
.
The functions fib
and factorial
are are recursive: they refer to themselves in their own
definition. When writing a recursive function it is
important that the recursion ends at a base case
(such as where fib
defines the value as 1 when
n <= 2
).
Recursion without a base case will loop forever and freeze up. There must be initial values for which the function does not depend on itself.
Although the built-in numbers represent reals,
complex numbers can be represented as pairs of numbers.
In Mandelbrot,
the parameters c
and z
are complex numbers represented by objects that
each contain an r
and i
property.
That example uses scale 150
to grow the turtle
by 150-fold. The hsl
function
generates colors based on hue, saturation, and lightness.
Mathematical algorithms have a long and fascinating history. It is worth researching how Mandelbrot's remarkable fractal works; how Gauss's Gamma function generalizes factorials to all numbers; and how the Fibonacci sequence relates to sunflower seeds and the golden mean.
startpos = pageX: 80 pageY: 10 moveto startpos pen coral moveto pageX: 30 pageY: 50 moveto {pageX: 160, pageY: 50}
figure = [ {c: dimgray, x: 75, y: 12} {c: gray, x: 0, y: 78} {c: dimgray, x: -75, y: 5} {c: gray, x: -35, y: -18} {c: plum, x: 0, y: -62} {c: gray, x: 35, y: -15} {c: black, x: 0, y: 95} ] for line in figure pen line.c slide line.x, line.y
points = a: 1, e: 1, i: 1, l: 1, n: 1, o: 1, r: 1, s: 1, t: 1, u: 1 d: 2, g: 2, b: 3, c: 3, m: 3, p: 3, f: 4, h: 4, v: 4, w: 4, y: 4 k: 5, j: 8, x: 8, q: 10, z: 10 score = (word) -> total = 0 for letter in word total += points[letter] write "#{word}: #{total}" score x for x in ['bison', 'armadillo', 'giraffe', 'zebra']
memo = sum: 0 count: 0 add: (x) -> @sum += x; @count += 1 stats: -> write "Total #{this.sum} / #{this.count}" write "Average #{this.sum / this.count}" memo.add(n) for n in [40..50] memo.stats()
An object is a value that has its own properties.
Each property of an object associates a name with a value.
The object startpos
has two properties: pageX
,
which has value 80, and pageY
, which is 10.
The moveto
function understands objects with a
pageX
and pageY
property as
a "page coordinate." (Page coordinates measure distances
from the top-left corner of the page instead of from the center.)
In the Page Coordinate example, we can see that there are two styles for writing object literals in CoffeeScript. Each property can be put on separate lines, indented (YAML style); or the properties can be enclosed in curly braces and separated by commas (JSON style). The two styles are equivalent, and the program uses both.
The properties of an object are referenced using a dot:
line.x
refers to the value of the property named "x"
in the object named "line".
The most common use of objects is as a way of encapsulating a packet of related data together: in Figure, each object bundles the data needed for one line: a color and an x, y displacement.
A property name can be any string, so an object can be used as an associative array that defines a map from strings to values.
In Scoring, points
maps letters to point values. The square
bracket notation points[letter]
means
"look up the value of the property whose name is
the value of letter
."
Properties of an object may be changed by assigning
a value using the normal =
or +=
or
++
variable-setting operators. (Changing a property
of an object is sometimes called mutation.)
Properties of an object that happen to be functions are
called methods. Methods are particularly useful,
because they can use the word this
or the
symbol @
to refer to the object on which the
method was called.
(Note that the line
memo.add for n in [40..50]
puts the for
at the end of the statement in order to repeat it.)
It is common to write methods like memo.add
that mutate several properties of the object at once, or
methods like memo.stats
that
do computation summarizing the properties
of the object.
story = [ 'Exclamation?' '! he said ' 'adverb?' ' as he jumped into his convertible ' 'noun?' ' and drove off with his ' 'adjective?' ' wife.' ] for i in [0...story.length] by 2 prompt = story[i] await read prompt, defer answer story[i] = answer write story.join ''
primes = [] candidate = 2 while primes.length < 10 composite = false for p in primes if candidate % p is 0 composite = true break if not composite primes.push candidate write candidate candidate = candidate + 1
stack = [] pen green speed Infinity button 'R', -> rt 30 button 'F', -> fd 10 button 'Push', -> dot crimson stack.push [getxy(), direction()] button 'Pop', -> if not stack.length then home(); return [xy, b] = stack.pop() jumpto xy turnto b dot pink
Arrays are objects that contain
a sequence of values. Throughout this book we have
used arrays for iteration in for
loops;
arrays are used wherever a program needs to organize
sequential data.
The i
th element of an array story
is story[i]
, and
the number of elements is story.length
.
Indexing is zero-based, so the first element is
story[0]
and the last is
story[story.length - 1]
.
All the elements of an array can be joined together in one big
string by story.join ''
. The argument is the "glue"
put between the elements.
The statement await read prompt, defer answer
pauses the program until the read
is done.
defer answer
is a continuation function that
resumes the program after putting
the result in answer
.
A program can use push
to add elements to the end
of an array.
Primes starts with primes = []
as an empty array, and then it calls primes.push candidate
to add each discovered prime to the array of divisors to check.
This ancient algorithm is the Sieve of Eratosthenes.
Arrays have a pop
method that reverses of push
by removing and
returning the last value.
An array used by pushing and popping
is called a stack.
It is common to use a stack of objects to undo
a sequence. In Push and Pop,
stack
is an array where every element is
a turtle position.
Each element is itself a two-element array containing
an [x, y] (itself another array) and a numerical direction.
getxy()
returns the turtle's current
[x, y] as an array of two numbers.
direction()
returns the
current direction of the turtle in degrees.
stack.push [getxy(), direction()]
reads
the turtle's
current xy coordinates and its current direction, forms an array
with the results, and pushes it on the stack
.
[xy, b] = stack.pop
removes the last element from
the stack
(the element is itself an array),
and assigns the first item within of the element to the variable
xy
and the second item in element to b
.
The form [xy, b] = value
is called a
destructuring assignment. It is a concise way
to give local variable names to the elements of a short array.
spiral = (x) -> if x > 0 fd x * 10 rt 90 spiral x - 1 lt 90 bk x * 10 pen red spiral 10
speed 1000 fern = (x) -> if x > 1 fd x rt 95 fern x * .4 lt 190 fern x * .4 rt 100 fern x * .8 lt 5 bk x pen green fern 50
speed Infinity flake = (x) -> if x < 3 then fd x else flake x / 3 lt 60 flake x / 3 rt 120 flake x / 3 lt 60 flake x / 3 pen 'path' for s in [1..3] flake 150 rt 120 fill 'azure strokeStyle navy'
Recursive functions refer to themselves, and they can achieve powerful effects. Recursion is at the core of fractals, language, and reasoning.
Operationally, recursion works by stepping through a stack of work. Consider the sequence as Spiral draws a shape and retraces it back.
spiral 10 sets x to 10 rt 90; fd x * 10; spiral x - 1 ⇓ lt 90; bk x * 10 ⇑spiral 9 sets x to 9 rt 90; fd x * 10; spiral x - 1 ⇓ lt 90; bk x * 10 ⇑spiral 8 sets x to 8 rt 90; fd x * 10; spiral x - 1 ⇓ lt 90; bk x * 10 ⇑... etc, until the base case spiral 0 ⇑
Each time spiral
is called, it puts the previous
call on hold and does the smaller spiral. After the smaller
spiral is done, it returns to finish work on the bigger one.
spiral 0
does nothing:
that is called the base case.
The x
at different levels
are local variables that do not interfere with
each other. Each red box is a stack frame with
its own "copy" of x
.
Conceptually, recursion reduces a problem to smaller cases. Consider how Fern draws a large fern by assuming it can draw smaller ferns:
slide 60, -5 label '⇒' jump -110, -110 fern = (x, depth, boxd) -> if x > 1 fd x rt 95 fern x * .4, depth + 1, boxd lt 190 fern x * .4, depth + 1, boxd rt 100 fern x * .8, depth + 1, boxd lt 5 bk x if depth is boxd pen red rt 90 fd 1.7 * x lt 90 fd 4.5 * x lt 90 fd 3.5 * x lt 90 fd 4.5 * x lt 90 fd 1.8 * x lt 90 pen green pen green fern 50, 0, 1 hatch().moveto(turtle) jump 220, 0 fern 50, 0, 0
All fern
does is draw a stem with
three smaller ferns at the end. The main caveat
is that the reduction has a limit: it ends when x ≤ 1.
Both Spiral and Fern return the turtle to exactly the same position and direction at the end of a function call. Maintaining an invariant like this can make recursion much easier to understand.
onedice = -> random [1..6] twodice = -> onedice() + onedice() for n in [1..5] write twodice()
for n in [1..20] fd 10 rt random(181) - 90 dot gray, 5
for n in [1..14] pen random [red,black,blue] fd random 70 rt 90
for n in [1..300] moveto random position dot random color
for n in [1..2] write Math.random()
c = [0, 0, 0, 0, 0, 0] for n in [1..500] heads = 0 for flips in [1..5] heads += random 2 c[heads] += 1 for h of c b = write h + ":" + c[h] b.css background: skyblue width: c[h]
Most functions and programs are designed to be deterministic, which means they run exactly the same way given the same input.
The random
function is different. Each time it runs,
it produces random results that are unpredictable. Here are several ways
of using random
:
random [1..6]
chooses a random member of a list.
random 70
chooses a random integer from 0 to 69.
random position
picks a random screen position.
random color
picks a random color.
random normal
picks a normally distributed number.
All of these are built using the lower-level function:
Math.random()
returns a random number between
0.0 and 1.0.
Randomness is a subtle concept. Think about flipping a coin five times:
flips = random ['heads', 'tails'] for [1..5]
random
function is careful to pick between
choices with equal probability, and our coin flip will be perfectly fair.
So we might be surprised to run this program and get
['heads', 'heads', 'heads', 'heads', 'heads']
However, we should not be too surprised: there are 32 possible sequences of heads and tails, and each of those sequences is just as likely as any others. An output of five heads is just as "random" as any other sequence of heads and tails.
If we repeated five coin flips five hundred times, we expect to see "all heads" about 500/32=15.625 times. Five Flips illustrates this effect.
Yet the principle of unpredictability holds even when
repeating a random process 500 times: we should not
be too surprised if we obtain "all heads" much more or less
often than 15 or 16 times. With random
,
rare coincidences will happen if you try often enough.
Another remarkable fact about randomness is the
Central Limit Theorem: if you average enough
random events - regardless of bias - the distribution
of the average will be guaranteed to form a precise bell
curve, called a Gaussian, or Normal distribution.
random normal
directly
generates random numbers according to this
bell curve: it produces random numbers from
the normal distribution with a
mean of zero and a variance of one.
The reliable unpredictability of random
makes it indispensable for games, simulations, and
cryptography.
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 thing
to 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 parameters:
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. The function call random normal
returns a
normally distributed random number with mean 0 and
variance 1.
The program 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. For example,
$('.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.
text = """If you can look into the seeds of time And say which grain will grow and which will not, Speak, then, to me."""
see text.indexOf 'which' see text.substr 47, 7
see 'charCode', text.charCodeAt(0) see 'string', String.fromCharCode(73) for x in [88, 188, 9988] see x, String.fromCharCode(x)
see text.match /w....g.../ see text.match /[a-z][a-z]/ see text.match /\s[a-z][a-z]\s/ see text.match /\b[a-z][a-z]\b/ see text.match /\b[a-z][a-z]\b/gi see text.match /\b[gn][a-z]*\b/g see text.match /z/
lines = text.split /\n/ see lines[2] words = text.split /\s+/ see words[0..2]
pattern = /\b([a-z]+) of ([a-z]+)\b/ matched = pattern.exec text for g in [0..2] see "group #{g}: #{matched[g]}"
r = text.replace /[A-Z][a-z]*/g, "<mark>$&</mark>" r = r.replace /\n/g, "<br>" r = r.replace /\bw[a-z]*\b/g, (x) -> x.toUpperCase() write r
String algorithms to locate patterns in text are a fundamental tool for understanding written language.
Strings are arrays of characters. The Unicode character set supports textual communication around the world, so it includes characters for every international alphabet, every Asian pictographic word, and every common mathematical symbol.
Unicode assigns a number to every character.
String.fromCharCode
gets the character
for the number, and text.charCodeAt
gets
the number for a character.
Numbers up to 127 are ASCII
codes that cover American English:
65 is uppercase A, 122 is lowercase z, 48 is the
0 digit, 32 is the space, and 36 is the $ dollar symbol.
The simplest way to match text is to find an exact
substring: text.indexOf
returns the location
of the first occurrence of the given substring in
text
, or -1
if none was found.
Conversely, if you have an index of interest,
text.substr x, len
returns the len
characters starting at index x
.
Regular expressions, are flexible and precise
text patterns written between pairs of slash
/.../
delimiters. Regular expression
syntax is a whole language that is the topic of several
good books and websites. Here are a few basics:
(abc)*
matches zero or more repetitions of abc.
[abc]
matches either an a or b or c.
a..c
matches a followed by two characters then c.
ab+c
matches a, then one or more b's, then c.
[a-z]{3}
matches three lowercase letters.
\d*
matches zero or more digits.
x\s+
matches x followed by one or more spaces.
z\b
matches a z followed by a word boundary.
The text.match
method returns matching substrings.
Normally, the first match is found,
but if the letter g
(for "global") follows the pattern then all matching
substrings are returned. There are other useful suffixes:
i
makes the match case-insensitive.
The pattern.exec
method extracts of
submatches within parentheses in the pattern, and
the text.replace
method replaces
matches with a new string.
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 + direction 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 thee examples on this page demonstrate how to simulate motion: a bouncing turtle, a game of tag, and an orbiting planet.
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 acceleration due to gravity is simulated by a slight
change in velocity on each tick: vy -= 1
.
The sudden acceleration 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
direction
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 accelerated 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.
direction(b)
the direction from the turtle to b
.
distance(sun)
the distance from the turtle to sun
.
b = hatch blue r = hatch red b.lt 90; b.pen blue b.play 'g' b.rt 170, 50 b.dot 50, blue r.rt 90; r.pen red r.play 'd' r.lt 170, 50 r.dot 50, red
dot orange, 220 dot white, 180 jump 100, 0 pen skyblue while true fd 3 + random 3 await done defer() if touches orange lt 5 else rt 5
shared = { d: 0 } do -> while true await read defer shared.d do -> pen red while true fd 10 await done defer() rt shared.d
button 'send color', -> send 'go', random color do -> for x in [1..25] await recv 'go', defer c pen c fd 50 rt 88, 10
A thread is a sequence in a program that runs in parallel to other code. Iced CoffeeScript has cooperative threads, which means that:
await
statements.
Some new idioms that appear in these examples:
b = hatch blue
hatches a new turtle, wearing blue.
b.lt 90
tells the turtle b
to turn.
await done defer()
waits until turtles stop moving.
send 'go'
sends a message 'go'.
await recv 'go', defer()
waits until 'go' is received.
while true
repeats the enclosed code forever.
touches orange
tests if the turtle touches
any orange.
do ->
runs the enclosed code as a function.
In Race Condition, the second turtle to arrive will draw a dot that covers the first dot. The turtles run concurrently, and it is is not possible to predict which parallel turtle will arrive first.
If order is important, insert await b.done defer()
before calling r
. The program will
wait for b
to finish before moving
the red turtle.
An await
only pauses the current function, not
its caller. That is why the last two
examples run threads in parallel.
It is important to let a turtle finish moving before reading
its state. If Line Follower
did not await done defer()
to let the turtle finish
moving forward before checking the touched color, the turtle
would still be in its start position when check is done.
Even if not reading turtle state, an infinite
while true
loop should contain an
await done defer()
so that other threads
get a turn.
Threads can communicate using shared memory
(sharing a common variable) or message passing
(sending a value from one to the other). If a shared
variable changes very quickly or slowly, the thread
that reads the variable can skip a value or read the
same value twice. On the other hand, a thread that
uses await recv
will
wait to receive each message sent by send
exactly once without duplication or omission.
pen blue, 10 fd 100; rt 90 pen pink, 3 fd 50; rt 90 pen 'orange ' + 'lineWidth 10 ' + 'lineCap square' fd 100; rt 90 pen black fd 50
text = write 'Outlined.' text.css { border: '2px solid red' } turtle.css { border: '3px dotted blue' }
h = write 'Fancy!' h.css font: '55px Helvetica' fontStyle: 'italic'
write 'Before' d = write 'Decorated' write 'After' d.css display: 'inline-block' cursor: 'pointer' padding: '10px' margin: '-5px' opacity: '0.7' color: 'white' fontSize: '110%' letterSpacing: '5px' textDecoration: 'underline' boxShadow: '1px 1px black' background: 'mediumaquamarine' transform: 'rotate(10deg)translateX(20px)'
About styles. TBD.
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
write """<style> h2 { color: red; } h3 { background: bisque; } </style> """ write "<h2>Stylesheet</h2>" write "<h3>Tag Styles</h3>" write "<h3>style specific tags</h3>"
write """ <style> .a { text-decoration: underline; } .b { font-style: italic; } </style> """ write "<p class='a'>Class a</p>" write "<h3 class='b'>Class b</h3>" write "<p class='b'>Classes apply to any tag.</p>"
write """ <style> i { border: 1px solid black; margin: 2px; display:inline-table } i:nth-of-type(1) { background: gold } i:nth-of-type(2n+4) { background: skyblue } i:nth-of-type(3n+9) { background: thistle } </style> """ for x in [1..24] write "<i>#{x}</i>"
write "<p><mark>a</mark>v<mark>o</mark>" + "c<mark>a</mark>d<mark>o</mark></p>" $('p').css { fontSize: '200%' } $('mark').css { background: palegreen } $('mark').animate { padding: '5px' } $('mark:nth-of-type(2n)').animate { opacity: 0.3 }
This page gives shows how to use CSS, which is a set-oriented language. The fundamental operations in CSS operate on sets: every CSS selector represents a set of HTML elements, and every selection operator transforms one set of elements to another set. With CSS selectors, it is easy to select any useful set of elements without writing a loop.
A typical HTML document contains a tree structure like this:
<body> <h1>...</h1> <p>...</p> <p>...<i>..</i>..<i>..</i>...</p> <p>...<i>..</i>...</p> <h2>...<i>..</i>...</h2> </body>
The <body>
element contains five children:
one <h1>
, three <p>
, and
one <h2>
. Some of those children in turn have
further children: this example has four <i>
descendants.
CSS selectors name a tag to select all elements with that tag.
The selector p
selects the three
<p>
elements in the document, and the
selector i
selects all four <i>
elements. In CSS, curly braces following a selector contain
CSS styles to apply to the selected elements. To color the
text within every <i> element, we could write
i { color: blue }
.
The result of a selector is always a set, and there
is nothing wrong with selecting a singleton or empty set:
h2
selects a single element, and
h3
selects the empty set. Some more techniques:
Filtering by Ancestry - CSS selectors composed
in sequence narrow sets according to ancestry. For example p i
selects only those
<i>
elements that are contained within
<p>
.
CSS Classes -
Elements with class
attributes can be
selected using match dot selectors such as .a
.
Advanced Operators
such as :nth-of-type
can
select elements according to their ordinal position or other
properties.
jQuery's $ Function
The jQuery function $(
...)
is
a bridge between set-oriented selectors and object-oriented
code. $('p')
creates a single CoffeeScript object
that represents the whole set elements selected by p
.
jQuery provides a bridge between the
set-oriented CSS language and the the object-oriented
CoffeeScript language.
The jQuery library provides a set of methods for all set objects, including functions for changing and animating CSS properties interactively.
$(document).click (event) -> see event if event.shiftKey pen blue else pen null moveto event
pen plum [L, R, U, D] = [37, 39, 38, 40] keydown (event) -> if event.which is L then lt 5 if event.which is R then rt 5 if event.which is U then fd 5 if event.which is D then bk 5
t = write "<button>Touch This</button>" t.speed Infinity t.moveto document t.mousemove (event) -> t.rt random(91) - 45 while t.touches(event) t.bk 1
speed Infinity turtle.remove() t = write '<img>' t.home() start = -> t.wear 'openicon:magic-tophat' tick off t.click (event) -> play() play = -> t.wear 'openicon:animals-rabbit' tick -> t.moveto random 'position' t.click (event) -> start() start()
About events. TBD.
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
choices = (menu, sofar = []) -> if menu.length is 0 write sofar.join ' ' else for item in menu[0] choices menu[1...], sofar.concat item choices [ ['small', 'medium', 'large'] ['vanilla', 'chocolate'] ['cone', 'cup'] ]
suits = ['\u2663', '\u2666', '\u2665', '\u2660'] deck = [] for v in [2..10].concat ['J', 'Q', 'K', 'A'] deck.push (v + s for s in suits)... shuffle = (d) -> for i in [1...d.length] choice = random(i + 1) [d[i], d[choice]] = [d[choice], d[i]] deal = (d, n) -> d.splice(-n) shuffle deck for x in [1..3] write deal(deck, 5).join('/')
key = 13 a2z = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' rot = a2z[key...].concat a2z[...key] box = write '<input>' out = write '' box.keyup -> result = for c in box.val() char = c.toUpperCase() if char in a2z rot[a2z.indexOf char] else char out.text result.join ''
About slicing. TBD.
The concat
method creates an array that
joins the elements of two arrays together.
[2...10].concat ['J', 'Q', 'K', 'A']
forms
an array starting with the numbers 2 through 10 followed
by the strings "J", "Q", "K", and "A".
The parenthesized loop (v + s for s in suits)
is a list comprehension that creates an array
using a for loop. The array consists of each value
computed by the loop, in sequence.
Shuffle applies the Fisher-Yates algorithm for shuffling a deck of cards. The algorithm is called a "perfect shuffle" because every permutation of the deck is equally likely.
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
list = (random 10 for x in [1..8]) list.sort() write list
show = (points, highlight) -> render = for k, v of points if Number(k) in highlight "<mark>#{v}</mark>" else "#{v}" write "<div>#{render.join ','}</div>" list = 'SORTME'.split '' show list, [] for i in [0 ... list.length - 1] for j in [i + 1 ... list.length] if list[i] > list[j] [list[i], list[j]] = [list[j], list[i]] show list, [i, j]
sketch = (points) -> cg() pen null for p in points moveto p pen red dot black array = [] button 'scatter', -> array = for x in [1..10] random 'position' sketch array button 'sort', -> array.sort (a, b) -> a.pageX - b.pageX sketch array
About sorting. TBD.
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
[width, height] = [9, 9] grid = table(width, height).home() sides = [ {dx: 0, dy: -1, ob: 'borderTop', ib: 'borderBottom'} {dx: 1, dy: 0, ob: 'borderRight', ib: 'borderLeft'} {dx: 0, dy: 1, ob: 'borderBottom', ib: 'borderTop'} {dx: -1, dy: 0, ob: 'borderLeft', ib: 'borderRight'} ] isopen = (x, y, side) -> return /none/.test( grid.cell(y, x).css side.ob) isbox = (x, y) -> return false unless ( 0 <= x < width and 0 <= y < height) for s in sides if isopen x, y, s return false return true makemaze = (x, y) -> loop adj = (s for s in sides when isbox x + s.dx, y + s.dy) if adj.length is 0 then return choice = random adj [nx, ny] = [x + choice.dx, y + choice.dy] grid.cell(y, x).css choice.ob, 'none' grid.cell(ny, nx).css choice.ib, 'none' makemaze nx, ny wander = (x, y, lastdir) -> moveto grid.cell y, x for d in [lastdir + 3 .. lastdir + 7] dir = d % 4 s = sides[dir] if isopen x, y, s then break turnto grid.cell y + s.dy, x + s.dx unless dir is lastdir plan -> wander x + s.dx, y + s.dy, dir makemaze 0, 0 speed 5 wander 4, 4, 0
About search. TBD.
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
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}
In this section, we use Pencil Code to make a game of hangman, starting from scratch, explaining all the basic concepts of programming in Iced CoffeeScript along the way. The approach in Part 2 is different from the first part the book. Here, we take a step-by-step approach, explaining the thinking behind every phase of the project-building process.
While following the tutorial, it may be useful to consult the online help:
test panel (type help for help) > help help is available for: bk cg cs ct fd ht if ln lt rt st abs cosd dot ... >
Throughout the tutorial, suggestions on what to type are highlighted in white boxes, like the idea to type "help" above.
It takes a couple hours to learn enough programming to make hangman.
We will learn about:
At the end we will have a game we can play.
Start up a browser and go to pencilcode.net. Click on "Let's Play!" to go to the editor. Our screen should look something like this:
The left hand side of the screen is a program editor, and the right hand side of the screen is where we can run programs. The bottom half of the right side is a test panel where we can type code to run right away.
The test panel should show something like this:
test panel (type help for help) >
The > means Pencil Code is waiting for us to enter some CoffeeScript.
CoffeeScript can remember things, so let's use the test panel to tell it to remember our secret word.
test panel (type help for help) > secret = 'crocodile' "crocodile" >
We can display a value with write.
> write secret (crocodile appears above) >
We can also show a value in the test panel by entering its name.
> secret crocodile >
CoffeeScript will remember the secret until we reload the page. So we can always go back and print our secret again by saying "secret" again.
Now try something CoffeeScript doesn't know.
> number
▶number is not defined
>
Don't worry. This is fine. We can just teach CoffeeScript what "number" is and try again.
> number = 43 43 > number 43 >
A computer is better than any calculator at doing math. Let's try.
> 2+33+66 101 > 33333333 * 44444444 1481481451851852 > n=123456789 1234567889 > n*n*n 1.8816763717891548e+24 > n += 1 123456790 >
The e+24 at the end of the last number is the way that large numbers are written in CoffeeScript. It means 1.8816763717891548 × 1024. CoffeeScript calculates numbers with 15 digits of precision.
In CoffeeScript, plus and minus are normal but times and divide are done using the * and / symbol. Some other symbols to know:
|
|
|
These operations can be combined.
What will it do when we say "String(99 * 123).length"?
CoffeeScript obeys the same order of operations used in Algebra.
What will it say for (2 * 3 + 3 * 5) / 7 - 1?
Try your own fancy formulas. Don't worry if you get errors.
What do you think happens when we try to do addition with words?
> 'dog' + 'cat' dogcat > 'dog' + 5 dog5 > 34 + 5 39 > '34' + 5 345 >
If we add something to a word, it sticks it on and makes a longer word. When we put something inside quotes, CoffeeScript treats the value like a string of letters, even if they are all digits! That is why '34' + 5 is 345. Quoted values like this are called "strings."
The Number() function can be used to convert a string to a number, so that we can do ordinary arithmetic with it.
The String() function is opposite, and turns numbers into strings.
> Number('34') + 5 39 > String(34) + 5 345 > Number('dog') + 5 NaN >
If we try to convert a string to a number in a way that does not make sense, we get the special value NaN, which stands for "Not a Number".
In Pencil Code, we can create graphics by using the turtle. There are five basic turtle functions:
code | meaning |
---|---|
pen red | chooses a pen color |
fd 100 | moves forward by some pixels |
rt 90 | turns right by some degrees |
lt 90 | turns left by some degrees |
bk 50 | slides back by some pixels |
In the test panel, enter two commands to draw a line:
> pen red > fd 100 >
Try turning the turtle and drawing another line. Notice that rt turns the turtle in place, and we need to move the turtle with fd to draw a corner.
> rt 90 > fd 50 >
Read about the rt function using help:
> help rt rt(degrees) Right turn. Pivots clockwise by some degrees: rt 90 rt(degrees, radius) Right arc. Pivots with a turning radius: rt 90, 50 >
If we give a second number to rt, the turtle will move while turning and form an arc. Try making a circle:
> rt 360, 100 >
Remember to put a comma between the two numbers.
A program is just a saved list of commands that are run in order. Let's set up a hangman game.
Here we will begin working with the editor on the left side of Pencil Code.
pen blue fd 150 rt 90 fd 50 rt 90 fd 20
When we press the triangluar play button in the middle, the turtle will trace out a shape, one step at a time.
If it doesn't work, check the typing carefully and try again. Things to watch out for:
Each time we run the program, it clears the screen and starts again.
Now, rename the program from "first" to "hangman" by editing the name next to the pencil. Save it with the button at the top right.
A website will be created with your account name. When I chose the name "newbie", a website was created at "newbie.pencilcode.net".
Once you have saved the program with the name "hangman", it is available at two different addresses on pencilcode, like this:
You can share these websites with anybody.
Write a welcome message after drawing the hangman shape:
pen blue fd 150 rt 90 fd 50 rt 90 fd 20 write 'time to play hangman'
Notice that the Pencil Code Turtle is as slow as a turtle! Unless we speed it up with the speed function, the turtle takes its own slow time long after we have asked it to move, and the welcome message appears before the turtle is finished.
We can do two things to help with the slow turtle:
speed 10 pen blue fd 150 rt 90 fd 50 rt 90 fd 20 await done defer() write 'time to play hangman'
Now the turtle moves faster, and the program waits until the turtle is done before writing the welcome message.
A couple things to know:
If we give the turtle speed Infinity, you will not need the "await done defer()" line, because the turtle will be done moving as soon as we ask it to move.
We can repeat steps in a program with the "for" command.
If we say "for something in something", CoffeeScript will repeat any indented lines that come next. Try adding three lines to the end of our program so that it looks like this:
write 'time to play hangman' secret = 'crocodile' for letter in secret write letter
You should see this:
time to play hangman c r o c o d i l e
The program is saying "for every letter in the secret, do this next thing." So the computer repeats "write letter" nine times, once for each letter.
If it doesn't work, check the program and make sure the line after the for is indented: that is how CoffeeScript knows which line to repeat.
Once you have the hang of it, keep the secret by changing program to write underscores:
write 'time to play hangman' for letter in secret: append '_ '
Using append instead of write puts text on the same line instead of starting a new line each time:
time to play hangman _ _ _ _ _ _ _ _ _
In our hangman game, we should show where any guessed letters are. To decide whether to print a blank line or a letter, we will need to use "if" and "else".
Try changing the program like this. There are four new lines:
write 'time to play hangman' secret = 'crocodile' guesses = 'aeiou' for letter in secret if letter in guesses append letter + ' ' else append '_ '
Don't forget to line everything up, and remember to save it.
What happens when you run it?
The line "if something in something" makes a choice.
Since the whole thing is under the "for something in something", this choice is repeated for every letter.
Our screen looks like this:
time to play hangman _ _ o _ o _ i _ e
Check the spelling and spacing and punctuation if you get errors. Take your time to get it to work.
Our game is no good if we can't guess. To let the player guess we will use a function called "read"
It works like this:
await read defer guess
This shows an input box puts the program on hold until the user enters a value for "guess".
The "await" and "defer" commands work together to pause and resume the program while waiting for an answer to be entered.
Try adding two lines to the program to add an await read, like this:
write 'time to play hangman' secret = 'crocodile' guesses = 'aeiou' write 'guess a letter' await read defer guess guesses += guess for letter in secret if letter in guesses append letter + ' ' else append '_ '
The "guesses += guess" line adds the guess to the string of guesses. If the string of guesses was "aeiou" and the new guess is "c", then the string of guesses will become "aeiouc".
Let's run it.
time to play hangman guess a letter ⇒ c c _ o _ o _ i _ e
When we run the program, it will show us where our guessed letter appears.
We need to let the player take more than one turn.
The "while" command can repeat our program until the player is out of turns.
write 'time to play hangman' secret = 'crocodile' guesses = 'aeiou' turns = 5 while turns > 0 for letter in secret if letter in guesses append letter + ' ' else append '_ ' write 'guess a letter' await read defer guess guesses += guess turns -= 1
We need to indent everything under the "while" command to make this work. So we will need to add some spaces in front of most of the program.
Let's also move the guessing after the hint instead of before.
The command "turns -= 1" means subtract one from "turns," so if it used to be 5, it will be 4. Then the next time around it will be 3 and so on. When turns is finally zero, the "while" command will stop repeating.
Try running the program. Does it work?
Any time we want to see the value of a variable, we can type its name into the test panel.
test panel (type help for help) > guesses aeioucsn > turns 2 >
We can already play our game. Now we need to fix it up so that it is fun.
Here is one way to improve it.
write 'time to play hangman' secret = 'crocodile' guesses = 'aeiou' turns = 5 while turns > 0 blanks = 0 for letter in secret if letter in guesses append letter + ' ' else append '_ ' blanks += 1 if blanks is 0 write 'You win!' break write 'guess a letter' await read defer guess guesses += guess if guess not in secret turns -= 1 write 'Nope.' write turns + ' more turns' if turns is 0 write 'The answer is ' + secret
The "blanks" number counts how many blanks we are still missing. If if is zero, it means we won, and the "break" command breaks out of the "while" section early.
The "if guess not in secret" line checks if the guess was wrong. We only count down the "turns" if our guess was wrong.
When we guess wrong, we also print a bunch of messages like "Nope" and how many more turns we have. When we are wrong for the last time we print the secret.
It will be more fun if we make our game look like Hangman.
All we need to do is draw parts of the poor hangman person when there is a wrong guess. Try adding something like this to the wrong guess part:
... write 'Nope.' write turns + ' more turns' if turns is 4 then lt 90; rt 540, 10; lt 90 if turns is 3 then fd 20; lt 45; bk 30; fd 30 if turns is 2 then rt 90; bk 30; fd 30; lt 45 if turns is 1 then fd 30 if turns is 0 rt 45; fd 30 await done defer() write 'The answer is ' + secret
The semicolons ; are just a way to put more than one step on the same line. Notice when putting the "if" on the same line as the commands to run, we must use the word "then" between the test and the commands.
Try making variations on the hangman drawings for each step.
Whenever we want to pause the program to wait for the turtle to finish drawing, we can use "await done defer()". This starts the done function and resumes the program after it has completed.
pen blue fd 150 rt 90 fd 50 rt 90 fd 20 lt 90; rt 540, 10; lt 90 fd 20; lt 45; bk 30; fd 30 rt 90; bk 30; fd 30; lt 45; fd 30 rt 45; fd 30
The only problem with the game is that it always plays the same secret word. We should use the random function to choose a random word.
Change the line that sets the secret so that it looks like this:
... write 'time to play hangman' secret = random ['tiger', 'panda', 'mouse'] guesses = 'aeiou' ...
The square brackets [ ] and commas make a list, and the random function picks one thing randomly from the list.
Of course, we can make the list as long as we like. Here is a longer list:
... print 'time to play hangman' secret = random [ 'crocodile' 'elephant' 'penguin' 'pelican' 'leopard' 'hamster' ] ...
We can write a long list on lots of lines like this, as long as we remember to end any brackets [] that we started. When we list items on their own lines, the commas are optional.
There is a longer list of animals on the internet at the address http://pencilcode.net/data/animals.
We can load this data in CoffeeScript using a jQuery function "$.get". (The dollar is the object used by the jQuery library to bundle more than one hundred functions that are very useful for web apps. Read more about jQuery functions at learn.jqery.com.)
The code looks like this:
... write 'time to play hangman' await $.get 'http://pencilcode.net/data/animals', defer animals secret = random animals.split '\n' ...
What this means is:
await $.get 'http://pencilcode.net/data/animals', defer animals
await $.get 'http://pencilcode.net/data/animals', defer animals
await $.get 'http://pencilcode.net/data/animals', defer animals
secret = random animals.split '\n'
secret = random animals.split '\n'
secret = random animals.split '\n'
secret = random animals.split '\n'
Here is the whole program from beginning to end:
speed 10 pen blue fd 150 rt 90 fd 50 rt 90 fd 20 await done defer() write 'time to play hangman' await $.get 'http://pencilcode.net/data/animals', defer animals secret = random animals.split '\n' guesses = 'aeiou' turns = 5 while turns > 0 blanks = 0 for letter in secret if letter in guesses append letter + ' ' else append '_ ' blanks += 1 if blanks is 0 write 'You win!' break write 'guess a letter' await read defer guess guesses += guess if guess not in secret turns -= 1 write 'Nope.' write turns + ' more turns' if turns is 4 then lt 90; rt 540, 10; lt 90 if turns is 3 then fd 20; lt 45; bk 30; fd 30 if turns is 2 then rt 90; bk 30; fd 30; lt 45; fd 30 if turns is 1 then rt 45; fd 30 if turns is 0 bk 30; lt 90; fd 30 await done defer() write 'The answer is ' + secret
The best part of programming is customizing your program to make it your own. Can you make your hangman game harder or easier? Can you make it automatically play again at the end of the game?
Here are a few other places to go to learn more.
Learn more about programming in CoffeeScript with the book Smooth Coffeescript, by E. Hoigaard (based on the book Eloquent Javascript, by Marijn Haverbeke).
The await and defer keywords are explained well on Max Krohn's Iced CoffeeScript homepage (search on Google).
The website guide.pencilcode.net has more example programs and reference material to use with Pencil Code.
Pencil Code is based on open web standards HTML5 and CSS3. HTML is a rich subject. There are more than 100 types of HTML elements, more than 100 HTML attributes, more than 100 CSS properties, and an expanding set of APIs. The best way to explore all these options is to search on Google and consult the many resources on the Internet about these standards.
Pencil Code is also built on jQuery, which is the most popular open-source AJAX library for building browser-based web applications. Learn about jQuery at learn.jquery.com.
When you have further questions, turn to the Pencil Code discussion group at pencilcode.net/group, or look to the superb technical community on StackOverflow at stackoverflow.com.
About intelligence. TBD.
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
|
|
Colors | white | gainsboro | silver | darkgray | gray | dimgray | black |
---|---|---|---|---|---|---|
whitesmoke | lightgray | lightcoral | rosybrown | indianred | red | maroon |
snow | mistyrose | salmon | orangered | chocolate | brown | darkred |
seashell | peachpuff | tomato | darkorange | peru | firebrick | olive |
linen | bisque | darksalmon | orange | goldenrod | sienna | darkolivegreen |
oldlace | antiquewhite | coral | gold | limegreen | saddlebrown | darkgreen |
floralwhite | navajowhite | lightsalmon | darkkhaki | lime | darkgoldenrod | green |
cornsilk | blanchedalmond | sandybrown | yellow | mediumseagreen | olivedrab | forestgreen |
ivory | papayawhip | burlywood | yellowgreen | springgreen | seagreen | darkslategray |
beige | moccasin | tan | chartreuse | mediumspringgreen | lightseagreen | teal |
lightyellow | wheat | khaki | lawngreen | aqua | darkturquoise | darkcyan |
lightgoldenrodyellow | lemonchiffon | greenyellow | darkseagreen | cyan | deepskyblue | midnightblue |
honeydew | palegoldenrod | lightgreen | mediumaquamarine | cadetblue | steelblue | navy |
mintcream | palegreen | skyblue | turquoise | dodgerblue | blue | darkblue |
azure | aquamarine | lightskyblue | mediumturquoise | lightslategray | blueviolet | mediumblue |
lightcyan | paleturquoise | lightsteelblue | cornflowerblue | slategray | darkorchid | darkslateblue |
aliceblue | powderblue | thistle | mediumslateblue | royalblue | fuchsia | indigo |
ghostwhite | lightblue | plum | mediumpurple | slateblue | magenta | darkviolet |
lavender | pink | violet | orchid | mediumorchid | mediumvioletred | purple |
lavenderblush | lightpink | hotpink | palevioletred | deeppink | crimson | darkmagenta |