Drawing the Grid
The first task is to draw the tic-tac-toe grid. At the commander prompt type:
| Commander |
edit "draw.grid |
And enter the following definition:
| Editor |
to draw.grid ; ; draw the tic-tac-toe screen ; setsc [255 255 255] setpc [0 0 0] setpensize [5 5] ht pu cs seth 0 setpos [-50 -150] pd fd 300 pu setpos [50 -150] pd fd 300 pu seth 90 setpos [-150 -50] pd fd 300 pu setpos [-150 50] pd fd 300 pu end |
Extra spaces are there to make it easier to read, you should get into the
habit of putting the spaces in (in this case, there are 4 spaces at the start of
every line).
The "draw.grid" word sets the screen color to white, the pen color to black.
It sets the pen size to 5 (thick). Hides the turtle, pen-up (no drawing) and
clears the screen. We set the heading to 0 (north/up) and draw the two lines
from bottom to top. Then we set the heading to 90 (east/right) and draw the two
lines from left to right.
Press Alt+S to save this function, and now type at the commander prompt:
We should now see the grid.
Calculating Positions
We are going to split the tic-tac-toe grid into 9 numbered boxes. We will
number them as follows:
We need to create a word to convert an position number (index) to a position
(x,y) that represents the center of that box. We will call the word "pos.from.index".
Bring up the editor in the same way as before:
| Commander |
edit "pos.from.index |
And enter the following definition:
| Editor |
to pos.from.index :i ; ; get x,y position from index ; local [x y] make "x ((remainder :i 3)-1)*100 make "y ((int :i/3)-1)*100 output (list :x :y) end |
Notice the ":i" after the word in the first line? This means "pos.from.index"
requires a value after it (in this case a number). We will refer to this value
throughout the definition as ":i". (The colon ":" is
important, and indicates that this is a value). We call "i" a
variable, as the value may vary (can be different).
We want to remember two other values, an x-coordinate and a y-coordinate. We
also only want to know about these values inside the definition. The word
"local" is used for this purpose. When we want to remember the result
of something we type, we "make" a variable have a value.
We also want this word to "output" a value so it can be used to
provide values for other words. The value that the word outputs is the x-y
coordinates. Notice that we create the x-y coordinates using "(list :x
:y)". This takes the two values we calculated and pairs them together (a
pair of values is a list of 2 values).
How do we get the coordinates from the box? Mathematics! We divide the index
by three and obtain the quotient and remainder. E.g., if the index is 7, divide
7 by 3, and we get 2r1. 2*3+1 = 7. The '2' is the quotient, and the '1' is the
remainder. Type at the command prompt:
| Commander |
show remainder 7 3 1 |
You will get the answer "1". Now type at the command prompt:
| Commander |
show 7/3 2.33333333333333 |
Almost but not quite what we want. We really want '2', so we have to do this:
Better! Somehow we need to convert this to 100, the value '1' to 0, and the
value '0' to -100. Subtract 1 from the result (giving -1, 0 and 1 respectively)
and multiply by 100 (giving -100, 0, 100 respectively).
We now need to test the new word.
| Commander |
show pos.from.index 7 [0 100] show pos.from.index 8 [100 100] show pos.from.index 0 [-100 -100] show pos.from.index 4 [0 0] |
Does this look right? Yes? Good. The square brackets indicate this is a list.
You might be used to writing x-y coordinates as (100,100). In logo, it's written
as [100 100].
Drawing the Pieces
Given an index (0 to 8) we want to draw either an "O" (oh) or an
"X" (ex) in the appropriate box. We're going to write two words called
"O" and "X". (This is the letter "O" not the
number "0"). You will find out later how we can use these names.
Create the first word "O" using the editor:
| Editor |
to O :ind ; ; place O piece ; setpc [0 255 0] setpensize [3 3] pu setpos pos.from.index :ind pd circle 30 pu end |
This is very simple, we set the pen color to Green, the pen size to 3
(medium). We move the turtle to the center of the circle, using "pos.from.index"
we wrote earlier, and draw a circle of radius 30 (what is the diameter? Remember
that diameter = 2r or 2xRadius.). The box is slightly smaller than 100x100, so
we want a circle that will fit nicely in the box.
Now create the word "X":
| Editor |
to X :ind ; ; draw X piece ; local [pos x y] make "pos pos.from.index :ind make "x item 1 :pos make "y item 2 :pos setpc [255 0 0] setpensize [3 3] pu setpos (LIST :x-30 :y-30) pd setpos (LIST :x+30 :y+30) pu setpos (LIST :x-30 :y+30) pd setpos (LIST :x+30 :y-30) pu end |
We are drawing a cross that is 60x60. We use the "setpos" command
to draw between absolute positions. What other ways can you do this? Notice that
we use "make" three times. The first is to remember the result of
"pos.from.index" (we called this "pos"). We then take the x
and y coordinate from the position. Remember that the position may be [0 100],
where x is 0 and y is 100. 0 is the first item of the list (item 1), and y is
the second item of the list (item 2). We can write a list as [0 100], unless we
have to do any calculations or substitute any values, in which case we have to
write (list (1+2) (3+4)).
Once these words have been saved, try them out (think first what you should
be seeing).
We call this array "grid" so we can refer to it later. Note that
since we didn't use "local", the variables can be seen outside of this
word. Each box will have one of three values, "?" (empty),
"X" (X there) or "O" (O there). We use the variable
"turn" to determine who's currently playing. We set this to
"O" as we determined that "O" plays first.
The next line beginning with the word "for" will go through the
numbers 0, 1, 2, 3, 4, 5, 6, 7 and 8 (for each box in the grid). We want to set
the value of each box to "?" and put them into the grid. Since
"grid" is an array, we have to use "setitem" to set the
values.
Finally we call "draw.grid" to draw the grid. You can try out this
word as follows: