Hello, YouTube. Welcome to Coding Together. In the previous episode, I
programmed the snake game in Applesoft BASIC on this
Apple II Plus computer by printing text to the screen. Today, I am so
excited because guess what we're going to do today? Computer graphics. (SINGING) Coding together. We'll always be best
friends forever. I'll be there whenever. If you feel down, then
I'll make it better. Know what to measure. The for loop we have,
the joy, the pleasure. So let's go to
enjoy the weather. These are the days
I'll always treasure. Oh, baby, we're coding together. We'll always be best
friends forever. Coding together. Coding together. I've got everything I need
here, my Apple II reference manual, my basic programming
reference manual for Applesoft BASIC, and my brand
new, just-released, published-in-1979
Applesoft tutorial. Just to jog your memory, to
print text to the screen, I use the Print command. If I want to draw to the
screen instead of using print, I can enter graphics mode
with the command, GR. Let's take a look
in the tutorial about how graphics mode works. The first thing it's
showing me how to do is how to set the TV color. Now, one of these
days, I'm going to get my hands on a color TV. But for now, I just have
this monochrome monitor. Good news for you, the
way that I'm actually capturing the output
of the Apple II through an analog
to HDMI converter and into a modern PC with
Open Broadcast Studio, even though I can't
see it, you'll actually see the full
color in the video that you're watching right now. One of the nice things
about graphics mode is that I've got a little
prompt here still at the bottom. I get about three
rows of text to work with while I've got the graphics
mode at the top of the screen. And I can set the
color to, let's try,-- what, it's telling me it's
purple, a light purple, lavender. And then let's
plot a point, 0,0. Look at that. Is it purple? I don't know. I'm seeing just
green right here. But you're probably
seeing purple right in the top left just like
Processing or P5, 0,0, top left. Now, what's the
resolution of its display? That's the top right
corner, 39 comma 0. So we've got 40 pixels
across and 40 down. There, all four corners. I can also draw lines with
Hline for horizontal line and Vline for vertical line. If I want to draw a
horizontal line from 0 to 39, those are the horizontal
positions across at a vertical position
like 20, there we go. Horizontal line in the middle. Vertical line up and
down in the middle. Clear the screen. Go back to text mode. And here we are. What should we try to do? How about let's program
a bouncing ball. If you remember
if I want to write some code that I
can execute, I've got to give it line numbers. So let's start with line number
10, entering graphics mode. Line number 20, let's
create an x and a y for where the ball will be. Then in line 30, let's set
the color, a nice purple ball. Line 40, let's plot the ball. And in line 50, let's
go back to line 30. And there we go. The ball is not moving because
I need to add an x speed and a y speed. And what if I make those random? Some value between
0 and 3 for x speed. Some value between
0 and 3 for y speed. I could include having
negative numbers in the random, but this is just for
demonstration purposes. I'll leave that as a challenge
for you to try in an emulator. Now, right after I
plot the xy position, let's have x increment by
x speed and y by y speed. Let's try running this. Oh, syntax error in line 25. So this is something that
I've been noticing a lot when programming in Applesoft BASIC. There are a lot
of reserved words. And if I ever use
those reserved words even as part of a variable
name, I can run into issues. So let's try xs
just for x speed. I could also use dx or delta
x or vx for velocity x. I like vx actually. Those should be the
changes I need to make. Look at that. It started moving. And it's random and
different each time. But I'm getting an
illegal quantity error. Why is that? Because I've left the boundaries
of where I'm allowed to draw. So I can add an if statement. Let's overwrite line 50 and say,
if x is greater than 39, then vx equals vx times negative 1. So that should reverse the
direction of the x velocity. Can I actually say an or? I think I could put an
or in here because I also want to do this. If it is or if x is
less than 0, then vx equals vx times negative 1. And then 60, well, let's
make it 70 because I know I'm going to do a y. We're also going
to say go to 30. So we still got an
illegal quantity. Is that because I
haven't done the y yet? I like how it looks
like it's like trying to draw something
past where it can, and it put these pound signs. Let's try adding the y. You know what the issue is? If x is greater than
39, that means it's 40. And what was the
last thing I did? Plot xy. So I actually
allowed myself to try to plot it at the column
40, which doesn't exist. There's different ways
I could handle this, maybe greater than or equal. But just for simplicity's
sake right now, let's make the boundary
38 and less than 1. Then let's do the y. What have I done here? I messed up my line number. So line 50 is still in there. Now, I've got changing x and
y, checking x, checking y. And now, I need
to say on line 80. And I actually don't need
to set the color again. So I can go to line 40. Whoa, look at that. There's my bouncing
ball or giant spider. And eventually it's
covered the whole terrain of where it's going to go, and
it's just overwriting itself. Now, of course, you're
probably noticing that it's drawing a trail. I'm not erasing the
background as I might in, say, Processing or P5. This is actually a big
topic of discussion. We take for granted
that Processing and in JavaScript, there's
request animation frame, that handle, what's known as
double-buffered animation. There's always an
off-screen memory buffer of what's being
drawn to the screen that it can swap out so that we
don't see these interim steps of clear, draw, clear, draw. If you've ever seen a computer
graphics program flickering, it's because the
double-buffered animation is not implemented properly. This is something
that I actually think I might like to do another
video all about, how to do double-buffered animation. Maybe I can create a simple
3D renderer in BASIC. Ooh, that would be fun. But for now, let's
just look at how to erase the previous ball
position so that we just see one moving along. So how about if right
before I change the x and y, I save the previous
x and y position. Let's say previous x equals
x, previous y equals y. Then in line 30, let's
set the color to black and plot the previous
x and previous y. That way, after I've
changed x and y, I can set the color back to-- and let's just have
it be white for right now because I actually have
no idea what it's looking like on what you're watching. Just to make sure I have
something that's working, let's set the color to 15. And then in line 55,
let's plot x comma y. Let's see what happens. So this is working. But it's not erasing
the previous position. And I got an illegal
quantity error in 55. 55 is plot xy. Let's debug this by
printing out the values of x and y in line 52, followed
by space and then y. This is now working nicely. It looks like my random values
were kind of wonky there. Yeah, there we go. So I got a negative number. So clearly, something
went wrong there having to do probably
with the way I'm using floating-point values. I could just, anytime
it reaches the edge, if it got a little bit
passed, I could slide it back into the last column, last
row, first column, first row. Let's do that. So in line 60, I need to
instead of just reversing the x, I need to reverse x and
set x back to a position. I need to do two things, which
means I need a subroutine. And same for line 70. The first subroutine,
reverse vx. If x is less than one, I can
actually just set x back to 0. Oh, I'm realizing
this is actually also going to solve
the problem of me using 38 and 1 as the boundary. But let's not worry
about that right now. If x is less than
1, then x equals 0. If x is greater than
39, then x equals 39. Return. What's wrong with me? I'm being so inconsistent
about my edges. Let's just see if this works. Whoops. Oh, I think I forgot return. There we go. No errors no matter what I do. Still not erasing the
previous position properly. Let me fix all of the boundaries
to be what they properly should be. 48? How did it get to be 48? I really should be
plotting the x and y after I do this
because these are where my constraints are now. So we can get rid of this print. I feel pretty confident now. We can get rid of
line 50, line 55. And at 75, we can say Plot. We can say Color
15, Plot x comma y. By the way, I can list a single
line of code by saying List 75. And I forgot the equals. Illegal quantity error in 75? Oh, my goodness. I'd go sub 1000 also in line 70. There we go. Now, I have my
perfect bouncing ball. I'm just not erasing
the previous position. How am I going to do that? Oh, no. I see the problem. My logic is 100% correct. Go to 40 is where I plot px, py. 30 is where I set
the color to black. There we go, everybody. You know what we
should have it do? We should have it beep
when it hits the edge. How do I make a sound? Sound equals Peek. Oh, I can use a Peek. Let's look at our Peeks. Click speaker is 49200. So that gives me a
syntax error probably because, like it's
telling me here, I need to set it to a variable. Did you hear anything? I didn't. Did you hear that buzz? I love that. OK, let's write
another subroutine. And then I want to call this
buzz subroutine in both the x and y boundary subroutines. There we go. That's the most
satisfying sound ever. This is way too much fun. There's so much I could do here. We could try creating
Pong, Brick Out. We could make a fancier version
of the snake game with color. I'm not going to stop here
because while I have shown you GR, graphics mode, I
have something even more exciting and futuristic to show
you, high-resolution graphics. What if I now say plot. Well, let's set the
color again to 15. What if I say plot 10 comma 10. I don't see anything. Hplot 10 comma 10? Oh, look at that. A little tiny dot up there
in high-resolution graphics. Amazing. We're going to need a
whiteboard for this. High-resolution graphics. For high-resolution
graphics, instead of having a 40 by 40
low-resolution grid, I now have a canvas
to draw in that is 280 pixels wide
by 160 pixels high. And I believe that
HGR2 will actually allow me to draw 20
more pixels below. But I like having my ability
to have a command prompt here. And as we've seen,
instead of plot, I now have high-resolution
Plot, or Hplot. It says here, now, color-wise,
high-resolution graphics are truly wonderful,
but you do have to make some sacrifice
in order to use them. There are fewer
colors available. The high-resolution colors go
from 0, black, to 7, white. Let's set the color to
white just to start. Now, let's see if we can
draw a line from the top left to the bottom right. With 280 by 160, the edge
pixels are index 279, 159. Oh, look at that beautiful line. This makes me want
to do a for loop and draw some twirling
lines really badly. I'm going to wipe our bouncing
ball program by saying New. So let's make a loop,
where x goes from 0 to 279. Let's draw from x comma 0
to 297 minus x comma 159. Is it going to let me do that? 40 Next x. Let's have the step be
a little bit higher. I just don't know
where I put that. Let me look here. I just put it on
with the for loop. So I can say in line 20, for
x equals 0 to 279 step 5. Awesome. I probably should stop here. But I want to try something
pretty high degree of difficulty. Can I make my coding
challenge, a fractal tree, right here in Applesoft BASIC
on this Apple II Plus computer? Now, I don't have
access to Push. I don't have access to Pop. I don't have Rotate. But I think I can
do it with Line and some basic trigonometry. What if I were to start to draw
a line from the bottom middle here up until about the center? Then let's say what I want
to do is draw the next line half as long but rotated
by, let's say, 45 degrees. What does that mean? Let's not worry about
shrinking the line. We know this is about 80 pixels. So what is the offset
of x and the offset of y to find this next point? This is certainly something
I've covered extensively in videos about polar
coordinates and trigonometry. I'll make sure to include
a bunch of resources and references to those
videos along with this one. But the quick
answer here is if I know this angle using the Greek
letter theta to notate it, sine of that angle
is the opposite side y divided by the length of this
line, which I just said was 80. Cosine of this angle is the
adjacent side x divided by 80. And so I can actually
get these offsets to find this point
by saying x equals 80 times cosine of
that angle y equals 80 times sine of that angle. This is going to be the length
of a given branch of the tree. And then I need to find
the next branch of the tree by drawing a line
from its endpoint to a new point
offset by an angle. Let's see if I can make that
work in a BASIC program. Let's start over again. Enter HGR. Set the color to 3. Just have everything be white. I need a length. What's the length of
this first branch? What did I say, 80? Then I need to draw a line. If I'm going to ultimately
start drawing this tree pattern, I need to start with this
point, move it to this point, and then move it to this point. So I need a variable for that. Let's call that
x, which would be at the bottom, 159, and y, which
would be at the middle, 140. Sorry to interrupt. I don't know what
I was thinking. But I wrote x is
159 and y is 140. The middle horizontal is 140. That's what x should be. The bottom y should be 159. It's going to be wrong through
the rest of this video. It'll be slightly askew,
but you get the idea. I'll make sure to correct it
in whatever code I release. 50 plot from xy to xy minus l. There's my line. Is that really the middle? No, that's not the middle. You swap your x's
and y's, Shiffman. Approximately, let's
make the arc length less. Start at 50. Now, what am I doing here?
dx equals the length times cosine of an angle. And let's say that angle
is about 45 degrees, which is 90 degrees is about 1.5
radians, 1.6, divide that by half. Let's just call it
0.7 for approximation. And in fact, let's make that
a variable, a for angle, and dy is the length
times sine of that angle. Let's set up the
angle in line 25. Angle is 0.7. Once we've drawn that
first line, let's move y. And then the next line
will be Hplot x comma y to x plus dx comma y plus dy. Let's take a look
at this whole thing. So in theory, I'm drawing a line
from the bottom to the middle and then calculating it
offset based on some angle and drawing a new line
to that new point. OK, so what did
I get wrong here? The angle is actually
this angle right here. And I want it to be
this angle up here. So I think I can just
invert what I'm doing with y and say Hplot xy to x
plus dx, y minus dy. Much better. This looks like the
beginnings of my tree. So now, I have the math
that I need for branching. But I need some kind of loop. Because ultimately, I want
to attach another branch to this branch. And I also want to have another
one going the other way, if you've looked at my
fractal recursion videos. I start with this line,
attach two lines to that line, and to those, attach
two more, and to those, attach two more, and
so on and so forth. This is a process
known as recursion. And again, I have several
videos where I cover this much more extensively. The question is can I
do recursion in BASIC on this Apple II computer? I think so. First thing first. I need a subroutine to
draw each line of the tree. And I'm going to
put that at 1,000. So actually, at 1,000, I'm
going to calculate dx and dy. Then let's actually put into a
variable the next xy position. I'll put it in one
line, minus dy. And then let's draw. Well, I'm just going to
put line 1900 Return. I don't want 50, 55, 60,
or 70, or 80 anymore. So in line 50, I say GoSub 1000. And then in line 60,
I'm going to say End. When it comes back,
it'll be done. OK, so I got that one line. Oh, because the angle,
right, that first line, is now a branch to the right. So let's actually have
the angle start at zero. And let's have a variable
for the delta angle. I'll call that da. We'll see where that comes in. But now, OK, so it's
doing it horizontally. This is where
everything is flipped because my y-axis is
really my x-axis, which is kind of awkward. But I think there's going to
be a quick easy way for me to resolve this by ultimately
just swapping the sine and cosine or the x and the y. I don't know what's going to
be the least confusing thing for me to do. You know what? I could also start the
angle at 90 degrees. So that's approximately pi
times 0.5 with my delta angle. And you know what? This should be on a
separate line of code because I'm going to want to
change that delta angle pretty often. So let's make that line 27. There we go. I've got the first
branch of the tree. All right, what do I do next? As soon as I draw that
line, the next thing I do is move xy to its next position,
alter the angle, and then what? GoSub 1000. Do it again. So inside of this subroutine
that starts at line 1000, I'm going to restart
the subroutine. Only, I need an exit condition. If I just said GoSub 1,000,
it's going to do it forever. So what else can I do here? In addition to changing the
angle, let's shrink the length. So let's add another variable. Let's call it shrink. So every new line will be
about 2/3 of the length. And so in line 1060,
length equals length times shrink and then GoSub 1000. Oh, no, no. If l is greater than 10,
then GoSub 1000 as long as there's more than 10 pixels. So calculate the offset. Get the new position. Draw the line. Move to the next spot. Change the angle. Change the length
And do it again. That's one side of the tree. Look at that. So now, I just need to
also go to the other side. Right after I GoSub 1000,
what do I need to do? I need to rotate back
the other direction. So that would be 1080. a equals-- I just
changed it by da. a minus da times 2. 1090, if l is greater
than 10, then GoSub 1000. Well, unfortunately,
I've got a problem. In addition to
reversing the angle, I've also got to go back
to the previous position. I don't think this
is going to work. I think I'm going
to need an array to keep track of all
the previous positions. But in theory, I could
say x equals x minus dx, y equals y minus dy is
what I want to undo. After I've drawn
the second line, I need to come back
to the original angle and unshrink the length,
add da back, and say l equals l divided by Shrink. Oh, kind of getting there. But look, all my
positions are wrong. So this is the main problem. This just subtracting
back is not getting me to the right place. I need an array to
keep track of what's known as a stack of all
the previous positions. And in fact, something
that I can use is this idea of a level. This maybe would be
level 0 of the tree. This is level 1 of the
tree, 2, 3, et cetera. So each generation--
I could also call that generation--
is something that I want to keep
track of separately. So at the beginning
of the program, right before I go to GoSub
1000, let's add line 45. Level equals 0. And I also want to then have
a maximum number of levels. Well, let's just try 2 to start. Call it MaxLev equals 2. Then instead of my exit
condition being the length, let's keep track of what
level of the tree I'm on. So 1070 would be, if Level
is less than max level, then GoSub 1000. And 1090 would also be, if
Level is less than max level, then GoSub 1000. Then every time I
get a new position, I go to the next level. And after I've drawn
those branches, I go back a previous level. Let's list from 1,000 to 2000,
so this is just the subroutine. What's happening
in the subroutine? I find the offset. I get the new position. I draw the line. I go to the next position. I increase the level. I rotate. If I'm not at the end, go
back and do the next one. Then undo. Go the other way. Go back and do the next one. Then come all the way back
again to the previous level if you have to keep going. Illegal quantity error in 1030. Again, I'm off into outer space. I'm not keeping track of
the positions properly. I'm going to need an array. Let's put that somewhat
awkwardly at line 48. Remember, for an array,
I use Dim for dimension. Call the x's max level. Right before I
increase the level, let's save the current x
position and the current y position. I could use a two-dimensional
array here, just have the maximum
number of levels and then two for the
second dimension instead of two arrays. But anyway, this is a little
easier for me to think through. And then once I've
gone back to the level, I should be able to just update
and get that position back. I'm sure I'm missing
something here. OK, I didn't pop back. So you could see this as
the correct second branch for two levels, but it didn't
pop back here properly. Why is that? Weirdly, it looks like the x
came back, but the y did not. So that just might be a typo. Oh, I don't need
to do this anymore. I don't need to try to
move back with 1085. That's what my array will do. Ooh, that's right, closer. But I need to come
back to there. Where did I-- did I store
it in the wrong place? Maybe I'm supposed to save x
and y before I get nx and ny. If I move line 1040
to 1043, look at that. Oh, my goodness. I'm sure if I change
the max levels to three, there's some
unanticipated issue here. But this is really close. Max level equals 3. Oh, my goodness. It's a fractal tree
on the Apple II. Oh, this is amazing. OK, let's just go. Let's just go for it. Max level equals, let's try, 7. That might be too ambitious. Look how slow it's drawing
all the little bits by bit. Well, now, you see
what's possible with high-resolution
graphics, recursion, drawing lines, algorithmic shapes. Can you make your own tree? Can you alter the angles? Oh, you know what would be fun? Let's just do one thing. I want to show you how I could
get input from me, the user. Maybe I can enter in
an angle and a max number of branches or
something like that or a color. Let's just do the angle. I'm just going to
enter the angle. Let's list just the
beginning of the program. So before I even get
into graphics mode, I really wish there is--
there used to be a command to renumber all the numbers. And it used to be Renumber. Or Renum. Sorry, again from
the future again. It looks like that
Renumber command that I remember so fondly, just
not available when the Apple II Plus boots up without
any disk put into it. But I'm pretty sure it was
part of DOS 3.3, ProDOS. If you remember, if you know,
let me know in the comments, and I'll link to some
resources about how you renumber a BASIC program. So I've got to use
between 0 and 10. I can say, let's call it line 2. I can use the input, and I
can get in a variable, da. Let's get rid of line 27. And let's say on line
5, say Input da, 0.5. Is that a different angle? I think it is. 0.1. Make it much more extreme. Yeah, there we go. Yeah, so I'm going off
the screen with that. I don't have any error checking. Let's do 1.5. Woah, that is wild. I should probably print a query
and say Home and all that. So let's make this a little
bit nicer before I go. Enter an angle in radians. 1 equals Home. All right, let's
run this program. I will draw you a tree. Enter an angle in radians. I kind of like that 1.5. Let's try 2 or 1.8. This is way too much fun,
but I've got to stop here. I hope that you
take what I've done. Find one of the emulators. I'll link to them in
this video's description. Try running this code. Make it your own. How can you alter the tree? Can you try color? One advantage you'll have is
will probably run quite a bit faster than here. What should be the topic of
the next Coding Together video? Should it be double-buffered
animation and a 3D renderer on this Apple II Plus? Do you need me to get
back to Processing and P5? Because I could do that also. Let me know in the comments
on Twitter @Shiffman, @thecodingtrain, any of these
places where you can find me. I can't wait to be with
you again coding together.