Apple ][ Coding Challenge: Fractal Tree

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
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.
Info
Channel: The Coding Train
Views: 327,845
Rating: undefined out of 5
Keywords:
Id: UNkHditYGls
Channel Id: undefined
Length: 35min 35sec (2135 seconds)
Published: Mon Sep 12 2022
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.