Showing posts with label code. Show all posts
Showing posts with label code. Show all posts

Tuesday, December 11, 2012

Petit Computer Picross


I'm curious of how well can people copying code from text as opposed to just scan QR code. So, this will come only as source code, no QR. Let's see how well you can copy this, and hopefully understand it. I won't be explaining anything. The source code tells all!
Major bragging rights to the first person who can change P[] array to meaningful numbers. 
PS: I did this in 2 hours pecking at the screen with the stylus! How far can you go? Keep pushing!
CLS:CLEAR
DIM P[16]
GOSUB @INIT
GOSUB @DISP
@LOOP
GOSUB @PANE
GOSUB @MOVE
IF G$==P$ GOTO @END
GOTO @LOOP
@END
BEEP 7:LOCATE 18,18:?"WIN!"
FOR I=1 TO 1:VSYNC 1:I=BUTTON(3):NEXT
END
@MOVE
FOR I=1 TO 1:VSYNC 1:B=BUTTON(3):I=B:NEXT
BEEP 66
IF B AND 32 THEN G$=SUBST$(G$,CY*16+CX,1,"0")
IF B AND 16 THEN G$=SUBST$(G$,CY*16+CX,1,"1")
IF B AND 1 THEN CY=CY+15
IF B AND 2 THEN CY=CY+1
IF B AND 4 THEN CX=CX+15
IF B AND 8 THEN CX=CX+1
CX=CX%16:CY=CY%16
SPOFS 1,CX*8,CY*8
RETURN
@PANE
FOR I=0 TO 15:FOR J=0 TO 15
LOCATE J,I
IF "0"==MID$(G$,I*16+J,1) THEN ?CHR$(32) ELSE ?CHR$(224)
NEXT:NEXT
RETURN
@DISP
CLS
FOR I=0 TO 255 STEP 16
H$="":C=0
FOR J=0 TO 15
IF "1"==MID$(P$,I+J,1) THEN C=C+1
IF "0"==MID$(p$,I+J,1) THEN IF C THEN H$=H$+STR$(C):C=0
NEXT
IF C THEN H$=H$+STR$(C)
LOCATE 17,I/16:?H$
NEXT
FOR I=0 TO 15
H$="":C=0
FOR J=0 TO 15
IF "1"==MID$(P$,I+(J*16),1) THEN C=C+1
IF "0"==MID$(p$,I+(J*16),1) THEN IF C THEN H$=H$+STR$(C):C=0
NEXT
IF C THEN H$=H$+STR$(C)
FOR J=0 TO LEN(H$)
LOCATE I,16+J:?MID$(H$,J,1);
NEXT
NEXT
RETURN
@INIT
P[0]=2016
P[1]=14364
P[2]=16770
P[3]=33729
P[4]=35793
P[5]=40377
P[6]=24186
P[7]=19506
P[8]=12300
P[9]=21066
P[10]=18834
P[11]=38505
P[12]=44085
P[13]=25638
P[14]=24582
P[15]=4104
FOR I=0 TO 15
V=P[I]:T$=""
FOR J=0 TO 15
T$=STR$(V%2)+T$:V=FLOOR(V/2)
NEXT
P$=P$+T$
NEXT
G$="0"*LEN(P$)
SPSET 1,156,0,0,0,0,16,16
SPANIM 1,4,15
SPSCALE 1,50
CX=0:CY=0
RETURN

Monday, October 8, 2012

Petit Computer Journal#10


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?

Monday, September 3, 2012

Petit Computer Journal #5


Petit Computer Journal #5 - Buttons and Touchscreen

Let's take a little detour for now. We should be doing strings and graphics, but I want to do something else real quick: Buttons, Touchscreen, and Keyboard. In other words: INPUT.

We have done buttons, touchscreen, and keyboard inputs before. However, I'm interested in doing them all at once. And the trick is to do it without stopping the other input methods. That's not too easy.

Regarding keyboard input method that doesn't stop other process, we have INKEY$. We also have touchscreen variables TCHX,TCHY and all those. How about buttons? We have BUTTON(0), and that is sufficient. So, at the surface, we have all that we need.

The thing is, I don't want to have to structure the program into multi-threading format at this point in time. So, we will have to make some sacrifices. The INKEY$ is well enough. How about buttons and touchscreen?

In Touchscreen, it is convenient to have a drag-and-drop process. That means X1,Y1,X2,Y2,TouchStatus. Let's build that capability.

@SETT
IF TCHST==0 THEN TS1=TCHST:RETURN
IF TS1==0 THEN TX1=TCHX:TY1=TCHY:TS1=1
IF TS1==1 THEN TX2=TCHX:TY2=TCHY
TS1=TCHST
RETURN

That looks simple enough. Basically, we want to update the variables if TCHST==1. The first line takes care of that by returning from subroutine if TCHST==0. Next, we want to see which pair we want to update X1,Y1 or X2,Y2? And that's all there is to it!


The buttons isn't so simple, though. There are 4 possible arrangements that I can see:
1. No Wait+Multiple: BUTTON(0)
2. No wait+Single: @SETB1
3. Wait+Multiple: @SETB2
4. Wait+Single: @SETB3

Of these, we want no wait version. If the no wait version is equivalent to INKEY$, then the wait version is equivalent to INPUT. The whole process involve trying out different versions of the commands. You see the finished product as clean, but I assure you that the process involves repeatedly trying and failing to come up with that clean method. You do not see the hard work that is done. At least, if you ever wonder why my progress is at glacial pace, you know the reason: Lacking tutorial such as this, I do a lot of experiments, not all of them successful.

There are 4 cases and only 3 subroutines. The first case can be easily met via BUTTON(0). The rest is done with simple subroutines. It only works on the first 8 bits, corresponding to UDLRABXY. This is because the method I use requires string characters, and those only goes to 255. I typed in the character in the actual program, but for the purpose of tutorial there are two index variables used by INSTR()

1. IST1$=CHR$(128)+CHR$(64)+CHR$(32)+CHR$(16)+CHR$(8)+CHR$(4)+CHR$(2)+CHR$(1)
2. IST2$=CHR$(129)+CHR$(65)+CHR$(33)+CHR$(17)+CHR$(9)+CHR$(5)+CHR$(3)+CHR$(2)

I use this technique a lot as it simplifies things greatly. It's not the fastest running code, and so only amateur hobbyist would use it. Certainly not a professional quality code. If need be, I may changed the code later to a more efficient one. But I like doing rapid prototyping in the beginning.

One more thing, the no wait version is tricky. If you check out the clock, you will see that no-wait @SETB1 does cause the program to stop when you press the button for a long time. A way to fix this would be to use a variable, but I would rather just do it directly with BUTTON(0) or @SETB2.

@SETB1 :'SINGLE FIRE
INBUTTON$="":Z=BUTTON(0):IF!Z THEN RETURN
FOR Z=0 TO 1:Z=BUTTON(3):NEXT:Z=Z AND 255
Z=INSTR(IST2$,CHR$(Z)):IF Z< 0 THEN RETURN
INBUTTON$=MID$("YXBARLDU",Z,1)
RETURN

@SETB2 :'CONTINUOUS
INBUTTON$="":Z=BUTTON(0):IF!Z THEN RETURN
Z=Z AND 255:Z=INSTR(IST1$,CHR$(Z)):IF Z< 0 THEN RETURN
INBUTTON$=MID$("YXBARLDU",Z,1)
RETURN

@SETB3 :'WAIT
INBUTTON$=""
FOR Z=0 TO 1:VSYNC 1:Z=BUTTON(1):NEXT:Z=Z AND 255
Z=INSTR(IST2$,CHR$(Z)):IF Z< 0 THEN RETURN
INBUTTON$=MID$("YXBARLDU",Z,1)
RETURN

And those are the functions. Next, let's write a quick demo program to demonstrate the different functions. It may be best that you write a program and save it because you will be using this at all times. I know I do!

'BUTTON/TOUCHSCREEN TEST EXAMPLE
CLS:CLEAR:P1=0:P2=1
@MAINLOOP
LOCATE 0,0:?TIME$
VSYNC 1:A$=INKEY$():?A$
GOSUB @SETB1:'?INBUTTON$;
GOSUB @SETT

IF INBUTTON$!="" OR TS1 THEN GOSUB @DT
GOTO @MAINLOOP

@DT :'DRAW TEXT
IF INBUTTON$!="" THEN L1=(L1+1)%32:LOCATE L1,1:?INBUTTON$;

'DRAW BOX
IF INBUTTON$=="L" THEN P1=P1+15
IF INBUTTON$=="R" THEN P1=P1+1
IF INBUTTON$=="U" THEN P2=P2+15
IF INBUTTON$=="D" THEN P2=P2+1
P1=P1%16:P2=P2%16
SX1=FLOOR(TX1/8):SY1=FLOOR(TY1/8)
SX2=FLOOR(TX2/8):SY2=FLOOR(TY2/8)
FOR X=SX1 TO SX2:FOR Y=SY1 TO SY2:
C$=CHR$(151):COLOR P2:'0=BIG BLOCK CHARACTER IN PETIT COMPUTER
IF X==SX1 OR X==SX2 THEN C$=CHR$(150):COLOR P1:'1=VERT LINE
IF Y==SY1 OR Y==SY2 THEN C$=CHR$(149):COLOR P1:'2=HORZ LINE
IF X==SX1 AND Y==SY1 THEN C$=CHR$(152):COLOR P1:'3=UPPERLEFT
IF X==SX2 AND Y==SY1 THEN C$=CHR$(153):COLOR P1:'4=UPPERRIGHT
IF X==SX1 AND Y==SY2 THEN C$=CHR$(154):COLOR P1:'5=LOWERLEFT
IF X==SX2 AND Y==SY2 THEN C$=CHR$(155):COLOR P1:'6=UPPERRIGHT
LOCATE X,Y:?C$;
NEXT:NEXT

RETURN

For some reason, my computer does not read my memory card. That's a setback. I have to have those special characters, and so, I'm forced to do it the hard way, which is very annoying. However, either I overcome that setback, or I don't do this at all. I can work on the DSi no problem, but if I want to share it, I have to do this thankless work of translating those characters into their numeric equivalent. I wrote a simple program just for that:

'ASCII TABLE
S=0
@MAINLOOP
CLS
FOR I=S TO S+15
R=I%16
LOCATE 0,R:?I;:LOCATE 5,R:?CHR$(I)
NEXT

GOSUB @SETB3

IF INBUTTON$=="U" THEN S=S+16
IF INBUTTON$=="D" THEN S=S+256-16
S=S%256
?:?"S=";S;"   ";INBUTTON$:WAIT 60:'OPTIONAL FOR DEBUGGING
GOTO @MAINLOOP

And that's it. Not even 10 minutes. You need to provide Subroutine @SETB3, but that's trivial. Just copy the one above.



Problems and How to Ask Questions

You know how people say there's no such thing as stupid questions? I know I'm bucking the convention here, but I'd say there are! Here's a sample, quoted in its entirety:

"Help! SAVE doesn't work."

I'm not saying that SAVE command is so easy that it cannot fail. I am saying that the question doesn't even begin to show the framework in which the problem occurs. We need more data! You know how PRINT statement works, right? What if there's somebody who ask help like this: "How do you use PRINT?", following your answer with "It doesn't work."

You know it works, and you know how it works. The problem is, how does it doesn't work? You have no clue as to what problem this person encounter. So, here is how you handle a problem that you cannot solve, because the unwritten rule is, if you ask a question that you later answer without any prompting whatsoever, YOU JUST ASKED A STUPID QUESTION THAT YOU KNOW THE ANSWER TO!

Problem solving technique:
1. Ran into problem, WRITE IT DOWN!
2. Write down all the relevant elements.
3. Read the Manual/Help file
4. WRITE ALL THE POTENTIAL SOLUTIONS DOWN.
5. Implement them all.

That's step-by-step. You are not allowed to skip steps. Half of your problems can be solved this way. As for the rest, well, that's when it gets tricky.

Hard Problem Solving:
1. You are tired. STOP AND GO TO SLEEP!
2. Wake up. Eat something solid
3. Repeat problem solving steps above.

By this time, if you followed this advice, a lot of you would do a lot of face palming "Of course! Why didn't I think of that?" sequence. That happens to me, too.

Stubborn Problem Solving:
1. You are sadly misunderstanding the problem. YOU are at fault!
2. Find 3 different interpretations to the problem.
3. Also, find 3 different OTHER places where it may cause the problem.
4. Consult the manual for help.

It may help to pretend that you're a newbie who doesn't understand everything. Don't laugh. It works! I used that technique myself occasionally. For the next level you must first admit that you are stupid. No, really. You are! You may humbly ask other people for help. Ever seen somebody arrogantly ask for solution to their problem? That never gets resolved, does it? Bingo.

Impossible Problem Solving:
1. Explain What the Problem is
2. Tell what you think are the relevant elements
3. Show what you did to solve the problem
4. WRITE THE SIMPLEST, SHORTEST CODE to explain the problem.

That last element is vital. No one wants to read 200 lines of code just to debug your program. So, there. Problem solving explained. Either that or you explain your problem to a duck.

Haha, joking aside, the ability to properly explain your problem is crucial in getting it solved. You don't want to ask a question like a grade schooler if you can ask your questions like a professor!

My PRINT doesn't work!

1. Did you type it in RUN(direct) mode or EDIT (deferred) mode?
2. Did it give you Syntax Error?
3. Did it print 0?
4. Did you set VISIBILITY?

What if PRINT doesn't work because it was set to XOR Mode? How will you respond to that? You can't set Console to XOR mode, right? How does that work? This is where giving out sample code is crucial.

CLS
COLOR SET XOR ! DOIT
PRINT "HELLO WORLD"

SET and DOIT ARE not keywordS. Why is there an exclamation mark preceding it? Because when I put it after the word (DOIT!), the computer complained, DUH!

You see how sample source code clarifies the issue quickly and easily? Don't act like a grade schooler. Ask questions like a professor! When I see the words "I don't understand ..." it'd better be followed by "These are the things I tried in order to understand it."



Monday, August 20, 2012

Petit Computer Journal#3


Petit Computer Journal #3

How to use Buttons and Directional Pad

Let's do something real quick. Let's make a counter that will increase when a button is pushed. Looking at the various commands, it is obvious that it is either BUTTON() or BTRIG(). Let's use BTRIG(). Reading the Help entry shows that the value returned by BTRIG() is usually 0. Non-zero if there's a button pushed. Perfect. We come up with this very quickly:

CLS
@COUNTER
B=BTRIG()
IF B==0 GOTO @COUNTER
A=A+1:A=A%1000:'0-999 CYCLE
?A;"      "
GOTO COUNTER

We run the program, and press a button. We see that the counter increments, all right. And it cycles to 999 like it should. However, the program registers so many button presses. That is not what we want. We want 1 increment per button push. So, we need to fix it. At this point, most people would do the sensible thing and type into Google: "Petit Computer how to use button". 

Haha, joking aside, we do need to understand how to read buttons properly. What do we do when the platform does not have built-in debugger? Well, we make our own!


What to do when things go wrong

I know I'm bucking the convention, here, but writing self-debugging program isn't that much trouble. The most important thing is that you want to understand what's going on. The simplest, easiest method to do it is read the manual. When the manual fails, however, we need to do better. That means we need to try things out and see what happens. It's called, "Learning without a teacher" or "Research". If you passed high school, then you know how to do it.

First make a hypothesis: One button press=one event. (desired behavior)

Then gather data: One button press=many events (observed)

Therefore, the conclusion is: what we want, and what we have are two different things. Error!

There are many kinds of errors in computer programming, but let's just simplify things a bit for now.

Computers are stupid, remember? If you mistype a command, the computer cannot handle it. If you forget a variable, the computer cannot handle it. If you transposed commands or letters or anything, the computer cannot handle it. The computer will complain: "Syntax Error!" That means, you have typed something incorrectly as to confuse the computer.

Another kind of error occurs when you ask the computer to do the impossible. Dividing by zero is one. GOTO a unlabelled section is another. These kind of errors are called "Run-time Error", because they occur at the running of the program.

Sometimes the program runs fine, but doing something else than what we want. We want to roll 5 dice, but only 1 is rolled. That means that what we say and what we want to say are two different things. We need to figure out a different way to say what we want. This is called "Semantic Error".

If, however, your screen is cracked, plastic is chipped, and stylus bent, then we have what is called PEBKAC Error: Problem Exists Between Keyboard And Chair. You probably should stay off computer programming for a while.

A binary review

If you're wondering why the values for the buttons are 1,2,4,8 etc, the answer is that the buttons do not have separate variables. The figure is all one number! There are different bits to the number, and that is what we're seeing. We can take a look at various bits by doing this code:

V=9
FOR I=0 TO 7
P=POW(2,I)
IF (P AND V) THEN ?"1"; ELSE ?"0";
NEXT
PRINT

In this case, we're doing a "Binary AND" (instead of "Logical AND") using bit 0-7. You can, of course, increase the bits to 31. I'm trying to keep it simple. Try putting in different values into V variable.


Source code for button readings

If we want to see what the buttons do, then it is very simple to do this:

CLS
@MAINLOOP
A=BUTTON(0)
B=BUTTON(1)
C=BUTTON(2)
D=BUTTON(3)
E=BTRIG()
LOCATE 0,0
?"BUTTON(0) ";:V=A:GOSUB @BIN
?"BUTTON(1) ";:V=B:GOSUB @BIN
?"BUTTON(2) ";:V=C:GOSUB @BIN
?"BUTTON(3) ";:V=D:GOSUB @BIN
?"BTRIG()   ";:V=E:GOSUB @BIN

'PLACEHOLDER

GOTO @MAINLOOP

@BIN
FOR I=0 TO 7
P=POW(2,I)
IF (P AND V) THEN ?"1"; ELSE ?"0";
NEXT
PRINT
RETURN

You can see the flickers for Button 1-3. That means the change happens in an instant. In this case, the change happens only in one frame, or 1/60 of a second. That's fast! I also see that the flickers on B is identical with the flicker on E. From that, I deduce that BTRIG() is equivalent to BUTTON(1). Is there any delay or waiting either with BTRIG() or BUTTON()? None that I can see.

If you want to make sure, replace the "'PLACEHOLDER" with these lines:

IF A!=E THEN LOCATE 0,7 :V=A:GOSUB @BIN:V=E:GOSUB @BIN
IF B!=E THEN LOCATE 0,10:V=B:GOSUB @BIN:V=E:GOSUB @BIN
IF C!=E THEN LOCATE 0,13:V=C:GOSUB @BIN:V=E:GOSUB @BIN
IF D!=E THEN LOCATE 0,16:V=D:GOSUB @BIN:V=E:GOSUB @BIN

The suspicion that BTRIG()==BUTTON(1) is confirmed! Now that's what I call successful research!



The fixed source code

Now that we know what is happening, we can do different things to fix it. 

First, we can specify that the increase is between Button Push and Button Release, like so:

CLS
@COUNTER
B=BUTTON(2)
IF B==0 GOTO @COUNTER
A=A+1:A=A%1000:'0-999 CYCLE
@C2
B=BUTTON(3)
IF B==0 GOTO @C2
?A;"      "
GOTO COUNTER

And that works nicely. However, let's see if we can improve things a bit. The manual mention VSYNC 1, as a way to get the input right. So, let's use that

CLS
@COUNTER
B=BUTTON(2):VSYNC 1
IF B==0 GOTO @COUNTER
A=A+1:A=A%1000:'0-999 CYCLE
?A;"      "
GOTO COUNTER

That also works, and notice that it is cleaner. What happens if VSYNC is set to something other than 1? Try it! You'll see that you will be missing some button presses. The exception to that is if you use BUTTON(0), and only at the sync.

CLS
@COUNTER
B=BUTTON(0):VSYNC 15:IF B==0 GOTO @COUNTER
A=A+1:A=A%1000:'0-999 CYCLE
?A;"      "
GOTO COUNTER

And that's what we want. Problem solved!

The rest of Console Entry Commands

Try out this command: BREPEAT 4,60,4
That will cause the A button to be repeated if you press it longer than 1 second. Pretty neat, eh?

Also, what is the difference between INKEY$, INPUT, and LINPUT?
INKEY$ behaves just like BUTTON(), in that it doesn't stop and wait for keypress.
LINPUT, LINE-INPUT, takes the whole line, including commas.
INPUT takes the line, and assign different values separated by commas.

And that's all there is to it!

Tuesday, August 14, 2012

Japanese Abacus Soroban 2



'Soroban - Japanese Abacus - SKH302-2
'Harry Hardjono
'August 2012
'
'Apparently, moving shapes is faster than show/hide shapes.

InitLoop:
GraphicsWindow.MouseUp=OnMouse
GraphicsWindow.Title="Small Basic Abacus"
GraphicsWindow.Width = 400
GraphicsWindow.Height=400
GraphicsWindow.FontSize=20
GraphicsWindow.FontName="Courier"
GraphicsWindow.Clear()
GraphicsWindow.Show()
GraphicsWindow.BrushColor="white"
GraphicsWindow.FillRectangle(0,0,GraphicsWindow.Width,GraphicsWindow.Height)
GraphicsWindow.BrushColor="black"

DoBG()
SetBead()
For i=0 To 14
BB[i]=0
SB[i]=0
EndFor
DoBead()
DoDigit()
IBB=BB
ISB=SB

DrawLoop:
BB=IBB
SB=ISB
x= GraphicsWindow.MouseX
y= GraphicsWindow.MouseY

DoMouse()
DoBead()
DoDigit()


Program.Delay(150) 'Wait 150 miliseconds
Goto DrawLoop

Sub DoBG
  GraphicsWindow.BrushColor="Black" 'Draw Frame
  GraphicsWindow.FillRectangle(0,0,399,212)
  GraphicsWindow.BrushColor="White"
  GraphicsWindow.FillRectangle(12,12,375,188)
  GraphicsWindow.BrushColor="Brown"
  For i=0 to 14
  GraphicsWindow.FillRectangle(23+(i*25),12,3,188)
  endfor
  GraphicsWindow.BrushColor="Black" 'Draw Slate & Rod
  GraphicsWindow.FillRectangle(0,62,399,13)

EndSub

Sub SetBead
'BigBead
  For i=0 to 14
  GraphicsWindow.BrushColor=GraphicsWindow.GetRandomColor()
  SSBB[0][i]=Shapes.AddEllipse(25,25)
  Shapes.Move(SSBB[0][i],12+(i*25),12)
  EndFor

'SmallBead
For j=0 To 3
  For i=0 to 14
  GraphicsWindow.BrushColor=GraphicsWindow.GetRandomColor()
  SSSB[j][i]=Shapes.AddEllipse(25,25)
  Shapes.Move(SSSB[j][i],12+(i*25),75+(j*25))
  EndFor
endfor

'Digits
  For i=0 to 14
  GraphicsWindow.BrushColor=GraphicsWindow.GetRandomColor()
  SSDG[i]=Shapes.AddText("0")
  Shapes.Move(SSDG[i],17+(i*25),212)
  EndFor

EndSub

Sub DoDigit
  For i=0 to 14
      Shapes.SetText(SSDG[i],(5*BB[i]+SB[i]))
endFor
EndSub


Sub DoBead
  For i=0 to 14
        Shapes.Move(SSBB[0][i],12+(i*25),12+(BB[i]*25))
EndFor

    For i=0 to 14
     For j=0 to 3
      If (SB[i]<=j) then
        Shapes.Move(SSSB[j][i],12+(i*25),75+((j+1)*25))
        Else
        Shapes.Move(SSSB[j][i],12+(i*25),75+((j)*25))
    endif  
    EndFor
EndFor

EndSub


Sub DoMouse
  map_var="mx1=12;mx2="+x+";mx3=387;my1=0;my3=15" 'Xcoord mapped to 0-14
  map()
  cx=Math.Floor(map_var["my2"])
  If (cx>=0 and cx<=14) then
    If (y>12 And y<62 p="p" then="then">      map_var="mx1=12;mx2="+y+";mx3=62;my1=2;my3=0" 'Ycoord mapped to 1-0
      map()
      cy=Math.Floor(map_var["my2"])
      If (cy>=0 And cy<=1) Then
        BB[cx]=cy
      EndIf
    EndIf

    If (y>75 And y<200 p="p" then="then">      map_var="mx1=75;mx2="+y+";mx3=200;my1=0;my3=5" 'Ycoord mapped to 4-0
      map()
      cy=Math.Floor(map_var["my2"])
      If (cy>=0 And cy<=4) Then
        SB[cx]=cy
      EndIf
    EndIf
  EndIf

EndSub

'----------------------------------------------
'map function
'----------------------------------------------
Sub map
  'x1-x2-x3 y1-y2-y3
  '(x2-x1)/(x3-x1)=(y2-y1)/(y3-y1)
  'y1+(y3-y1)*(x2-x1)/(x3-x1)=y2
  map_var["my2"]=((map_var["my3"]-map_var["my1"])*(map_var["mx2"]-map_var["mx1"])/(map_var["mx3"]-map_var["mx1"]))+map_var["my1"]
EndSub
 
'----------------------------------------------
'event function
'----------------------------------------------
Sub OnMouse
  if (BB<>IBB Or ISB<>SB) Then
    Sound.PlayClick()
  EndIf
  IBB=BB
  ISB=SB
EndSub

 

Tuesday, August 7, 2012

Small Basic Auto Indent (with Subroutine lister)

'Indent - LDH635
'Harry Hardjono
'August 2012
'
CSD=0
indentlevel=0
indenttext="  "
indentarray=""
indentPlus=" sub for while if else elseif "
indentMin=" endsub endfor endwhile endif else elseif "


Filename=Program.Directory+"\indent1.sb"
FileSlurp()
For i=1 To Array.GetItemCount(FileData)
  SkipLeadSpace()
  CatalogSub()
  DoIndent()
  'TextWindow.WriteLine(OutData[i])
endfor


TextWindow.WriteLine("Sub List:")
For i=1 To Array.GetItemCount(SubData)
  TextWindow.Write(SubData[i])
  TextWindow.WriteLine("() ")
EndFor


Sub DoIndent
  StrIn=Text.ConvertToLowerCase(OutData[i])
  SpcInx=text.GetIndexOf(StrIn," ")
  If SpcInx=0 then
    SpcInx=Text.GetLength(StrIn)
  endif 
  StrIn=Text.GetSubText(OutData[i],1,SpcInx)
  StrIn=Text.ConvertToLowerCase(StrIn)
  If (text.GetLength(StrIn) > 1 And Text.GetIndexOf(indentMin,text.Append(" ",StrIn))>0) then
    indentlevel=indentlevel-1
    If indentlevel < 0 then
      indentlevel=0
    endif
    If indentlevel=0 then 
      indentarray[0]=""
    else
      indentarray[indentlevel]=text.Append(indentarray[indentlevel-1],indenttext)
    endif
    'TextWindow.WriteLine("DoIndentMin: "+StrIn)
  endif
  
  TextWindow.WriteLine(indentarray[indentlevel] + OutData[i])
  
  
  If (Text.GetIndexOf(indentPlus,StrIn) > 0) then
    indentlevel=indentlevel+1
    If indentlevel=0 then 
      indentarray[0]=""
    else
      indentarray[indentlevel]=text.Append(indentarray[indentlevel-1],indenttext)
    endif
    'TextWindow.WriteLine("DoIndentPlus: "+StrIn)
  endif
endsub




Sub CatalogSub
  StrIn=Text.ConvertToLowerCase(OutData[i])
  if text.StartsWith(StrIn,"sub ") then
    StrIn=Text.GetSubTextToEnd(StrIn,5)
    SpcInx=text.GetIndexOf(StrIn," ")
    If SpcInx=0 then
      SpcInx=Text.GetLength(StrIn)
    endif 
    StrOut=Text.GetSubText(OutData[i],1,SpcInx+4)
    
    CSD=CSD+1
    SubData[CSD]=StrOut
  endif
endsub


Sub SkipLeadSpace
  StrIn=FileData[i]
  StrOut=""
  For SLS_i=1 to Text.GetLength(StrIn)
    If (Text.GetCharacterCode(Text.GetSubText(StrIn,SLS_i,1))>32) then 'Non-space
      if (StrOut="") then  
        StrOut=Text.GetSubTextToEnd(StrIn,SLS_i)
      EndIf
    EndIf
  endfor  
  OutData[i]=StrOut
endsub




Sub FileSlurp
  'Read a file and assign it to an array
  'Input Filename (string)
  'Output FileData (array)
  FileLength=Text.GetLength(File.ReadContents(Filename))
  FileData=""
  FL=0
  FS_i=1
  While FL
    FileData[FS_i]=File.ReadLine(Filename,FS_i)
    FL=FL+Text.GetLength(FileData[FS_i])+2
    If FileData[FS_i]="" Then 'Fudge for blank lines in file
      FileData[FS_i]=" "
    EndIf
    FS_i=FS_i+1
  Endwhile
EndSub


Tuesday, May 15, 2012

Small Basic Converter Code

One of April challenges is writing a converter, the kind that converts meter to yard. A lot of people are doing it the hard coding way. I am way to busy to do that. So, I decided to spend one hour on it, and no more! The result is here. Not perfect, but close enough.

There are several design issues:
1. The display for various conversion and the result is rolled into one.
2. You only select the original data, all the possible conversions is done automatically.
3. Range is limited to valid input
4. The data is provided via text. You can modify the text to suit.
5. More importantly, the text also include range of numbers, which the program uses for conversion. The ratio for conversion is calculated automatically. This includes negative numbers, or shifting numbers (i.e. 1-5 into 3-7)
6. I use a hidden Main scale to facilitate ease of conversion.
7. Pay attention to Field[] entries! They defined the data field locations!

These design decisions helped me contained the implementation of this program into one hour.

I did encounter one bug. When copying Choice Loop into NumLoop, I forgot to change the Goto statement, so it went back to Choice Loop. Easily fixed.

Turns out, the algorithm for conversion is robust enough to handle out of range condition, so that I don't have to restrict its input. I could have avoided that bug after all.

It also means, that I don't have to show the range of numbers. It means I can just display the descriptions, and the converted numbers. It would make a cleaner presentation.


'Small Basic Converter - ZLW480
'By Harry Hardjono
'April 2012
'
Init:
MainMin=0
MainNum=0
MainMax=9999999

Field[1]=1 'N
Field[2]=3 'Description
Field[3]=14 'From
Field[4]=18 'Min
Field[5]=28 'To
Field[6]=30 'Max
Field[7]=40 ':

'Data[0]="N DescriptionFrom To :"
Data[1]="1 USD From 0 To 100000 : "
Data[2]="2 GBP From 0 To 158308 : "
Data[3]="3 CAD From 0 To 99962 : "
Data[4]="4 EUR From 0 To 130240 : "
Data[5]="5 AUD From 0 To 103447 : "

TextWindow.WriteLine("Small Basic Converter")

MainLoop:
For i=1 To Array.GetItemCount(Data)
TextWindow.Write(Text.GetSubText(Data[i],1,Text.GetLength(Data[i])))
mx1=MainMin
mx2=MainNum
mx3=MainMax
my1=Text.GetSubText(Data[i],Field[4],Field[5]-Field[4])
my3=Text.GetSubText(Data[i],Field[6],Field[7]-Field[6])
map()
TextWindow.WriteLine(my2)
EndFor
TextWindow.WriteLine(" ")
ChoiceLoop:
TextWindow.Write(("Which data(1-"+Array.GetItemCount(Data))+")?")
Choice=TextWindow.ReadNumber()
If (Choice<1 or Choice>Array.GetItemCount(Data)) then
TextWindow.WriteLine("Out of Range!")
Goto ChoiceLoop
endif
NumLoop:
TextWindow.WriteLine(Text.GetSubText(Data[Choice],Field[4],Field[5]-Field[4])+" to "+Text.GetSubText(Data[Choice],Field[6],Field[7]-Field[6]))
TextWindow.Write("Enter the amount: ")
Num=TextWindow.ReadNumber()
' If (NumText.GetSubText(Data[Choice],Field[6],Field[7]-Field[6])) then
' TextWindow.WriteLine("Out of Range!")
' Goto NumLoop
'endif

mx1=Text.GetSubText(Data[Choice],Field[4],Field[5]-Field[4])
mx2=Num
mx3=Text.GetSubText(Data[Choice],Field[6],Field[7]-Field[6])
my1=MainMin
my3=MainMax
map()
MainNum=my2

Goto MainLoop
Sub map 'map function
'x1-x2-x3 y1-y2-y3
my2=((my3-my1)*(mx2-mx1)/(mx3-mx1))+my1
EndSub

Tuesday, May 8, 2012

Small Basic Morse Code

I got bored one day, and hey, remember that Rock/Paper/Scissors game? That was quick. So I was hunting for a quick coding project, quicker than my usual one hour session. The Morse code is it. It took about 10 minutes, including typing all those entries. Of course, the original design was phonetic alphabet project, but I decided that typing Morse code entries would be faster than typing phonetic alphabet entries.

BTW, you don't have to limit yourself. You can do your own project like this, and substitute Klingon alphabet, for example. Do you know that Small Basic uses Unicode? Try looping some big numbers through Text.GetCharacter and you'll get the idea.

Of course, as luck would have it, I still have some time, so I improved it with tones. I think there is something wrong here because the sound is so soft! Oh, well.

'Extending the code, as well as decoding is left as an exercise for the reader!
Morse="a=+-;b=-+++;c=-+-+;d=-++;e=+;f=++-+;g=--+;h=++++;i=++;j=+---;k=-+-;l=+-++;m=--;n=-+;o=---;p=+--+;q=--+-;r=+-+;s=+++;t=-;u=++-;v=+++-;w=+--;x=-++-;y=-+--;z=--++;"
Loop:
TextWindow.Write("Enter Text:")
t=TextWindow.Read()
For i=1 To Text.GetLength(t)
TextWindow.Write((Morse[Text.ConvertToLowerCase(Text.GetSubText(t,i,1))])+" ")
EndFor
TextWindow.WriteLine(" ")
Goto Loop


'Extending the code, as well as decoding is left as an exercise for the reader!
Morse="a=+-;b=-+++;c=-+-+;d=-++;e=+;f=++-+;g=--+;h=++++;i=++;j=+---;k=-+-;l=+-++;m=--;n=-+;o=---;p=+--+;q=--+-;r=+-+;s=+++;t=-;u=++-;v=+++-;w=+--;x=-++-;y=-+--;z=--++;"
TextWindow.WriteLine("Morse Code Encoder by Harry Hardjono")
Loop:
TextWindow.Write(Text.GetCharacter(13)+Text.GetCharacter(10)+"Enter Text:")
t=TextWindow.Read()
For i=1 To Text.GetLength(t)
MC=Text.Append(MC,(Morse[Text.ConvertToLowerCase(Text.GetSubText(t,i,1))]+" ")) 'Comment this out for no sound
TextWindow.Write((Morse[Text.ConvertToLowerCase(Text.GetSubText(t,i,1))])+" ")
EndFor
PlayMorse() 'Comment this out for no sound
Goto Loop

Sub PlayMorse
TT="+=C12;-=C4; =P4;"
For j=1 To Text.GetLength(MC)
MT=Text.Append(MT,TT[Text.GetSubText(MC,j,1)])
EndFor
Sound.PlayMusic(MT)
MC=""
MT=""
EndSub

Tuesday, May 1, 2012

Small Basic Rock Paper Scissors Code

Just a little quickie, this time. I was looking at the Rock, Paper, Scissors that others have written, and Jason boiled it down to 2 lines. Which I think isn't strictly true. I'd have call it one line. Here is another one-liner. This one works by first splitting the input into 3, and splitting it again. Basically, a simpler way to handle multi-dimensional strings. I also added a touch of detail in that you actually type R/P/S as entry. I could have added the code to convert the input into uppercase, but it's long enough as it is.

I do not do tutorials, at least at this time, but I left out all the original code as comments so hopefully, you can learn from it.

'Rock Paper Scissor
' by Harry Hardjono
'April 2012 - GTJ601
'
'This is an implementation of Rock-Paper-Scissors
'Rock beat Scisscors
'Scisscors beat Paper
'Paper beat Rock
'
'The input requires capital letter, and is one of these:
'(R)ock, (P)aper,(S)cissors
'The program will randomly choose one of them
'and display the result.
'
'The output is encoded because I'm too lazy to type them out.
'">R" means "You choose Rock."
'">P" means "You choose Paper."
'">S" means "You choose Scissors."
'"vR" means "Computer chooses Rock."
'"vP" means "Computer chooses Paper."
'"vS" means "Computer chooses Scissors."
'"=W" means "You win!"
'"=L" means "Computer wins!"
'"=D" means "It's a draw!"
'So, ">RvS=W" is interpreted as:
'"You choose Rock. Computer chooses Scissors. You win!"
'
'The following is the original source code before
'I collapse them into one line.
'
'Loop:
'T1=">RvR=D>RvP=L>RvS=W>PvR=W>PvP=D>PvS=L>SvR=L>SvP=W>SvS=D"
'P1=(Text.GetIndexOf("RPS",TextWindow.Read())-1)
'T2=Text.GetSubText(T1,1+(P1*18),18)
'P2=(Math.GetRandomNumber(3)-1)
'T3=(Text.GetSubText(T2,1+(P2*6),6))
'TextWindow.WriteLine(T3)
'Goto Loop

Loop:
TextWindow.WriteLine((Text.GetSubText(Text.GetSubText(">RvR=D>RvP=L>RvS=W>PvR=W>PvP=D>PvS=L>SvR=L>SvP=W>SvS=D",1+((Text.GetIndexOf("RPS",TextWindow.Read())-1)*18),18),1+((Math.GetRandomNumber(3)-1)*6),6)))
Goto Loop

Tuesday, April 3, 2012

SmallBasic Turtle Logo Source Code 2

'Turtle Logo in Small Basic
'by Harry Hardjono
'Turtle angle speed x1 y1 x0 y0
'Turtle move moveto penup pendown
'Turtle turn turnleft turnright
'Turtle show hide
'
' I did this in one afternoon. Could've been faster if I ignore Show/Hide Turtle
' Updated with Shapes. Sure is a lot faster had I known this earlier!

GraphicsWindow.Show()
GraphicsWindow.Clear()

cmd1="init"
Toitle()
cmd1="size"
cmd2=8
Toitle()



For loop=1 to 360 Step 20
cmd1="moveto"
cmd2=sx/2
cmd3=sy/2
Toitle()
cmd1="turn"
cmd2=loop
Toitle()
cmd1="move"
cmd2=200
Toitle()
Program.Delay(1000)
endfor



beginloop:
GraphicsWindow.Clear()
cmd1="init"
Toitle()
Program.Delay(2000)
cmd1="show"
Toitle()

For loop=1 to 50
cmd1="penup"
Toitle()
cmd1="moveto"
cmd2=Math.GetRandomNumber(sx)
cmd3=Math.GetRandomNumber(sy)
Toitle()
cmd1="pendown"
Toitle()
cmd1="color"
cmd2=GraphicsWindow.GetRandomColor()
Toitle()
cmd1="size"
cmd2=Math.GetRandomNumber(10)
Toitle()
' Show Turtle is very slow!
' If (loop=10) Then
' cmd1="show"
' Toitle()
' ElseIf (loop=30) then
' cmd1="hide"
' Toitle()
' EndIf
For loop2=1 to Math.GetRandomNumber(6)
cmd1="turn"
cmd2=Math.GetRandomNumber(360)
Toitle()
cmd1="move"
cmd2=Math.GetRandomNumber(1000)
Toitle()
endfor
endfor
Program.Delay(15000)
Goto beginloop



Sub Toitle
'cmd1=command (input)
'cmd2=param1 (input)
'cmd3=param2 (input)
If (cmd1="init") then
sy=GraphicsWindow.Height
sx=GraphicsWindow.Width
'Bug: GetPixel does not take Background color
GraphicsWindow.BrushColor="white"
GraphicsWindow.FillRectangle(0,0,sx,sy)
x1=sx/2
y1=sy/2
x0=x1
y0=y1
p=1
angle=180
col="blue"
GraphicsWindow.BrushColor=col
siz=1
ts=1
SaveBG()
elseif (cmd1="penup") then
p=0
elseif (cmd1="pendown") then
p=1
elseif (cmd1="move") then
'forward
' LoadBG()
x0=x1
y0=y1
x1=x0+cmd2*Math.Sin(Math.GetRadians(angle))
y1=y0+cmd2*Math.Cos(Math.GetRadians(angle))
draw()
' SaveBG()
elseif (cmd1="moveto") then
' LoadBG()
x0=x1
y0=y1
x1=cmd2
y1=cmd3
draw()
' SaveBG()
elseif (cmd1="turn") then
LoadBG()
angle=cmd2
SaveBG()
elseif (cmd1="turnleft") then
LoadBG()
angle=angle+cmd2
SaveBG()
elseif (cmd1="turnright") then
LoadBG()
angle=angle-cmd2
SaveBG()
elseif (cmd1="color") then
col=cmd2
elseif (cmd1="size") then
siz=math.Max(1,cmd2)
siz=math.Min(10,siz)
elseif (cmd1="show") then
if (ts<>1) then
ts=1
SaveBG()
endif
elseif (cmd1="hide") then
LoadBG()
ts=0
endif

'These really need to be Mod
'Seems buggy - doesn't work with negative number?
' RemBugFix2()
'x1=Math.Remainder(x1,sx)
'y1=Math.Remainder(y1,sy)
'angle=Math.Remainder(angle,360)

If (ts=1) then
' DrawTurtle()
' Shapes.Move(TT,x1,y1)
Shapes.Move(TT,px+(siz/2),py+(siz/2))
endif

endsub

Sub DrawTurtle
tx1=10*Math.Sin(Math.GetRadians(angle))
ty1=10*Math.Cos(Math.GetRadians(angle))
tx2=10*Math.Sin(Math.GetRadians(angle+140))
ty2=10*Math.Cos(Math.GetRadians(angle+140))
tx3=10*Math.Sin(Math.GetRadians(angle+220))
ty3=10*Math.Cos(Math.GetRadians(angle+220))
TT=Shapes.AddTriangle(tx1,ty1,tx2,ty2,tx3,ty3)
endsub


Sub RemBugFix
While (px<0) px=px+sx EndWhile While (py<0) py=py+sy EndWhile endsub Sub RemBugFix2 While (x1<0) x1=x1+sx EndWhile While (y1<0) y1=y1+sy EndWhile While (angle<0) angle=angle+360 EndWhile endsub 'Draw Sub Draw If (p>0) then
If (x0<>x1 or y0<>y1) then
m=math.Max(math.Abs(x1-x0),math.Abs(y1-y0))
mx1=0
mx3=m
For i=0 To m
my1=x0
my3=x1
mx2=i
map()
px=my2
my1=y0
my3=y1
mx2=i
map()
py=my2
RemBugFix()
px=Math.Remainder(px,sx)
py=Math.Remainder(py,sy)
If (siz>1) then
GraphicsWindow.BrushColor=col
GraphicsWindow.FillEllipse(px,py,siz,siz)
else
GraphicsWindow.SetPixel(px,py,col)
endif
Shapes.Move(TT,px+(siz/2),py+(siz/2))
EndFor
endif
endif

'These really need to be Mod
'Seems buggy - doesn't work with negative number?
RemBugFix2()
x1=Math.Remainder(x1,sx)
y1=Math.Remainder(y1,sy)
angle=Math.Remainder(angle,360)

endsub

'map function
Sub map
'x1-x2-x3 y1-y2-y3
'(x2-x1)/(x3-x1)=(y2-y1)/(y3-y1)
'(y3-y1)*(x2-x1)/(x3-x1)=(y2-y1)
'y1+(y3-y1)*(x2-x1)/(x3-x1)=y2
my2=((my3-my1)*(mx2-mx1)/(mx3-mx1))+my1
EndSub


sub SaveBG
If (ts=1) then
DrawTurtle()



' BGc=0
' BGx1=Math.Floor(Math.Max(0,x1-10))
' BGx1=Math.Floor(Math.Min(sx,BGx1))
' BGy1=Math.Floor(Math.Max(0,y1-10))
' BGy1=Math.Floor(Math.Min(sy,BGy1))
' BGx2=Math.Floor(Math.Max(0,x1+10))
' BGx2=Math.Floor(Math.Min(sx,BGx2))
' BGy2=Math.Floor(Math.Max(0,y1+10))
' BGy2=Math.Floor(Math.Min(sy,BGy2))
' For BGy=BGy1 To BGy2
' For BGx=BGx1 To BGx2
' BGc=BGc+1
' BG_Array[BGc]=GraphicsWindow.GetPixel(BGx,BGy)
' EndFor
' endfor
' TextWindow.Write("SaveBG ")
' TextWindow.Write(BGx1)
' TextWindow.Write(" ")
' TextWindow.Write(BGy1)
' TextWindow.Write(" ")
' TextWindow.Write(BGx2)
' TextWindow.Write(" ")
' TextWindow.Write(BGy2)
' TextWindow.WriteLine(" ")
endif
endsub

Sub LoadBG
If (ts=1) then
Shapes.Remove(TT)

' BGc=0
' For BGy=BGy1 To BGy2
' For BGx=BGx1 To BGx2
' BGc=BGc+1
' GraphicsWindow.SetPixel(BGx,BGy,BG_Array[BGc])
' EndFor
' endfor
' TextWindow.Write("LoadBG")
' TextWindow.Write(BGx1)
' TextWindow.Write(" ")
' TextWindow.Write(BGy1)
' TextWindow.Write(" ")
' TextWindow.Write(BGx2)
' TextWindow.Write(" ")
' TextWindow.Write(BGy2)
' TextWindow.WriteLine(" ")
endif
EndSub

SmallBasic Turtle Logo Source Code 2

The bit about Show/Hide Turtle was very time consuming. I do wish Small Basic has better Screen Buffer capabilities.










Turtle Logo in Small Basic
'by Harry Hardjono
'Turtle angle speed x1 y1 x0 y0
'Turtle move moveto penup pendown
'Turtle turn turnleft turnright
'Turtle show hide
'
' I did this in one afternoon. Could've been faster if I ignore Show/Hide Turtle

GraphicsWindow.Show()
GraphicsWindow.Clear()


beginloop:
GraphicsWindow.Clear()
cmd1="init"
Toitle()
Program.Delay(2000)
cmd1="hide"
Toitle()

For loop=1 to 50
cmd1="penup"
Toitle()
cmd1="moveto"
cmd2=Math.GetRandomNumber(sx)
cmd3=Math.GetRandomNumber(sy)
Toitle()
cmd1="pendown"
Toitle()
cmd1="color"
cmd2=GraphicsWindow.GetRandomColor()
Toitle()
cmd1="size"
cmd2=Math.GetRandomNumber(10)
Toitle()
' Show Turtle is very slow!
' If (loop=10) Then
' cmd1="show"
' Toitle()
' ElseIf (loop=30) then
' cmd1="hide"
' Toitle()
' EndIf
For loop2=1 to Math.GetRandomNumber(6)
cmd1="turn"
cmd2=Math.GetRandomNumber(360)
Toitle()
cmd1="move"
cmd2=Math.GetRandomNumber(1000)
Toitle()
endfor
endfor
Program.Delay(5000)
Goto beginloop



Sub Toitle
'cmd1=command (input)
'cmd2=param1 (input)
'cmd3=param2 (input)
If (cmd1="init") then
sy=GraphicsWindow.Height
sx=GraphicsWindow.Width
'Bug: GetPixel does not take Background color
GraphicsWindow.BrushColor="white"
GraphicsWindow.FillRectangle(0,0,sx,sy)
x1=sx/2
y1=sy/2
x0=x1
y0=y1
p=1
angle=180
col="blue"
GraphicsWindow.BrushColor=col
siz=1
ts=1
SaveBG()
elseif (cmd1="penup") then
p=0
elseif (cmd1="pendown") then
p=1
elseif (cmd1="move") then
'forward
LoadBG()
x0=x1
y0=y1
x1=x0+cmd2*Math.Sin(Math.GetRadians(angle))
y1=y0+cmd2*Math.Cos(Math.GetRadians(angle))
draw()
SaveBG()
elseif (cmd1="moveto") then
LoadBG()
x0=x1
y0=y1
x1=cmd2
y1=cmd3
draw()
SaveBG()
elseif (cmd1="turn") then
LoadBG()
angle=cmd2
SaveBG()
elseif (cmd1="turnleft") then
LoadBG()
angle=angle+cmd2
SaveBG()
elseif (cmd1="turnright") then
LoadBG()
angle=angle-cmd2
SaveBG()
elseif (cmd1="color") then
col=cmd2
elseif (cmd1="size") then
siz=math.Max(1,cmd2)
siz=math.Min(10,siz)
elseif (cmd1="show") then
if (ts<>1) then
ts=1
SaveBG()
endif
elseif (cmd1="hide") then
LoadBG()
ts=0
endif

'These really need to be Mod
'Seems buggy - doesn't work with negative number?
' RemBugFix2()
'x1=Math.Remainder(x1,sx)
'y1=Math.Remainder(y1,sy)
'angle=Math.Remainder(angle,360)

If (ts=1) then
DrawTurtle()
endif

endsub

Sub DrawTurtle
tx1=x1+10*Math.Sin(Math.GetRadians(angle))
ty1=y1+10*Math.Cos(Math.GetRadians(angle))
tx2=x1+10*Math.Sin(Math.GetRadians(angle+140))
ty2=y1+10*Math.Cos(Math.GetRadians(angle+140))
tx3=x1+10*Math.Sin(Math.GetRadians(angle+220))
ty3=y1+10*Math.Cos(Math.GetRadians(angle+220))
GraphicsWindow.FillTriangle(tx1,ty1,tx2,ty2,tx3,ty3)
endsub


Sub RemBugFix
While (px<0) px=px+sx EndWhile While (py<0) py=py+sy EndWhile endsub Sub RemBugFix2 While (x1<0) x1=x1+sx EndWhile While (y1<0) y1=y1+sy EndWhile While (angle<0) angle=angle+360 EndWhile endsub 'Draw Sub Draw If (p>0) then
If (x0<>x1 or y0<>y1) then
m=math.Max(math.Abs(x1-x0),math.Abs(y1-y0))
mx1=0
mx3=m
For i=0 To m
my1=x0
my3=x1
mx2=i
map()
px=my2
my1=y0
my3=y1
mx2=i
map()
py=my2
RemBugFix()
px=Math.Remainder(px,sx)
py=Math.Remainder(py,sy)
If (siz>1) then
GraphicsWindow.BrushColor=col
GraphicsWindow.FillEllipse(px,py,siz,siz)
else
GraphicsWindow.SetPixel(px,py,col)
endif
EndFor
endif
endif

'These really need to be Mod
'Seems buggy - doesn't work with negative number?
RemBugFix2()
x1=Math.Remainder(x1,sx)
y1=Math.Remainder(y1,sy)
angle=Math.Remainder(angle,360)

endsub

'map function
Sub map
'x1-x2-x3 y1-y2-y3
'(x2-x1)/(x3-x1)=(y2-y1)/(y3-y1)
'(y3-y1)*(x2-x1)/(x3-x1)=(y2-y1)
'y1+(y3-y1)*(x2-x1)/(x3-x1)=y2
my2=((my3-my1)*(mx2-mx1)/(mx3-mx1))+my1
EndSub


sub SaveBG
If (ts=1) then
BGc=0
BGx1=Math.Floor(Math.Max(0,x1-10))
BGx1=Math.Floor(Math.Min(sx,BGx1))
BGy1=Math.Floor(Math.Max(0,y1-10))
BGy1=Math.Floor(Math.Min(sy,BGy1))
BGx2=Math.Floor(Math.Max(0,x1+10))
BGx2=Math.Floor(Math.Min(sx,BGx2))
BGy2=Math.Floor(Math.Max(0,y1+10))
BGy2=Math.Floor(Math.Min(sy,BGy2))
For BGy=BGy1 To BGy2
For BGx=BGx1 To BGx2
BGc=BGc+1
BG_Array[BGc]=GraphicsWindow.GetPixel(BGx,BGy)
EndFor
endfor
' TextWindow.Write("SaveBG ")
' TextWindow.Write(BGx1)
' TextWindow.Write(" ")
' TextWindow.Write(BGy1)
' TextWindow.Write(" ")
' TextWindow.Write(BGx2)
' TextWindow.Write(" ")
' TextWindow.Write(BGy2)
' TextWindow.WriteLine(" ")
endif
endsub

Sub LoadBG
If (ts=1) then
BGc=0
For BGy=BGy1 To BGy2
For BGx=BGx1 To BGx2
BGc=BGc+1
GraphicsWindow.SetPixel(BGx,BGy,BG_Array[BGc])
EndFor
endfor
' TextWindow.Write("LoadBG")
' TextWindow.Write(BGx1)
' TextWindow.Write(" ")
' TextWindow.Write(BGy1)
' TextWindow.Write(" ")
' TextWindow.Write(BGx2)
' TextWindow.Write(" ")
' TextWindow.Write(BGy2)
' TextWindow.WriteLine(" ")
endif
EndSub