Showing posts with label tutorial. Show all posts
Showing posts with label tutorial. Show all posts

Friday, December 27, 2013

Petit Computer Journal #31



Digital Koto

A while back, I wrote a little piano program. It includes song player. It's just a little simple music player, so I used BEEP instead of MML command. However, I'm rather curious since most of the code is created for the interface. In other words, drawing the piano keys. So, I'm wondering whether or not it's simpler to just draw the keys straight. Well, what kind of musical instrument is like that? The answer is simple: Japanese Koto. In truth, different cultures have different ideas and names for what basically amounts to a bunch of strings mounted on a board.

Still, the idea of it is interesting, and it's simple enough for me to do. So, let' do it! There are several different parts, but generally speaking, once you touch the screen, you come up with a string number and beep the appropriate sound. That's just look up table. Touch screen is easy. About the only thing that's difficult is drawing the strings proportionately. A little mathematic should do it.


  1. INIT
  2. Build BEEP table
  3. Build Note table
  4. Build String table
  5. LOOP
  6. Draw Strings
  7. If Touch screen
  8. Highlight nearest string
  9. Play BEEP sound
  10. GOTO LOOP


So, do you think this will take you one hour to do? Time for another "Hour of Code" challenge!


  1. ACLS:CLEAR
  2. DIM BP[49]:'BEEP TABLE
  3. DIM NT$[49]:'NOTE TABLE
  4. DIM ST[16]:'STRING TABLE
  5. BREPEAT 0,60,12
  6. BREPEAT 1,60,12
  7. BREPEAT 2,60,12
  8. BREPEAT 3,60,12

  9. @INIT
  10. NSI=0
  11. P=4096/12:T$="CcDdEFfGgAaB"
  12. FOR I=-24 TO 24:J=I+24
  13. BP[J]=P*I
  14. NT$[J]=MID$(T$,(J%12),1)
  15. NEXT
  16. ST[0]=14:ST[1]=19
  17. ST[2]=21:ST[3]=22
  18. ST[4]=26:ST[5]=27
  19. ST[6]=31:ST[7]=33
  20. ST[8]=34:ST[9]=38
  21. ST[10]=39:ST[11]=43
  22. ST[12]=45:ST[13]=0
  23. ST[14]=0:ST[15]=0

  24. ACLS


That's about it for the Initialization table. I want to fiddle with the string, so I'll put in button interface so I can change the string values on the fly.


  1. @LOOP
  2. NS=13:A=256/NS:B=A/2
  3. VSYNC 1:BT=BTRIG():TX=FLOOR(TCHX*NS/256)
  4. IF NS<1 THEN NS=1
  5. FOR I=0 TO NS-1
  6. GLINE B+I*A,16,B+I*A,192,15
  7. LOCATE (B+I*A)/8,0:?FLOOR(ST[I]/12)
  8. LOCATE (B+I*A)/8,0:?NT$[ST[I]]
  9. NEXT
  10. GLINE B+TX*A,16,B+TX*A,192,8
  11. IF TCHTIME>0 THEN BEEP 20,BP[ST[TX]]
  12. GOTO @LOOP


And that's about it. 35 minutes so far, and that's because I was having trouble with the math. The truth is, had I did the math correctly beforehand, I wouldn't have had so much trouble and I wouldn't have spend the time debugging.

Oh, yeah, about the tuning on the fly. I think that it is nice to be able to change the notes. So, what I'll do is add some button interface. That's what the BREPEAT command is for. Also, I may as well throw the instrument along with it. Instead of the locked in BEEP 20, I substitute it with a variable that will allow you to choose your own instrument.

Finally, we're going to move the whole thing down to the lower screen for ease of play. What should we put on the top of the screen? I don't know. Title screen, maybe? Or some internal variables?


  1. REM DIGITAL KOTO
  2. REM BY HARRY HARDJONO
  3. REM DEC 2013

  4. ACLS:CLEAR
  5. DIM BP[49]:'BEEP TABLE
  6. DIM NT$[49]:'NOTE TABLE
  7. DIM ST[16]:'STRING TABLE
  8. BREPEAT 0,60,12
  9. BREPEAT 1,60,12
  10. BREPEAT 2,60,12
  11. BREPEAT 3,60,12
  12. PNLTYPE "OFF"

  13. @INIT
  14. NSI=0
  15. CI=16:'CURRENT INSTRUMENT
  16. P=4096/12:T$="CcDdEFfGgAaB"
  17. FOR I=-24 TO 24:J=I+24
  18. BP[J]=P*I
  19. NT$[J]=MID$(T$,(J%12),1)
  20. NEXT
  21. ST[0]=14:ST[1]=19
  22. ST[2]=21:ST[3]=22
  23. ST[4]=26:ST[5]=27
  24. ST[6]=31:ST[7]=33
  25. ST[8]=34:ST[9]=38
  26. ST[10]=39:ST[11]=43
  27. ST[12]=45:ST[13]=0
  28. ST[14]=0:ST[15]=0

  29. GPAGE 1:ACLS

  30. @LOOP
  31. NS=13:A=256/NS:B=A/2
  32. VSYNC 1:BT=BTRIG():TX=FLOOR(TCHX*NS/256)
  33. IF NS<1 THEN NS=1
  34. GPAGE 1,1,1
  35. FOR I=0 TO NS-1
  36. GLINE B+I*A,16,B+I*A,192,15
  37. LOCATE (B+I*A)/8,0:?FLOOR(ST[I]/12)
  38. LOCATE (B+I*A)/8,0:?NT$[ST[I]]
  39. NEXT
  40. GLINE B+TX*A,16,B+TX*A,192,8
  41. IF TCHTIME>0 THEN BEEP CI,BP[ST[TX]]

  42. IF BT AND 1 THEN ST[NSI]=ST[NSI]+1
  43. IF BT AND 2 THEN ST[NSI]=ST[NSI]-1
  44. IF BT AND 4 THEN NSI=NSI-1
  45. IF BT AND 8 THEN NSI=NSI+1
  46. IF BT AND 16 THEN CI=CI+1:GOSUB @DISP
  47. IF BT AND 32 THEN CI=CI-1:GOSUB @DISP
  48. NSI=(NSI+NS)%NS
  49. ST[NSI]=(ST[NSI]+49)%49
  50. CI=(CI+70)%70
  51. GOTO @LOOP

  52. @DISP
  53. ACLS
  54. ?"INST: ";CI
  55. FOR I=0 TO 15
  56. ?I,ST[I]
  57. NEXT
  58. RETURN



Oh, by the way. The actual problem is so simple, that you can actually fit the minimalistic version of the program into one screen. That's right. It's another single screen challenge program!


  1. REM DIGITAL KOTO
  2. REM BY HARRY HARDJONO
  3. ACLS:CLEAR:DIM BP[49]
  4. DIM NT$[49]:DIM ST[16]
  5. P=4096/12:T$="CcDdEFfGgAaB"
  6. FOR I=-24 TO 24:J=I+24
  7. BP[J]=P*I
  8. NT$[J]=MID$(T$,(J%12),1):NEXT
  9. ST[0]=14:ST[1]=19:ST[2]=21
  10. ST[3]=22:ST[4]=26:ST[5]=27
  11. ST[6]=31:ST[7]=33:ST[8]=34
  12. ST[9]=38:ST[10]=39:ST[11]=43
  13. ST[12]=45:ST[13]=0:ST[14]=0
  14. @LOOP
  15. NS=13:A=256/NS:B=A/2
  16. VSYNC 1:TX=FLOOR(TCHX*NS/256)
  17. GPAGE 1,1,1:FOR I=0 TO NS-1
  18. GLINE B+I*A,0,B+I*A,192,15
  19. NEXT
  20. GLINE B+TX*A,16,B+TX*A,192,8
  21. SD=BP[ST[TX]]
  22. IF TCHTIME>0 THEN BEEP 16,SD
  23. GOTO @LOOP


Friday, September 13, 2013

Petit Computer Journal #17

WITCH

I wrote WITCH to showcase the SPRITE capabilities of Petit Computer. There are lots of people who doesn't know how to use sprites. In fact, Petit Computer sprites are so powerful, you can easily replicate old games.

So here is the WITCH program that is purposely SPRITE heavy. There are certain techniques where I could have done it traditionally, but chose to implement it using a sprite instead. Notably, there is a TIMER sprite, where the sole purpose is to spread the bullets so that they don't trip over one another.

I chose the firing of the fireball using stylus because somebody was asking the way to do it. He was wondering about how to fire the bullet in the direction of the stylus, without stopping at the point of the stylus. In my solution, I also took care of the speed of the bullets. Done carelessly, the bullets may have different speed.

'THE WITCH AND THE UNDEAD
'A SPRITE EXERCISE

'1. WITCH
'2. FIREBALL
'3. UNDEAD FALLING
'4. UNDEAD WALKING
'5. UNDEAD HIT
'6. WITCH HIT
'7. EXTRAS

@INIT
H=100:'HEALTH
NUMZ=1:'CURRENT NUM OF ENEMY
MAXZ=20:'MAXNUM OF ENEMY
SCORE=0

ACLS
PNLTYPE "OFF"
SPSET 1,108,1,0,0,0:'WITCH
SPCOL 1,0,0,16,16,FALSE
SPOFS 1,120,170

'BULLET
FOR I=2 TO 9
SPSET I,142,0,0,0,0
SPANIM I,2,10,0
SPOFS I,30,180,I*90
SPCOL I,0,0,16,16,FALSE,2
NEXT

'CURSOR
SPSET 10,253,1,0,0,0
SPCOL 10,0,0,16,16,FALSE,0

'TIMER
SPSET 11,3,1,0,0,0
SPCOL 11,0,0,16,16,FALSE,0
SPOFS 11,200,200

'UNDEAD
FOR I=20 TO 20+MAXZ
SPSET I,130,RND(16),0,0,0
SPANIM I,2,20,0
SPOFS I,200,-300
SPCOL I,0,0,16,16,FALSE,1+2
NEXT



@MAINLOOP
VSYNC 1:X=TCHX:Y=TCHY:S=TCHST:B=BUTTON(0)
LOCATE 0,0:?"HEALTH=";H;"   ":?"SCORE=";SCORE
SPOFS 10,X,Y
GOSUB @FIREBALL
GOSUB @ENEMY

IF SPHIT(1) THEN H=H-1:BEEP 37
IF B AND 4 THEN DX=-8: GOSUB @MOVE
IF B AND 8 THEN DX=8:GOSUB @MOVE
'IF B AND 16 GOTO @END
IF H<=0 GOTO @END
GOTO @MAINLOOP

@MOVE
SPREAD(1),SX,SY
IF SX+DX>0 AND SX+DX<240 THEN SPOFS 1,SX+DX,SY,8
RETURN

@ENEMY
SPREAD(1),SX,SY
FOR I=20 TO 20+NUMZ
SPREAD(I),ZX,ZY
IF (1 AND SPCHK(i)) GOTO @ENEMY2
IF ZY>SY THEN SPOFS I,SX,SY,SPEED:GOTO @ENEMY2
SPEED=(50000-SCORE)/100:IF SPEED<60 THEN SPEED=60
SPOFS I,RND(255),0
SPOFS I,RND(255),SY+1,SPEED
@ENEMY2
NEXT
RETURN

@FIREBALL
FOR I=2 TO 9
IF SPHIT(I) THEN GOSUB @HITZ
IF !(1 AND SPCHK(I)) THEN SPOFS I,I*18,300
IF S==1 AND !(1 AND SPCHK(11) THEN @SHOOT
NEXT
RETURN

@HITZ
BEEP 14
SPOFS I,80,0
SPOFS SPHITNO,30,-150
SCORE=SCORE+FLOOR(SQR((SPHITX-SX)*(SPHITX-SX)+(SPHITY-SY)*(SPHITY-SY))/3)
NUMZ=FLOOR(SCORE/1000)+1
IF NUMZ>MAXZ THEN NUMZ=MAXZ
RETURN

@SHOOT
CB=((CB+1)%8:CBS=CB+2
IF (1 AND SPCHK(CBS)) GOTO @SHOOT2
BEEP 10:SCORE=SCORE-1:IF SCORE<0 THEN SCORE=0
SPREAD(1),SX,SY
A=ATAN((X-SX),(Y-SY))
SPOFS CBS,SX,SY
SPOFS CBS,SX+300*SIN(A),SY+300*COS(A),150
SPOFS 11,250,250,20
@SHOOT2
RETURN

@END
CLS:?"GAMEOVER"
BGMPLAY 6
WAIT 60
END

Wednesday, September 4, 2013

Raspberry Pi Journal #19


E-Book with Calibre

Printing and making book is fun. But what is really interesting, is to have an e-book you can take with you wherever you go. With e-book, that means Kindle, Nook, or other dedicated e-book reader. Sure, you can bring along your laptop to view your PDF or PowerPoint files, but that's not fun. For true fun, we want dedicated e-book reader. That means EPUB file format for most. Kindle has its own format. MOBI for old Kindle, and EZ3W for the new Kindle Fire.

So, how can we convert our books into those format? Amazon has a converter service that converts Microsoft Word Document into its format. I will probably investigate that later. In the meantime, I'm using a program called Calibre.

This program is designed to function as all-in-one e-book solution. It shows. The download is quite sizable, and the program runs like a hog through mud. Just struggling to move. CPU at 100% at all times.

The important thing is that it works!

The first thing you want is a proper format for the book. This means that you should put proper formatting in place. Let's use Abiword for example. The formatting is usually set on "Normal". For different parts, we need to specifically specify each type. It's not enough that it looks right. It has to be the right type.


For Chapter Heading, use the Chapter Heading format. Bullet list? Use Bullet List format, and so on. This is very important. The reason for this is so that the automatic Table of Content generator can pick this up. It just makes things more convenient if you do.



Here is my novel. Well, short story. I did this during Nanowrimo event last year. Notice that everything is formatted properly. Abiword can save epub file format, and that's what I'm using.



Open the Calibre program. Add book into it. If you're using Raspberry Pi, you'll probably notice the extremely long load times. Most of the functions is provided by Python, I believe. So, not only it is running its own program, but also Python interpreter. And well, heavy slog.



Edit your Metadata here. That means Title, Author, and Cover. There are other info as well, such as Publisher. If you're feeling lazy, there's a button called "Generate Cover" that will automatically generate the book cover for you.



It's time to convert the book! In this case, I'm converting it to MOBI format for Kindle reader.



I'm leaving everything as default. I do make sure that the program automatically generate the Table of Contents. Notice that even though the size of the document is small, it takes a long time to format the piece. About 2 minutes to format 20 kb file? That's unbelieably long time!



If all goes well, you'll have an e-book! Normally, you can upload the document directly via USB connection. For some reason, I keep getting "Low Voltage" error, and the book refused to upload. How can I transfer the book to Kindle? There's always the PC method, but I'm going to see if I can skip PC altogether.

Sure enough, there is! Calibre comes with its own Content Server. What that means is, you start some kind of document provider over the net. Since I'm doing local, it's just a local connection.

I set the Server Port to 8088. Then it's time to fire up my Kindle! What I need to do is to connect to server on my Raspberry Pi at port 8080. Like this:

http://myhostname:8080




On my Kindle Keyboard, it falls under Experimental feature. Launch the internal web browser, and type in the URL into the address bar. Just remember to use your Raspberry Pi Hostname.



There it is! Both epub and mobi format are downloadable. We want the mobi format. All you have to do is click on it. If you have specified the cover in the metadata, then it will show up here. Otherwise, the default cover is the first picture on the book.

Oh, look! My very own e-book! Isn't that nice!

Well, enough time for gawking. Now, let's see. Next on my To-Do list is to write The Great American Novel. Yup. That's the ticket. See you later.




Oh, one more thing. You can have images in your e-book. But remember, e-book format does not have pages, so there's no point of you aligning the pictures so it looks nice on your word processor. It all depends on the set up on your e-book reader.

Have fun making your own e-book format!


Thursday, August 22, 2013

Petit Computer Journal #15



Quickie Draw: A flipnote type app



I was reading messages in a Nintendo Life forum for Petit Computer when 2 people admitted to have inspiration to write a paint program over the weekend. I replied that it was unbelievable, because … me too! And suddenly, another one piped up and said that he too was inspired over the same weekend. Huh, sometimes real life is very strange.

So, anyway, I cooked up this drawing program real quick. I called it “Quickie Draw” because I wrote it in about one hour. It would have been faster, but it was done at the end of the day, and that day was extremely tiring because I was sick and was coughing and sneezing all day long. Overall, not a good condition to write computer programs.

QDRAW
@INIT
ACLS:GPAINT 1,1,1
PNLTYPE “OFF”
GPAGE 1: GPAINT 1,1,1

LOCATE 7,5:?”QUICKIE DRAW”
LOCATE 9,7:?”BY”
LOCATE 11,9:?”HARRY HARDJONO”

LOCATE 6,17:?”A CLEAR SCREEN”
LOCATE 6,18:?”B END PROGRAM”

So far, so good. There’s no problem at all here, or rather, there shouldn’t be at all. For some reason, I mixed up X and Y coordinates, so the “BY” somehow ended up in location 7,9. Ouch. How did that happen? Fixed it no problem.

@PRE
S=TCHST:X0=TCHX:Y0=TCHY
IF BTRIG()==16 GOTO @INIT
IF BTRIG()==32 GOTO @END
IF S==0 GOTO @PRE


You know, as simple as that @PRE is all about, I had a terrible time with it. The thing is, I knew it would be simple, but for some reason, I kept going off the deep end with various variables trying to keep track of different states of drawing. My brain was definitely jello when I did it. I kept thinking that my program was becoming too complicated and deleted my codes wholesale and started over. It was at my sixth attempt that I realized that S=TCHST should be the first thing I should grab. Doh! After that, no problem.

@LOOP
X1=TCHX:Y1=TCHY
GLINE X0,Y0,X1,Y1,14
X0=X1:Y0=Y1
IF TCHST==0 GOTO @PRE ELSE GOTO @LOOP

@END
END


@LOOP is very simple, isn’t it? And in fact, that’s all there is to it. If you ever want to write a paint program where you never let the pen leave the paper, that’s all there is to it. Haha, joking aside, computer programming can be easy and fun. Just don’t do it while you’re on deathbed unless you’re addicted to it (like me).

So far so good.

I posted the program to the forum. Chat a little bit and 50 minutes later, I wrote: “BTW, I changed the program to flipnote type.”

Interesting, isn’t it? I was sick to my deathbed, but after a breakthrough, I just kept going, and changing it to flipnote app. It wasn’t hard at all!

‘PNLTYPE “OFF”:PP=6:GOTO@ANIM
PP=0
@INIT
IF PP>0 GOTO @FADE
ACLS:GPAINT 1,1,1
PNLTYPE “OFF”
GPAGE 1:GPAINT 1,1,15

@TITLE
LOCATE 7,5:?”QUICKIE DRAW”
LOCATE 9,7:?”BY”
LOCATE 11,9:?”HARRY HARDJONO”
LOCATE 6,15:?”(A) NEW PAGE”
LOCATE 6,16:?”(B) FLIP PICS”
LOCATE 6,17:?”(X) END PROGRAM”
LOCATE 10,20:?PP;”/99”

The first line is commented. This is because it goes to animator subroutine directly, just in case you want to see your masterpiece in action without having to redraw the whole thing. PP stands for number of pages, and although I peg it at 99 pages max, I doubt there is space in the program to hold that many.

@PRE
S=TCHST:X0=TCHX:Y0=TCHY
VSYNC 1
IF BTRIG()==16 THEN GOSUB @SP:GOTO @INIT
IF BTRIG()==32 THEN GOSUB @SP:GOTO @ANIM
IF BTRIG()==64 GOTO @END
IF S==0 GOTO @PRE
@LOOP
X1=TCHX:Y1=TCHY
GLINE X0,Y0,X1,Y1,14
X0=X1:Y0=Y1
IF TCHST==0 GOTO @PRE ELSE GOTO @LOOP
@END
END

It is very interesting that it doesn’t take much more code to turn the paint program into a flip note app. That’s because most of the work is done by the subroutines. A sign of well-designed program is that the implementation of such program is effortless. I know I’m bucking the convention here, but I think that heroic effort in computer programming is misguided. I’d rather have heroic effort in program design and pedestrian implementation of it. Computer programs that are easy to understand tend to get maintained more, and it’s not about documentation. It’s about the cleanliness of the algorithm. How many of you are willing to throw out existing code out and start over? Because if you do not, then you’re effectively working with first draft. Just ask any Communication teacher about the value of first draft. Not much, eh? Then why are you satisfied with your first implementation? Be willing to discard bad code!

@SP
GOSUB @ERASE
FN$=”GRP1:FLIP”+STR$(PP)
SAVE FN$
PP=PP+1:IF PP>99 THEN PP=99
RETURN

@ANIM
WAIT 15:SYSBEEP=FALSE
IF I<0 THEN I-0
FN$="GRP1:FLIP"+STR$(I)
LOAD FN$,FALSE
I=I+1:IF I>=PP THEN I=0
VSYNC 1:IF BTRIG()==64 GOTO @END
GOTO @ANIM

@ERASE
FOR Y=0 TO 191:FOR X=0 TO 255
IF GSPOIT(X,Y)==1 THEN GPSET X,Y,15
NEXT X
NEXT Y
RETURN

@FADE
FOR Y=0 TO 191:FOR X=0 TO 255
IF GSPOIT(X,Y)==14 THEN GPSET X,Y,1
NEXT X
NEXT Y
GOTO @TITLE

OK, so now we have a fully functioning flip note program. Notice, however, that it is not user friendly. It’s still rough on edges. However, due to good design, the algorithm is clean and modular and easy to change. One more round of polishing, and we’re good to go.

‘QUICKIE DRAW
‘A SIMPLE FLIPNOTE
‘HARRY HARDJONO MAY 2013
DELAY=10
PNLTYPE “OFF”
INPUT “HOW MANY PAGES”;PP
IF PP>0 GOTO@ANIM
PP=0:CC=14
@INIT
IF PP<0 THEN GOSUB @FADE:GOTO @TITLE
ACLS:GPAINT 1,1,1
PNLTYPE “OFF”
GPAGE 1:GPAINT 1,1,15

The first thing you notice is that there is an INPUT statement, asking you how many pages. This is for the @ANIM function. If you enter 0, then it will go to editing mode. Otherwise, it will animate the pages. This change will enable us to run the program from the main menu, instead of having to go to EDIT screen and edit the program manually.

Another change is that there is a variable DELAY right in the beginning. This is for the delay loop for the animation. The more loops, the slower the flip. I simply set it to a number, instead of asking the user what he wants. This is for simplification of the program. With better UI, I may let the user specify the delay.

@TITLE
LOCATE 7,5:?”QUICKIE DRAW”
LOCATE 9,7:?”BY”
LOCATE 11,9:?”HARRY HARDJONO”
LOCATE 6,15:?”(A) NEW PAGE”
LOCATE 6,16:?”(B) SAVE AND FLIP PICS”
LOCATE 6,17:?”(X) COPY TO NEW PAGE”
LOCATE 6,18:?”(Y) PEN/ERASER”
LOCATE 10,20:?PP;”/99”


Nothing too new here. The biggest change is that I’m now very specific about (B). There is a concern that ending the drawing mode may not save the image. I simply made it explicit that yes, there will be a saving. If you do not intend to save it, then during program restart, simply specify 1 frame less than before. No problem.

(X) copy function is actually easier to do, since you’re not doing anything much. The Pen/Eraser function comes out of the copy function. This way, you can simply erase the difference, instead of drawing the whole thing over. I don’t really need to do this but it’s very convenient to the user.

@PRE
S=TCHST:X0=TCHX:Y0=TCHY
VSYNC 1
IF BTRIG()==16 THEN GOSUB @SP:GOTO @INIT
IF BTRIG()==32 THEN GOSUB @SP:GOTO @ANIM
IF BTRIG()==64 THEN GOSUB @SP:GOTO @TITLE
IF BTRIG()==128 THEN CC=29-CC
LOCATE 1,0:IF CC==14 THEN ?”X” ELSE ?”O”
IF S==0 GOTO @PRE


There is a little trick in changing the CC color. Notice that CC=29-CC. This is very useful mathematical formula if you ever want to toggle the value of a variable between two numbers. Let’s say you want to toggle the variable G between 4 and 7. Simply add the two values and use the formula above. So, we have G=(4+7)-G. In this case, CC=(14+15)-CC. I suppose I should have written it that way so it won’t be so much a magic number.

Also, the X and O, is actually filled block and hollow block in the program. I simply put the ASCII equivalent here.

@LOOP
X1=TCHX:Y1=TCHY
IF CC==14 THEN GLINE X0,Y0,X1,Y1,CC ELSE GFILL X1-3,Y1-3,X1+3,Y1+3,CC
X0=X1:Y0=Y1
IF TCHST==0 GOTO @PRE ELSE GOTO @LOOP
@END
SYSBEEP=TRUE
END


There is GLINE and GFILL. Why the difference? One is drawing (pencil) the other is erasing (eraser). I did try to erase with GLINE. Didn’t like it one bit. I also added SYSBEEP=TRUE. This is because I turned off SYSBEEP during @ANIM. In general, it’s good to have a specific exit point such as @END in the program even if you don’t know what you want to do with it.

@SP
GOSUB @ERASE
FN$=”GRP1:FLIP”+STR$(PP)
SAVE FN$
PP=PP+1:IF PP>99 THEN PP=99
RETURN

@ANIM
LOCATE 6,15:?”                “
LOCATE 6,16:?”                “
LOCATE 6,17:?”(X) QUIT            “
LOCATE 6,18:?”                “
LOCATE 10,20:?RIGHT$(“0”+STR(I),2;”/99 “
SYSBEEP=FALSE
IF I<0 THEN I-0
@ANIM2
LOCATE 10,20:?RIGHT$("0"+STR$(I),2);"/99 "
FN$="GRP1:FLIP"+STR$(I)
LOAD FN$,FALSE
I=I+1:IF I>=PP THEN I=0
FOR J=0 TO DELAY:VSYNC 1:IF BTRIG()==64 GOTO @END
NEXT J
GOTO @ANIM2




I had to duplicate the screen displays so the screen will update properly. I suppose I can write yet another function to simplify the structure. Since I’m going to be done with this, I decided to just leave it out like that. It is a good idea to put the screen displaying routine to a function, though. It makes it cleaner.

@ERASE
LOCATE 0,0:?”Z”
FOR Y=0 TO 191:FOR X=0 TO 255
IF GSPOIT(X,Y)==5 THEN GPSET X,Y,15
NEXT X
NEXT Y
LOCATE 0,0:?” “
RETURN

@FADE
LOCATE 0,0:?”Z“
FOR Y=0 TO 191:FOR X=0 TO 255
IF GSPOIT(X,Y)==14 THEN GPSET X,Y,5
NEXT X
NEXT Y
LOCATE 0,0:?” “
RETURN


The “Z” is the clock in the program. It indicates that the program is busy. It does take a while for the computer to scan the whole screen.

And that’s about it. Not a bad program to write in a hurry.

Monday, August 19, 2013

Petit Computer Journal #12



Packing, Saving, and Loading Data

One of the most desirable feature of computer programming is data loading and saving. Nintendo DS is used mainly for gaming, and the ability to save and load large amount of data is mostly an afterthought. However, there is a built-in feature to do so: MEM$. Basically, we simply assign the the string to MEM$, and save that.

We can enter multiple data into MEM$ by using splitting numbers technique: MID$. Let's say that we put the data for score in MEM$ location 10-15. We can extract that by using MID$, like this:

SCORE=MID$(MEM$,10,5)

A similar technique is to pack numbers into one large number. You can split them out like so:

CLS
?"FOR BITS:"
?"DEC:1=10;2=100;3=1000"
?"BIN:1=2;2=4;3=8;4=16"
INPUT "NUMBER,BITS";N,B
@LOOP
?N%B
N=FLOOR(N/B)
IF N!=0 GOTO @LOOP
?"DONE!"

SAMPLE OUTPUT:
123456,100
56
34
12


Let's say that we have data in multiple column format, that we want to save. I'm going to make it easy for testing, and just use 192*16*16. First, we make the data, then we save it two ways: Graphic and MEM$. We also load it 2 ways. Finally, we're going to put some timer on each operation.

'Make up data
DIM D[50000]
FOR X=0 TO 191
FOR Y=0 TO 15
FOR Z=0 TO 15
D[X*256+Y*16+Z]=RND(256)
NEXT:NEXT:NEXT

'SAVE DATA GRAPH
T0=MAINCNTL
FOR X=0 TO 191
FOR Y=0 TO 15
FOR Z=0 TO 15
GPSET (Y*16+Z),X,D[X*256+Y*16+Z]
NEXT:NEXT:NEXT
?"SAVE GRP0:TEST1"
T1=MAINCNTL

'SAVE DATA MEM$
T2=MAINCNTL
FOR X=0 TO 191
MEM$=""
FOR Y=0 TO 15
FOR Z=0 TO 15
MEM$=MEM$+CHR$(D[X*256+Y*16+Z])
NEXT:NEXT
S$="MEM:MEM"+RIGHT$("000"+STR$(X),3):?"SAVE ";S$
NEXT
T3=MAINCNTL

?"GRAPH:",T1-T0
?"MEM$: ",T3-T2

However way I look at it, the MEM$ version is harder, longer, and slower. I purposely skip the actual saving process because it asks whether or not you want to overwrite existing files, which in MEM$ case, rather ponderous. Even so, GRAPH method is faster than MEM$

GRAPH:  300
MEM$:   632

Now let's do LOAD:

'LOAD DATA GRAPH
T0=MAINCNTL
?"LOAD GRP0:TEST1"
FOR X=0 TO 191
FOR Y=0 TO 15
FOR Z=0 TO 15
D[X*256+Y*16+Z]=GSPOIT(Y*16+Z,x)
NEXT:NEXT:NEXT
T1=MAINCNTL

'LOAD DATA MEM$
MEM$="A"*256
T2=MAINCNTL
FOR X=0 TO 191
S$="MEM:MEM"+RIGHT$("000"+STR$(X),3):?"LOAD ";S$
FOR Y=0 TO 15
FOR Z=0 TO 15
D[X*256+Y*16+Z]=VAL(MID$(MEM$,Y*16+Z,1))
NEXT:NEXT:NEXT
T3=MAINCNTL

?"GRAPH:",T1-T0
?"MEM$: ",T3-T2

And the result?

GRAPH:  266
MEM$:   840

In all cases, the Graph version is leaner, cleaner, and faster than the use of multiple MEM$.


Sunday, August 18, 2013

Petit Computer Journal #11



Trigonometry


Our last unfinished business about Math is now about to be concluded! It's pretty simple, really, so I'll add a few more things to round things up.

1. Map() we've seen this already


Let's begin by including an old function: MAP(). This is a function that lets you do numbers interpolation. It's a very useful subroutine, especially when dealing with graphics.

@MAP
Y2=(((X2-X1)/(X3-X1))*(Y3-Y1))+Y1
RETURN

2. LOR Left Or Right.


This is very useful in game making, as well as different programs requiring graphics and the ability to tell left from right. The idea behind it is very simple: You take the slope of a line, and compare it to the slope of the other line. That is: dy1/dx1 cmp dy2/dx2. With some algebraic manipulation, we get this:

@LOR
LOR=SGN((DY1*DX2)-(DY2*DX1))
RETURN

3. Angle Measurements and Rotation


We want the ability to tell heading. Fortunately, ATAN functions takes two variables, and returns the angle (in radian), so it is trivial to do so. To check the angle from center screen:

A=ATAN(TCHY-96,TCHX-128) :'ATAN(DY,DX)

For drawing the line from the center, we can have

DX=R*COS(A):DY=R*SIN(A)
GLINE 128,96,128+DX,96+DY

Where R is the radius desired. Change the value of R for shorter or longer line.

4. Perpendicular line + angle offset


By changing the value of A, we can have different offset angle. Here's if you want 90 degree angle or perpendicular line.


DX=R*COS(A+(PI()/2)):DY=R*SIN(A+(PI()/2))


5. Cone Whether or not a point is within a cone


Take a point (X,Y), and two lines. We can see whether or not the point lines inside the cone by taking the LOR values and compare them. You have to be mindful of the angle.

You can also check whether or not a point is inside triangle, by doing so:

TX=TCHX:TY=TCHY
TX1=128:TY1=30
TX2=18:TY2=135
TX3=228:TY3=100

GLINE TX1,TY1,TX2,TY2
GLINE TX2,TY2,TX3,TY3
GLINE TX3,TY3,TX1,TY1

@LOOP
TX=TCHX:TY=TCHY
DX1=TX2-TX1:DY1=TY2-TY1:DX2=TX-TX1:DY2=TY-TY1:GOSUB @LOR:LOR1=LOR
DX1=TX3-TX2:DY1=TY3-TY2:DX2=TX-TX2:DY2=TY-TY2:GOSUB @LOR:LOR2=LOR
DX1=TX1-TX3:DY1=TY1-TY3:DX2=TX-TX3:DY2=TY-TY3:GOSUB @LOR:LOR3=LOR

IF LOR1==LOR2 AND LOR2=LOR3 THEN GPAINT 128,96,4 ELSE GPAINT 128,96,6
GOTO @LOOP

6. Intersection between 2 lines


We can also use LOR to check intersection between to lines. If the points of line 1 is on opposite side of line 2 AND the points of line 2 is on opposite sites of line 1, then we have intersection.

@LOOP
ACLS

PX1=RND(256):PY1=RND(192)
PX2=RND(256):PY2=RND(192)
PX3=RND(256):PY3=RND(192)
PX4=RND(256):PY4=RND(192)

GLINE PX1,PY1,PX2,PY2,6
GLINE PX3,PY3,PX4,PY4,10

GOSUB @ISCROSS
IF ISCROSS THEN ?"INTERSECT"

LINPUT A$
GOTO @LOOP

@ISCROSS
ISCROSS=TRUE
DX1=PX2-PX1:DY1=PY2-PY1
DX2=PX3-PX1:DY2=PY3-PY1
GOSUB @LOR:LOR1=LOR
DX2=PX4-PX1:DY2=PY4-PY1
GOSUB @LOR:LOR=LOR
IF LOR1==LOR2 THEN ISCROSS=FALSE

DX1=PX4-PX3:DY1PY4-PY3
DX2=PX1-PX3:DY2=PY1-PY3
GOSUB @LOR:LOR1-LOR
DX2=PX2-PX3:DY2=PY2-PY3
GOSUB @LOR:LOR2=LOR
IF LOR1==LOR2 THEN ISCROSS=FALSE
RETURN

@LOR
LOR=SGN((DY1*DX2)-(DY2*DX1))
RETURN


7. Movement: Direction and Speed


Let's take a simple spaceship movement real quick. Actually, it can be a car, duck, or whatever.

ACLS
SPSET 0,256,0,0,0,0,16,8
SPHOME 0.8.4
SX=128:SY=96:'SHIP POSITION
SD=0:SS=0:'DIRECTION AND SPEED

@LOOP
VSYNC 1:GCLS:CLS

GOSUB @SETB
IF B$=="U" THEN SS=SS+1
IF B$=="D" THEN SS=SS-1
IF B$=="L" THEN SD=SD-3
IF B$=="R" THEN SD=SD+3

IF 0>SS THEN SS=0
SD=(SD+360)%360

DX=(SS/16)*COS(RAD(SD))
DY=(SS/16)*SIN(RAD(SD))

SX=(SX+DX+256)%256
SY=(SY+DY+192)192

SPANGLE 0,SD
SPOFS 0,SX,SY

GOTO @LOOP

@SETB
B=BUTTON(0):B$=""
IF B AND 1 THEN B$="U"
IF B AND 2 THEN B$="D"
IF B AND 4 THEN B$="L"
IF B AND 8 THEN B$="R"
RETURN

8. A simple game: Asteroid


Let's see if you can take what we have, and build a simple game of Asteroid.

ACLS
SPSET 0,136,0,0,0,0,16,16
SPHOME 0,8,8
SPCOL 0,0,0,8,8,FALSE,&H10
SPANGLE 0,270
SPOFS 0,128,96
SX=128:SY=96:'SHIP POSITION
VX=0:VY=0:'SHIP VELOCITY
SD=0:SS=0:'DIRECTION AND SPEED
BL=0
NX=-50:NY=-50
GOSUB @BSTART
GOSUB @RSTART
SCORE=0:SHOT=1:COL=0
MAXEN=50:TT=0
WAIT 30

@LOOP
LOCATE 0,0
?"HIT=";SCORE;"   SHOT=";SHOT;"   COL=";COL;"   "
?"ACC=";FLOOR(SCORE*100/SHOT);"   MAXEN=";MAXEN;"   "
VSYNC 1:'GCLS:CLS

B=BUTTON(0)
IF B AND 1 THEN SS=1 ELSE SS=0
IF B AND 2 THEN SS=0
IF B AND 4 THEN SD=SD-3
IF B AND 8 THEN SS=0
SD=(SD+360)%360
'IF B AND 16 THEN GOSUB @FIRE

DX=(SS/16)*COS(RAD(SD))
DY=(SS/16)*SIN(RAD(SD))

VX=(VX+DX)
VY=(VY+DY)
IF VX< -5 THEN VX=-5
IF VY< -5 THEN VY=-5
IF VX>5 THEN VX=5
IF VY>5 THEN VY=5

SX=(SX+VX+256)%256
SY=(SY+VY+192)%192
SPANGLE 0,SD
IF SPHIT(0)!=0 THEN COL=COL+1
IF B AND 16 THEN GOSUB @FIRE
GOSUB @BLT
GOSUB @RLT

CNT=CNT+1:IF CNT>1000 THEN CNT=1000
TT=TT+1
GOTO @LOOP

@RLT
FOR I=50 TO 99
IF I>MAXEN GOTO @RLT2
IF SPCHK(I)==8 THEN SPOFS I,RND(256),RND(192),RND(300)+100
@RLT2
NEXT
LOCATE 0,3:?"C=";C;"  "
IF C+50>MAXEN THEN MAXEN=MAXEN+1:C=0
RETURN

@BLT
LOCATE 20,0
FOR I=1 TO 20
IF SPCHK(I)==8 THEN SPOFS I,NX,NT
IF SPHIT(I)!=0 THEN GOSUB @HIT
NEXT
RETURN

@HIT
IF SPHITNO<=20 THEN RETURN
SPOFS SPHITNO,NX,NY
SPOFS SPHITNO,RND(512)-128,NY-30,700
SPOFS I,NX,NY
SPCHR SPHITNO,192+8*RND(4)
BEEP 13
SCORE=SCORE+1
C=C+1
RETURN

@BSTART
FOR BL=1 TO 20
SPSET BL,40,0,0,0,0,16,16
SPHOME BL,8,8
SPCOL BL,0,0,4,4,FALSE,&H01
SPANIM BL,2,10,0
SPANGLE BL,SD
SPOFS BL,NX,NT
NEXT
RETURN

@RSTART
FOR I=50 TO 99
SPSET I,200,RND(16),0,0,0,16,16
SPHOME I,8,8
SPCOL I,0,0,8,8,FALSE,&H11
SPANIM I,2,10,0
SPOFS I,299,265
NEXT
RETURN

@FIRE
IF CNT>5 THEN CNT=0 ELSE RETURN
BL=BL+1:IF BL>20 THEN BL=1
SPOFS BL,SX,SY
SPANGLE BL,SD
DX=300*COS(RAD(SD))
DY=300*SIN(RAD(SD))
SPOFS BL,SX+VX+DX,SY+VY+DY,100
BEEP 10
SHOT=SHOT+1
RETURN



Saturday, August 17, 2013

Petit Computer Journal #10


Graphics and Paint


We are going to go ahead and do all the graphic routines. I think the best way to do it is to just make a paint program. It won't be the prettiest paint program ever, but it'll do to make us understand how the graphic works. For the interface, I'll use Directional Pad for choosing colors and Tools. Touchscreen is for drawing. Turn the panel off by PNLTYPE "OFF"

'SHAPE MAKER
ACLS:GPAGE1:PNLTYPE "OFF"
S=0:MAXS=12:

S is current shape number. MAXS is Max shape number. Starts with one, and as time goes on, keep increasing it.

@LOOP
GOSUB @DT
VSYNC 1:B=BUTTON(3)
IF B AND 1 THEN C=(C+1)%16
IF B AND 2 THEN C=(C+16-1)%16
IF B AND 4 THEN S=(S+MAXS-1)%MAXS
IF B AND 8 THEN S=(S+1)%MAXS

X=TCHX:Y=TCHY
FOR I=1 TO 1:I=!TCHST:IF I==0 THEN GOSUB @SETR:GOSUB @DRAW
NEXT
GOTO @LOOP

The interface routine is simplicity itself. Basically, either use the directional pad to change color and shape, or detect Touch screen for drawing. This is done simply because it was done spontaneously while waiting for something. I added the number of shapes gradually over time.

@DT
CLS:COLOR 0
?"COLOR=";
GPAGE 0:GFILL 50,0,100,50,C:GPAGE 1
LOCATE 0,10:?"SHAPE=";
IF S==0 THEN ?"CIRCLE"
IF S==1 THEN ?"LINE"
IF S==2 THEN ?"BOX"
IF S==3 THEN ?"FILL"
IF S==4 THEN ?"LINETO"
IF S==5 THEN ?"PAINT"
IF S==6 THEN ?"PSET"
IF S==7 THEN ?"PAINT BORDER"
IF S==8 THEN ?"AIRBRUSH"
IF S==9 THEN ?"AIRBRUSH STENCIL"
IF S==10 THEN ?"PSET STENCIL"
IF S==11 THEN ?"HEAVY AIRBRUSH STENCIL"
RETURN

This is to draw the top screen, showing the color and the shape tools. If the selection is somewhat random, it is because I add to it random. The first four was done immediately. Then I add LINETO. PAINT,PSET follows. PAINT BORDER because I want to learn how to use it. The rest is my desire to add stipling to the graphic.

It is actually an improvisation. A good way to do it is using ELSE-IF ladder. A better way to do it would be using Array. The best way to do it is by using Graphics, icons, and highlights. I'm not going to do that for now.

@SETR
DX=TCHX-X:DY=TCHY-Y
R=SQR(DX*DX+DY*DY)
RETURN

Set Radius. This is used for CIRCLE.

@DRAW
GCOLOR C
S=S%MAXS:ON S GOSUB @CIR,@LIN,@BOX,@FIL,@LI2,@PAI,@SET,@BOR,@AIR,@STE,@PST,@HAIR

It doesn't get any simpler than that. Who knew computer programming can be so easy? I know I'm bucking the convention here, but I really don't think much about computer programming. Computer programming is merely the translation from English (human) language to BASIC (computer) language. It can be done mechanically. Design, which requires you to specify something that doesn't exists, is a much more challenging endeavour, simply because there is no way to be sure what you do is the best way to do it. You must experiments with different things.

I like to have 3 different solutions to the problem: A good one, a better one, and a best one. It has served me well throughout the years and something I suggest you adopt in order to expand your skills as computer programmer.

@CIR
FOR J=0 TO R:GCIRCLE X,Y,J:NEXT
RETURN

@LIN
GLINE X,Y,TCHX,TCHY
RETURN

@BOX
GBOX X,Y,TCHX,TCHY
RETURN

@FIL
GFILL X,Y,TCHX,TCHY
RETURN

Do I really need to explain these? They are self-explanatory. I will mention that loop for circle. The circle command isn't filled. I don't find much use for rings. I want circles. This is a design decision. So, I wrote it like that. Filled CIRCLE! It's not perfect, though, and you are encourage to fix it.

The color was set by GCOLOR C statement above. I could, and indeed, should, use the expanded command of specifying color at the end: GLINE X,Y,TCHX,TCHY,C on all commands. It's part of unifying aspect of the program.

@LI2
X2=TCHX:Y2=TCHY
GLINE X,Y,X2,Y2
X=X2:Y=Y2
RETURN

This is simply connecting line. If you look at the Touch screen code above, you will see that the last point of the line becomes the first point of the next line. Easy as pie!

@PAI
GPAINT X,Y
RETURN

@SET
IF TCHST==1 THEN GPSET TCHX,TCHY:GOTO @SET

@BOR
GPAINT X,Y,C,C+1
RETURN

GPAINT is easy enough. How about @BOR? That's GPAINT with border. I want to see how the paint functions with border color specified. Here, I specified C+1 as the border. Obviously, you can specify any color you want as the border, not just the next one.

Regarding GPSET, I put it in a loop because I want to do stippling technique. No mystery there.

@AIR
FOR W=1 TO 200:NEXT
IF TCHST==1 THEN GPSET TCHX+(RND(17)-8,TCHY+(RND(17)-8):GOTO @AIR
RETURN

@STE
VC=GSPOIT(X,Y)
@STE2
VSYNC 1:VX=TCHX+(RND(11)-5):VY=TCHY+(RND(11)-5)
IF GSPOIT(VX,VY)==VC THEN GPSET VX,VY
IF TCHST==1 GOTO @STE2
RETURN

@PST
VC=GSPOIT(X,Y)
@PST2
VX=TCHX:VY=TCHY
IF GSPOIT(VX,VY)==VC THEN GPSET VX,VY
IF TCHST==1 GOTO @PST2
RETURN

@HAIR
VC=GSPOIT(X,Y)
@HAIR2
VX=TCHX+(RND(11)-5):VY=TCHY+(RND(11)-5)
IF GSPOIT(VX,VY)==VC THEN GPSET VX,VY
FOR W=1 TO 200:NEXT:'WAIT AWHILE
IF TCHST==1 GOTO @HAIR2
RETURN

The @AIR airbrush is simple enough. There is a WAIT 200 cycles because if not, the pixels are sprayed remarkably quickly. Otherwise, it's just a random GPSET within a square.

@STE Stencil is the same way with the added factor GSPOIT() where VC is set to the current color. The pixel won't be plotted unless the pixel color is the same as the original VC.

@PST GPSET with Stencil. Enough said.

@HAIR Heavy airbrush. This is where I experimented the most. The discoveries found here was shared with other functions, notably the WAIT 200 cycles.

And you know what? The implementation of the program itself was really quick. So much so, in fact, that it was done while there was still waiting time. I spent the rest of the time doodling. Dogs, Flowers, People, and other sketches. It really works! It makes me wonder whether or not I should spend points getting The New Art Academy!

Haha, joking aside, I did do several renderings that I like very much. I experimented with saving the pictures. Not too successful, so I will need to revisit the issue in later journal. Until then, please have fun with drawing sketches, and share!

UPDATE:

I managed to implement LOAD/SAVE GRP files. Pretty simple. SAVE GRP0:filename (top screen). GRP1:filename (bottom screen). Also GRP2, and GRP3, for back buffer screens. I will give you the subroutine codes only. I assume you know how to integrate it to the program.

@BIG :'BIG LINE TO
X2=TCHX:Y2=TCHY
GLINE X,Y,X2,Y2
GLINE X+1,Y,X2+1,Y2
GLINE X-1,Y,X2-1,Y2
GLINE X,Y+1,X2,Y2+1
GLINE X,Y-1,X2,Y2-1
X=X2:Y=Y2
RETURN

@SAV
SAVE "GRP1:G0"
RETURN

@LOA
LOAD "GRP1:G0"
RETURN

Also, there's no reason why we can't have at least reasonable User Interface. Try this:

@LOOP
GOSUB @DT
VSYNC 1:B=BUTTON(3)
IF B AND 8 THEN C=(C+1)%16
IF B AND 4 THEN C=(C+16-1)%16
IF B AND 1 THEN S=(S+MAXS-1)%MAXS
IF B AND 2 THEN S=(S+1)%MAXS

That's right. We just exchanged the up/down with left/right. What does this change give us? How about this?

@DT
CLS:GPAGE 0

GFILL 0,160,255,175
GFILL C*16,160,(C+1)*16,175,C
FOR I=0 TO 15
GFILL I*16,175,(I+1)*16-2,191,I
NEXT

COLOR 0
LOCATE 0,0:?"ACTION"
?"   CIRCLE"
?"   LINE"
?"   BOX"
?"   FILL"
?"   LINETO"
?"   PAINT"
?"   PSET"
?"   PAINT BORDER"
?"   AIRBRUSH"
?"   AIRBRUSH STENCIL"
?"   PSET STENCIL"
?"   HEAVY AIRBRUSH STENCIL"
?"   BIG LINETO"
?"   SAVE GRP1:G0"
?"   LOAD GRP1:G0"

LOCATE 0,S+1:?">"

GPAGE 1
RETURN

Now the interface have gone from only a Dilbert nerd would tolerate, to the average programmer would tolerate. Quite a big difference with only small changes involved. Small, but significant changes. I made the mistake of coding a lot of PRINT, only to take them out once I realized this small change is all it takes. The lessons here are:

  1. Good is the enemy of Great
  2. Do not be afraid in discarding bad code!
  3. Think then Do. Do then Think results in a lot of time wasted writing and discarding bad code.


I would love to have zoom, but the whole interface must be redesigned. Maybe a speech balloon and layer option, hmmm?