Hour of Code is coming, and I'm preparing several programs that you can do in your spare time. It may not be one hour, at least for you, but it will be one hour for me. :)
I'll be using my favorite computer development platform: Petit Computer for Nintendo DSi. I do wish that I have the latest Mk 3 version that is out in Japan now, but so far, not for US.
The first program is already done: Debugger. I'll just have to write it up. Turtle Graphic is an old stand-by, but I think I'm going to implement Travelling Salesman Problem. That's right, the NP hard problem that is the sink of so many man hours of computer science researchers. There's also a re-implementation of Quicksort both recursive version (in Basic!) and non-recursive version (Merge sort!)
I'm sure I can think of a few more fun stuff to write in one hour. Hey, it's quick and easy. What are you waiting for? Download Petit Computer DSiWare for your Nintendo game device and get programming!
A blogspot journal showcasing computer programming, personal development, and other hobbies. Featuring Raspberry Pi, Petit Computer, and miscellaneous How-To.
Showing posts with label Petit. Show all posts
Showing posts with label Petit. Show all posts
Saturday, December 6, 2014
Friday, January 10, 2014
Petit Computer Journal #33
Quick Programs
There are many programs that can be done quickly. Suppose that you only have 15 minutes to write a computer program. What do you think you would write? Let's start a quick one. The quickest program that you can write would be a simple game. Coin flips. Heads or Tail. Or in this case, A/B buttons. The program simply checks whether or not you pushed the right buttons. It's a simple one-liner.
- ?"A/B?":WAIT 180:IF BUTTON(0)==16*RND(2) THEN "YOU WIN!" ELSE "YOU LOSE!"
Yes, it's that simple! There's nothing to it, is there? Let's take it a little more and do a simple Rock-Paper-Scissor game. Let's take a look at this table(A-B) (RPS=012):
B\A R P S
R 0 1 2
P -1 0 1
S -2 -1 0
Now, consider this: Looking at the diagonals, we see that the values are the same. zero means draw. one means win. two means lose. -1, -2 means lose, win. So, how about if we shift it up and set the range to modulus 3?
- O=(2+A-B)%3:'O=OUTCOME
The range then becomes 0-2, 0=Win,1=Lose,2=Draw, and everything is peachy! The program then clearly writes itself:
- CLS:CLEAR
- DIM T$(3)
- T$(0)="ROCK"
- T$(1)="PAPER"
- T$(2)="SCISSORS"
- ?"A=ROCK,B=PAPER,X=SCISSORS"
- WAIT 180
- B=BUTTON(0)
- A=0
- IF B AND 32 THEN A=1
- IF B AND 64 THEN A=2
- ?"YOU CHOSE ";T$(A)
- B=RND(3)
- ?"I CHOOSE ";T$(B)
- O=(2+A-B)%3
- IF O==0 THEN ?"YOU WIN" ELSE IF O==1 THEN ?"YOU LOSE" ELSE ?"DRAW"
That's all there is to it. Pretty quick to do and implement.
Let's keep moving on. How about random outcome? That's important in a game. However, there's something that you can do for a purely random device, such as dice. Here's a quick implementation of the Magic 8 Ball, although I don't bother putting in too many responses.
- CLS:CLEAR
- DIM T$(6)
- T$(0)="ABSOLUTELY YES"
- T$(1)="YES"
- T$(2)="MAYBE"
- T$(3)="NO"
- T$(4)="ABSOLUTELY NOT"
- T$(5)="NO WAY JOSE!"
- ?T$(RND(6))
If the example looks lean, that's because it is. The Magic 8 Ball is quite famous. Another example, using the same structure, would be Yoda's Wisdom. Quotes of Yoda from Star Wars, presented with pictures of him is a rather entertaining program. Another one would be Bingo, or lottery, or anything random. One of my stand by program is a dice roller for playing board games.
Some games requires you to roll so many dice, and they're not all six dice, either. Let's say that you want to roll 1-4 dice, ranging from D6,D10,D20. It's a simple process using the touch screen and do it. I'm doing it quick, so there's no text. You certainly want to dress it up for practical use.
- CLS:CLEAR
- DIM D(3)
- D[0]=6
- D[1]=10
- D[2]=20
- @MAIN
- VSYNC 1:TX=TCHX:TY=TCHY
- R=0
- FOR I=0 TO FLOOR(TX/64)
- R=R+RND(D[FLOOR(TY/64)])
- NEXT I
- IF TCHST THEN LOCATE 0,0:?"YOU ROLL ";R;" "
- GOTO @MAIN
I actually implemented Snake and Ladder game in less than 10 minutes. One thing that is interesting about it, is that the program really doesn't require any user input other than rolling the dice. In that sense, all the activities that you do can be boiled down into rolling the dice. We can improve this further simply by letting the computer run it straight.
- CLS:CLEAR
- A=0:B=0
- @MAIN
- ?A,B
- A=A+RND(2):B=B+RND(2)
- IF A>100 OR B>100 THEN GOTO @END
- GOTO @MAIN
- @END
- IF A>100 THEN ?"A WINS!"
- IF B>100 THEN ?"B WINS!"
- IF A==B THEN ?"IT'S A DRAW!"
The program will resolve the race immediately. You may find it interesting to input some kind of delay in the output, so as to make it more readable as it goes.
All of these programs can be done really quick. In fact, I do these while waiting for something. Or maybe when I was in bed, trying to sleep. Most people would take a book to read in bed. I would take a Nintendo 3DS and write programs, instead.
One more quick example of the program, and this time it's useful when you're trying to do something regarding sleep: An alarm clock.
- CLS
- AH=6:'ALARM HOUR
- AM=0:'ALARM MINUTE
- LOCATE 0,2:?AH;":";AM
- @MAIN
- WAIT 60
- LOCATE 0,0:?TIME$
- CH=VAL(LEFT$(TIME$,2))
- CM=VAL(MID$(TIME$,3,2))
- IF AH==CH AND AM==CM THEN BEEP
- GOTO @MAIN
Oh, one more bonus program since not one hour has passed, yet. You know how timers have lap times? Usually, there's only one room for lap times. With this, you can have as many as you want. I'm going to limit it to just 20 for simplicity. You may modify to suit.
- CLS
- I=0:T=0
- @MAIN
- VSYNC 1:T=T+1
- IF BTRIG() THEN I=I+1
- J$=RIGHT$("00"+STR$(T%60),2)
- S$=RIGHT$("00"+STR$(FLOOR((T/60))%60),2)
- M$=RIGHT$("00"+STR$(FLOOR((T/3600))%60),2)
- LOCATE 0,I:?I,M$;":";S$;":";J$
- IF I>=20 THEN END
- GOTO @MAIN
These programs are rough, and really, for distribution, you may want to dress them up a little. However, for something that you can doodle in 10 minutes, while waiting for something, these are perfectly suited as a pleasant time waster. Did I just do 7 programs in one hour? I guess so.
Friday, January 3, 2014
Petit Computer Journal #32
Lottery Number Generator
A while ago, I wrote a simple Lottery Number Generator. It may interests you that it has these words in it: "...THEN A MIRACLE OCCURS" Of course, that was done as a joke, but in reality, choosing a bunch of random number isn't rocket science. Especially so, if the programming language provides a built-in random number generator, such as Petit Computer.
All of these talk about the biggest million jackpot ever got me fired up again, and well, I think it's time to update that program. This time, though, I want it to be an actual program that I can use. Speaking of which, there is more than one lottery, so I decided to write a customizable lottery number generator.
- CLS:CLEAR
- PNLTYPE "OFF"
- DIM MN[10]
- DIM MB[10]
- @INIT
- NT=15:'NUMBER OF TICKETS
- NS=2:'NUMBER OF SLOTS
- CS=0:'CURRENT SLOT
- MN[0]=75:'MAXNUM 1-75
- MB[0]=5:'NUMBER OF BALLS
- MN[1]=15
- MB[1]=1
- SE=0:'SELECTOR
- SSL=0:'SAVE SLOT
- DIR=0:'GOSUB @MSL
It's clear that I'm planning to have more than one slot. The old version only has one slot, and this one have more than one. I put in initial value for the most common configuration. Also notice that you can have more than one slot for saving. I do it via MEM$ for saving/loading.
- @LOOP
- VSYNC 1:B=BUTTON(1):TS=TCHST:TT=TCHTIME
- IF TS THEN GOTO @ROLL
- IF B AND 1 THEN SE=SE-1
- IF B AND 2 THEN SE=SE+1
- IF B AND 4 THEN DIR=-1:ON SE GOSUB @MNT,@MNS,@MCS,@MMN,@MMB,@MSL
- IF B AND 8 THEN DIR=1:ON SE GOSUB @MNT,@MNS,@MCS,@MMN,@MMB,@MSL
- IF B AND 64 THEN GOSUB @SAVER
- IF B AND 128 THEN GOSUB @LOADER
That's all for the user interface. There are 6 different actions you can take, and U/D refers to the different action. L/R refers to the value being changed. In truth, there's a simpler 2 dimensional way that you can do this, but I decided to stick with this generic form for now.
- COLOR 15
- PNLSTR 0,0,"LOTTERY GENERATOR"
- PNLSTR 0,1,"(C) 2013 HARRY M. HARDJONO"
- PNLSTR 0,3," NUMBER OF TICKETS: "+STR$(NT)+" "
- PNLSTR 0,4," NUMBER OF SLOTS: "+STR$(NS)+" "
- PNLSTR 0,5," CURRENT SLOT: "+STR$(CS)+" "
- PNLSTR 0,6," MAXNUM 1-"+STR$(MN[CS])+" "
- PNLSTR 0,7," MAXBALL 1-"+STR$(MB[CS])+" "
- PNLSTR 0,8," SAVE NAME:"+MM$+" "
- PNLSTR 0,20," TOUCH SCREEN TO ROLL"
- PNLSTR 0,21,"(X) TO SAVE (Y) TO LOAD"
- PNLSTR 0,23,MID$(MEM$,0,32)
- SE=(SE+6)%6
- PNLSTR 0,SE+3,">"
- GOTO @LOOP
And that's about it for the User Interface. There's really nothing new here. It's just displaying some variables, and that's about it.
- @MNT
- NT=(NT+DIR+21)%21
- RETURN
- @MNS
- NS=(NS+DIR+10)%10
- RETURN
- @MCS
- CS=(CS+DIR+NS)%NS
- RETURN
- @MMN
- MN[CS]=(MN[CS]+DIR+100)%100
- RETURN
- @MMB
- MB[CS]=(MB[CS]+DIR+10)%10
- RETURN
- @MSL
- SSL=(SSL+DIR+10)%10
- MM$="L"+STR$(SSL)+"TTOCFG"
- RETURN
As you can see, the structure is all the same. Therefore, I can very well use a 2 dimensional array to do the whole thing. Doing it this way is actually more complicated, although it does have the benefit of mindless coding. Ha! Mindless coding. Isn't that an oxymoron? Haha, joking aside, it's true that simply adhering to plan is on the low end of the difficulty scale.
- @ROLL
- CLS
- FOR I=0 TO NT-1
- COLOR 15
- ?RIGHT$(" "+STR$(I),2);
- FOR J=0 TO NS-1
- CNL$=""
- IF MB[J]<1 THEN MB[J]=1
- FOR K=0 TO MB[J]-1
- IF MN[J]<1 THEN MN[J]=1
- @THROW
- NUM$=" "+RIGHT$("00"+STR$(RND(MN[J])+1),2)
- IF INSTR(CNL$,NUM$)>=0 THEN GOTO @THROW
- CNL$=CNL$+NUM$
- NEXT K
- COLOR 12+FLOOR((I%10)/5)
- COLOR 4+FLOOR((I%10)/5)
- ? CNL$+" -";
- NEXT J
- ?" "
- NEXT I
- GOTO @LOOP
Then a miracle occurs...
In fact, it's just a simple string search. Everytime I roll a number, the number is checked via INSTR whether or not the it exists in the string. If so, roll another number, else add the number to the string. Pretty simple, yet devious at the same time. Normally, you use an array, associative array, or hash, depending on the language you're using.
- @SAVER
- MEM$=RIGHT$("00"+STR$(NT),2
- MEM$=MRM$+RIGHT$(STR$(NS),1)
- FOR J=0 TO 8:'MAX NS
- MEM$=MEM$+RIGHT$("00"+STR$(MN[J]),2)
- MEM$=MRM$+RIGHT$(STR$(MB[J]),1)
- NEXT J
- SAVE "MEM:"+MM$
- RETURN
- @LOADER
- LOAD "MEM:"+MM$
- NT=VAL(MID$(MEM$,0,2))
- NS=VAL(MID$(MEM$,2,1))
- FOR J=0 TO 8:'MAX NS
- MN[J]=VAL(MID$(MEM$,J*3+3,2))
- MB[J]=VAL(MID$(MEM$,J*3+3+2,1))
- NEXT J
- RETURN
Saving and loading are really just building the string and decoding it. You can see the string on the last line. I made several mistakes here, mainly because I didn't write anything down, and just winging it. I build a long string array because I'm multiplying it by the number of tickets, which is silly because one ticket is the same as the other. Another one is that I multiplied J by 8. Oops. How did that happen?
I was on my bed trying to sleep, so I guess that kind of mistakes can't be helped. Now that I think of it, such mistakes are laughable silly. I guess I shouldn't be programming when I'm trying to sleep. Nah. Who cares? Some people read books on their bed. I'm doing computer programming. That's that.
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.
- INIT
- Build BEEP table
- Build Note table
- Build String table
- LOOP
- Draw Strings
- If Touch screen
- Highlight nearest string
- Play BEEP sound
- GOTO LOOP
So, do you think this will take you one hour to do? Time for another "Hour of Code" challenge!
- ACLS:CLEAR
- DIM BP[49]:'BEEP TABLE
- DIM NT$[49]:'NOTE TABLE
- DIM ST[16]:'STRING TABLE
- BREPEAT 0,60,12
- BREPEAT 1,60,12
- BREPEAT 2,60,12
- BREPEAT 3,60,12
- @INIT
- NSI=0
- P=4096/12:T$="CcDdEFfGgAaB"
- FOR I=-24 TO 24:J=I+24
- BP[J]=P*I
- NT$[J]=MID$(T$,(J%12),1)
- NEXT
- ST[0]=14:ST[1]=19
- ST[2]=21:ST[3]=22
- ST[4]=26:ST[5]=27
- ST[6]=31:ST[7]=33
- ST[8]=34:ST[9]=38
- ST[10]=39:ST[11]=43
- ST[12]=45:ST[13]=0
- ST[14]=0:ST[15]=0
- 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.
- @LOOP
- NS=13:A=256/NS:B=A/2
- VSYNC 1:BT=BTRIG():TX=FLOOR(TCHX*NS/256)
- IF NS<1 THEN NS=1
- FOR I=0 TO NS-1
- GLINE B+I*A,16,B+I*A,192,15
- LOCATE (B+I*A)/8,0:?FLOOR(ST[I]/12)
- LOCATE (B+I*A)/8,0:?NT$[ST[I]]
- NEXT
- GLINE B+TX*A,16,B+TX*A,192,8
- IF TCHTIME>0 THEN BEEP 20,BP[ST[TX]]
- 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?
- REM DIGITAL KOTO
- REM BY HARRY HARDJONO
- REM DEC 2013
- ACLS:CLEAR
- DIM BP[49]:'BEEP TABLE
- DIM NT$[49]:'NOTE TABLE
- DIM ST[16]:'STRING TABLE
- BREPEAT 0,60,12
- BREPEAT 1,60,12
- BREPEAT 2,60,12
- BREPEAT 3,60,12
- PNLTYPE "OFF"
- @INIT
- NSI=0
- CI=16:'CURRENT INSTRUMENT
- P=4096/12:T$="CcDdEFfGgAaB"
- FOR I=-24 TO 24:J=I+24
- BP[J]=P*I
- NT$[J]=MID$(T$,(J%12),1)
- NEXT
- ST[0]=14:ST[1]=19
- ST[2]=21:ST[3]=22
- ST[4]=26:ST[5]=27
- ST[6]=31:ST[7]=33
- ST[8]=34:ST[9]=38
- ST[10]=39:ST[11]=43
- ST[12]=45:ST[13]=0
- ST[14]=0:ST[15]=0
- GPAGE 1:ACLS
- @LOOP
- NS=13:A=256/NS:B=A/2
- VSYNC 1:BT=BTRIG():TX=FLOOR(TCHX*NS/256)
- IF NS<1 THEN NS=1
- GPAGE 1,1,1
- FOR I=0 TO NS-1
- GLINE B+I*A,16,B+I*A,192,15
- LOCATE (B+I*A)/8,0:?FLOOR(ST[I]/12)
- LOCATE (B+I*A)/8,0:?NT$[ST[I]]
- NEXT
- GLINE B+TX*A,16,B+TX*A,192,8
- IF TCHTIME>0 THEN BEEP CI,BP[ST[TX]]
- IF BT AND 1 THEN ST[NSI]=ST[NSI]+1
- IF BT AND 2 THEN ST[NSI]=ST[NSI]-1
- IF BT AND 4 THEN NSI=NSI-1
- IF BT AND 8 THEN NSI=NSI+1
- IF BT AND 16 THEN CI=CI+1:GOSUB @DISP
- IF BT AND 32 THEN CI=CI-1:GOSUB @DISP
- NSI=(NSI+NS)%NS
- ST[NSI]=(ST[NSI]+49)%49
- CI=(CI+70)%70
- GOTO @LOOP
- @DISP
- ACLS
- ?"INST: ";CI
- FOR I=0 TO 15
- ?I,ST[I]
- NEXT
- 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!
- REM DIGITAL KOTO
- REM BY HARRY HARDJONO
- ACLS:CLEAR:DIM BP[49]
- DIM NT$[49]:DIM ST[16]
- P=4096/12:T$="CcDdEFfGgAaB"
- FOR I=-24 TO 24:J=I+24
- BP[J]=P*I
- NT$[J]=MID$(T$,(J%12),1):NEXT
- ST[0]=14:ST[1]=19:ST[2]=21
- ST[3]=22:ST[4]=26:ST[5]=27
- ST[6]=31:ST[7]=33:ST[8]=34
- ST[9]=38:ST[10]=39:ST[11]=43
- ST[12]=45:ST[13]=0:ST[14]=0
- @LOOP
- NS=13:A=256/NS:B=A/2
- VSYNC 1:TX=FLOOR(TCHX*NS/256)
- GPAGE 1,1,1:FOR I=0 TO NS-1
- GLINE B+I*A,0,B+I*A,192,15
- NEXT
- GLINE B+TX*A,16,B+TX*A,192,8
- SD=BP[ST[TX]]
- IF TCHTIME>0 THEN BEEP 16,SD
- GOTO @LOOP
Labels:
31,
challenge,
computer,
digital,
hour of code,
Journal,
koto,
music,
Petit,
screen,
single,
tutorial
Friday, December 20, 2013
Petit Computer Journal #30
Programming Turtle Graphic
It somewhat annoys me when people are teaching turtle graphic as the basis of learning computer programming. It's not that turtle graphic is bad, as I do believe that the thought process does indeed teach programming. It is that the turtle is presented as a module. A black box, if you will.
I think it's more appropriate, especially for computer programming students to learn how simple and easy it can be to write their own turtle module. Therefore, I propose that instead of turtle graphic module, it should be a turtle graphic routines. Instead of a mysterious black box, it should be a transparent wrapper.
Done as a function call, especially interested students can take a peek into the function and see exactly how easy it can be to write a useful program such as turtle graphic.
In fact, I believe that it can be done in one hour. Turtle Graphic algorithm as an "Hour of Code" challenge? Bring it on!
First, what is involved in a turtle graphic program? Well, there's turtle and there's graphic. What are the commands necessary? Forward, Left, Right, and Pen. Each of those commands are followed by a number. We'll call that Number variable. Well, well. There are only 4 commands, are they? Would you believe that they are doable in about one hour? Why not?
If you are familiar with Object Oriented methodology, those are called "methods". What about variables? There's the position of the turtle, X0 and Y0. There's the destination of the turtle, X1 and Y1. There's Angle and Distance. And there's Pen Color. Is that 8 variables total? Are you saying coding these 8 variables into 4 commands is a great challenge? I don't think so!
Of course, we're skipping a lot of things. Namely, the LOGO language. LOGO is a programming language and if you are learning LOGO, you are learning computer programming language. The first turtle graphic and LOGO make effective combination. Most people incorrectly assume that turtle graphic must involve LOGO somewhat. Yet, Turtle graphic is just the graphical component of it. As for the language, we'll use Petit Computer Smile Basic.
That's all there is to it. So, let's set the timer and begin. 1 hour is all that takes. For simplicity, I will assume that commands are given in Strings and consist of 3 characters: A letter command, followed by 2 digit number. If the string has more than 3 characters, then the program will extract the first three characters only. If the string is less than 3 characters, it will be ignored. Also, Color 0 means Pen Up, which means moving the turtle without drawing the line. And perhaps, we should expand the language so we can do GPAINT command for colorful final image. With that in mind, let's begin!
In 15 minutes, I got the initial framework already.
- ACLS:CLEAR
- @INIT
- C$="":'COMMAND STRING
- N=0:'COMMAND PARAMETER
- X0=128:Y0=96:'TURTLE POSITION
- X1=0:Y1=0:'TURTLE DESTINATION
- A=0:'ANGLE
- D=0:'DISTANCE
- C=0:'PEN COLOR
- SPSET 1,139,0,0,0,0:SPOFS 1,X0,Y0
- @MAIN
- 'CLASSIC RECTANGLE
- C$="F35R90F35R90F35R90F35R90"
- GOSUB @TURTLE
- WAIT 300
- @END
- BEEP 6
- END
- @TURTLE
- IF LEN(C$)<3 THEN RETURN
- TC$=LEFT$(C$,3)
- N=VAL(RIGHT$(TC$,2))
- TC$=LEFT$(C$,1)
- C$=RIGHT$(C$,LEN(C$)-3)
- ?TC$;" ",N;" ":WAIT 60
- GOTO @TURTLE
If you look at the code, there is nothing much there. It's simply the structure to pass on the command for the turtle. We can use IF-THEN ladder structure, or we can use ON-GOTO command structure. Depending on TC$, which contains the actual command. We can actually skip TC$, and specify LEFT$(C$,1) instead, but using that extra variable clarifies things for now.
I decided to do the easy thing first and implement Paint and Color command. Change C$ to "C01P00C02P00C03P00C04P00" for now. Also, we'll use ON-GOTO. This requires a numeric variable, so we'll do this
- TN=INSTR("FLRCP",TC$)
- ?TC$,TN,N:WAIT 60
- ON TN GOTO @TTF,@TTL,@TTR,@TTC,@TTP
- GOTO @TURTLE
- @TTF
- GOTO @TURTLE
- @TTL
- GOTO @TURTLE
- @TTR
- GOTO @TURTLE
- @TTC
- C=N:GCOLOR C
- GOTO @TURTLE
- @TTP
- IF N==0 THEN GPAINT X0,Y0,C ELSE GPAINT X0,Y0,N
- GOTO @TURTLE
Wow, that was easy! Of course, the hard part is coming up. Specifically, we're going to talk a bit about polar coordinates. We need it to draw the turtle. But first, we're going to just set the angle. That's not too difficult. All we have to do is adjust the angle variable just like we did with color. About the only thing that we have to do is to convert the angle to radians.
A bit of caution here. Mostly, we can just to "L180" to do a turn-about movement. However, since we decided to limit the numeric portion to just 2 digits, the most we can do is quarter turn. If we want to have turn-about the turtle, then we have to do it twice, with "L90L90" command.
Another thing. We want to rotate the turtle to the appropriate angle. So far, all we did was display the sprite. Now we want to set the angle and have the turtle facing in the right direction. So, there are two steps. First, we want to convert degrees to radians, and second, we want to orient the sprite accordingly.
There is a complication here. That is, computers have 0,0 coordinate on the top-left corner of the screen, unlike math with their 0,0 coordinate on the bottom-left of the screen. We'll have to fudge the math a bit, and the best way to do it is to just experiment. So, we're going to set up a new subroutine @ROTATE which will rotate the sprites according to angles. That is, given the value of A in degree, set the direction accordingly. All we have to do then, is decide which values goes which direction. I think 0 degree should point up. With that in mind, do this real quick:
- @TEST
- FOR A=0 to 360 STEP 360/12
- GOSUB @ROTATE:WAIT 60
- NEXT
- @ROTATE
- A=A%360
- SPANGLE 1,A,6
- RETURN
We discovered a weakness in our program in that the rotation is not centered on the sprite. We fix it by adding this line on the @INIT subroutine.
SPHOME 1,8,8
Then we simply add these lines to the LEFT and RIGHT portion of the code:
- @TTL
- A=A-N:'TURN LEFT
- GOSUB @ROTATE
- @TTR
- A=A+N:'TURN RIGHT
- GOSUB @ROTATE
And there you go! Just one more function and that's all there is to it! Who says turtle graphic programming is hard? Haha, joking aside, we have been delaying the inevitable. Not only we have to convert degree to radians, but we also have to make sure that the turtle is facing the right way. Fortunately, there are functions RAD(),SIN() and COS() that makes it simple. There is something else that we have to worry about, but we'll defer that until later. For now, let's draw!
- @TEST
- FOR A=0 to 360 STEP 360/12
- SPOFS 1,128,96:GOSUB @ROTATE
- X1=50*SIN(RAD(A)):'RSINTHETA
- Y1=50*COS(RAD(A)):'RCOSTHETA
- SPOFS 1,128+X1,96+Y1,50:'50 IS THE DISTANCE
- WAIT 60
- NEXT
If you run this code, you will see that the whole thing breaks up. You see that the sprite and the movement do not match. We want to see the sprites to move in a clock-wise pattern. The problem now is how to fix it? Remember that the coordinate is centered at top-left? That's the problem! To the math savvy among you, it may interest you to come up with the solution yourself. Otherwise, here's the fix!
Y1=-50*COS(RAD(A)):'RCOSTHETA
Yes, it's that easy! Simply turn R into negative R, and that's all there is to it. Really. Now, all we have to do is do the @TTF subroutine and we'll be done!
- @TTF
- D=N
- X1=D*SIN(RAD(A))
- Y1=(-1*D)*COS(RAD(A))
- SPOFS 1,X0+X1,Y0+Y1,D
- @TTF1
- SPREAD(1),TTFX,TTFY:'GOSUB @WRAP
- IF C!=0 THEN GPSET TTFX,TTFY,C
- IF SPCHK(1)!=0 GOTO @TTF1
- X0=X0+X1:Y0=Y0+Y1
And that's it! You're probably wondering why we're doing interval D on SPOFS. The reason for it is so that we can retrieve sprite coordinates TTFX,TTFY along the way, and plot the points with color C. That's a pretty neat trick. Otherwise, we'd have to mess around with line drawing algorithm. Not that coming up with line drawing algorithm is bad, just that it's an unnecessary complexity if all that we want is a simple subroutine.
Oh, one more thing: Suppose we want to do a wrap around? What should we do? Well, I don't like to mess with the current code, but basically, what we want to do is adjust the sprite location and update the target accordingly. So, we need to check whether TTFX or TTFY is below zero and if so, shift everything by the screenful. Trivial, had we did our own line drawing algorithm. Not so much if we're dependent upon internal routines, just like we did.
What exactly do we need to implement coordinate wrapping, anyway? Well, we need the start and stop coordinates, which are X0,y0 and X1,Y1. We also need a coordinate that specifies the current location going there. And we want to be able to reset the coordinates from the sprites.
So, we can do a bunch of calculation, but basically, we want to handle these 2 lines:
SPOFS 1,NEWX,NEWY
SPOFS 1,ADJX,ADJY,TIMELEFT
So, we need 5 variables to be set. To the math savvy among you, I suggest that you work it out yourself.
Time left is easy. We have already have D. For the new D, all we have to do is calculate the distance of the current position with the target coordinate. TIMELEFT=SQR((X1-TTFX)^2+(Y1-TTFY)^2) The old distance formula standby. NEWX and NEWY is simply OLDX=OLDX+256 and OLDY=OLDy+192. Basically, screen width and height. Same thing with ADJX and ADJY.
There's nothing to it, is there? We need to add WRAP=1 and replace the commented 'GOSUB @WRAP with IF WRAP THEN GOSUB @WRAP.
Before we do a bunch of complicated math, though, I'll point out to you something very interesting. The fact that there is only one GPSET makes it easy. If we don't care about drawing the point where the turtle is, and just worry about wrap, we can easily modify the line to this:
- IF C!=0 THEN GPSET ((TTFX+256)%256),((TTFY+192)%192),C
- IF SPCHK(1)!=0 GOTO @TTF1
- X0=((X0+X1+256)%256):Y0=((Y0+Y1+192)%192)
- SPOFS 1,XO,YO
That's something to think about. Do we want to write some complicated routines or do we just want to get the job done? With that in mind, instead of doing a bunch of mathematics, we can simply create a secondary sprite and put it into the current point set whenever the original turtle is out of bound! Simple, yet effective!
- @WRAP
- SPOFS 2,((TTFX+256)%256),((TTFY+192)%192)
- SPANGLE 2,A,0
- RETURN
And that's all there is to it! We can easily implement this within one hour. With wrap function, we're pushing it into the hour, depending on how fast you can type with the stylus. Regardless, we're only typing less than 100 lines program, with testing subroutine. What's the test? Well, remember that we're limiting the data to only 2 digits? That helps when we're doing wrapping test cases. But how about when we want to do large drawings? The test subroutine is used throughout the development process in order to try out subroutines outside the main program.
I commented out the test subroutine in the final program, but it's a good example on how you can use it in your program, so I'm leaving it there. The total time that it takes to do this program is:
15 min Initial Framework
10 min C and P command
10 min Rotation
15 min TTF drawing
10 min Wrap option
Total Development time? 1 hour, including screen wrap function. Now that's what I call "Hour of Code"!
- REM TURTLE GRAPHIC
- REM BY HARRY HARDJONO
- REM DEC 2013
- ACLS:CLEAR
- @INIT
- WRAP=1
- C$="":'COMMAND STRING
- N=0:'COMMAND PARAMETER
- X0=128:Y0=96:'TURTLE POSITION
- X1=0:Y1=0:'TURTLE DESTINATION
- A=0:'ANGLE
- D=0:'DISTANCE
- C=0:'PEN COLOR
- SPSET 1,139,0,0,0,0:SPOFS 1,X0,Y0
- SPHOME 1,8,8
- SPSET 2,139,0,0,0,0:SPOFS 1,-255,-255
- SPHOME 2,8,8
- @MAIN
- 'GOSUB @TEST
- 'CLASSIC RECTANGLE
- FOR I=1 TO 8
- C$="F35R90F35R90F35R90F35R90"
- GOSUB @TURTLE
- NEXT
- @END
- BEEP 6:WAIT 600
- END
- @TURTLE
- IF LEN(C$)<3 THEN RETURN
- TC$=LEFT$(C$,3)
- N=VAL(RIGHT$(TC$,2))
- TC$=LEFT$(C$,1)
- C$=RIGHT$(C$,LEN(C$)-3)
- TN=INSTR("FLRCP",TC$)
- ON TN GOTO @TTF,@TTL,@TTR,@TTC,@TTP
- GOTO @TURTLE
- @TTF
- D=N
- X1=D*SIN(RAD(A))
- Y1=(-1*D)*COS(RAD(A))
- SPOFS 1,X0+X1,Y0+Y1,D
- @TTF1
- SPREAD(1),TTFX,TTFY:IF WRAP THEN GOSUB @WRAP
- IF C!=0 AND WRAP==1 THEN GPSET ((TTFX+256)%256),((TTFY+192)%102),C
- IF C!=0 AND WRAP==0 THEN GPSET TTFX,TTFY,C
- IF SPCHK(1)!=0 GOTO @TTF1
- IF WRAP==1 THEN X0=((X0+X1+256)%256:Y0=((Y0+Y1+192)%192):SPOFS1,X0,Y0
- IF WRAP==0 THEN X0=X0+X1:Y0=Y0+Y1
- GOTO @TURTLE
- @TTL
- A=A-N:'TURN LEFT
- GOSUB @ROTATE
- GOTO @TURTLE
- @TTR
- A=A+N:'TURN RIGHT
- GOSUB @ROTATE
- GOTO @TURTLE
- @TTC
- C=N:GCOLOR C
- GOTO @TURTLE
- @TTP
- IF N==0 THEN GPAINT X0,Y0,C ELSE GPAINT X0,Y0,N
- GOTO @TURTLE
- @TEST
- WRAP=1:'0 OR 1
- FOR SP=0 TO 150 STEP 3
- V=SP:LOCATE 0,0:?SP
- @LINER
- IF V>99 THEN C$="F99":GOSUB @TURTLE:V=V-99:GOTO @LINER
- L1$=RIGHT$(("000"+STR$(V)),2)
- C$="F"+L1$+"R70C"+L1$:GOSUB @TURTLE
- NEXT
- RETURN
- @ROTATE
- IF A<0 THEN A=A+360:GOTO @ROTATE
- A=A%360:SPANGLE 1,A,6
- RETURN
- @WRAP
- SPOFS 2,((TTFX+256)%256),((TTFY+192)%192)
- SPANGLE 2,A,0
- RETURN
Labels:
100 lines,
30,
algorithm,
challenge,
code,
computer,
coordinates,
degree,
graphic,
hour of code,
Journal,
LOGO,
Petit,
polar,
programming,
radian,
source,
turtle
Friday, December 13, 2013
Petit Computer Journal #29
A Simple Sprite Game
I've always said that writing a computer program is pretty easy if you know what you're doing. The question is, how easy can it be? I've written a version of Snakes and Ladders game in under 10 minutes. How quickly and easily can I write a sprite based game? Certainly, not too difficult.
I'm curious as to just how easy it is to write one. Certainly, there is the control. I settled for Up-Down-Fire, which is a variation of Left-Right-Fire. I was hoping for a single screen challenge program, and I managed to do it. However, it isn't fun at all. Also, it turns out that I don't need the Up-Down movement at all. Just sit there and hit the fire button!
It's all very well and good for a single screen challenge, but I was curious as to how much better it would be, had it been a better fleshed out game. So, forget about the single screen challenge and just do better!
So far, I'm using standard graphics. It's very convenient that the graphics are all built-in. However, there is always a way to have custom made graphic. I won't list the source code, but check out the QR made for this game.
http://petitcomputer.wikia.com/wiki/Santa_Helper
I've always said that writing a computer program is pretty easy if you know what you're doing. The question is, how easy can it be? I've written a version of Snakes and Ladders game in under 10 minutes. How quickly and easily can I write a sprite based game? Certainly, not too difficult.
I'm curious as to just how easy it is to write one. Certainly, there is the control. I settled for Up-Down-Fire, which is a variation of Left-Right-Fire. I was hoping for a single screen challenge program, and I managed to do it. However, it isn't fun at all. Also, it turns out that I don't need the Up-Down movement at all. Just sit there and hit the fire button!
- CLS:Y=0:A=256:C=128:S=0
- SPSET 1,96,0,0,0,0:T=3600
- SPSET 2,40,0,0,0,0
- SPOFS 2,-16,0,0:SPANIM 2,2,15
- SPSET 10,13,3,0,0,0
- @LOOP
- T=T-1:IF T<0 THEN END
- VSYNC 1:B=BUTTON(0):DX=0:DY=0
- LOCATE 0,0:?"S=";S,"T=";T;" "
- IF SPHIT(10) THEN S=S+1:BEEP
- IF !SPCHK(10) THEN GOSUB @MM
- IF (B AND 16) THEN GOSUB @FR
- IF !(B AND 3) GOTO @LOOP
- DY=(B AND 3)*2-3
- Y=Y+DY:IF Y<0 THEN Y=Y+192
- Y=Y%192:SPOFS 1,0,Y,0
- GOTO @LOOP
- @FR
- SPOFS 2,0,Y,0:SPOFS 2,A,Y,C/2
- RETURN
- @MM
- SPOFS 10,99+RND(99),-20,0
- SPOFS 10,99+RND(99),2*C,C
- RETURN
It's all very well and good for a single screen challenge, but I was curious as to how much better it would be, had it been a better fleshed out game. So, forget about the single screen challenge and just do better!
- CLS:Y=0:A=256:C=128:S=0
- BGFILL 0,0,0,63,63,9,8,0,0
- FOR I=1 TO 32
- BGPUT 0,RND(64),RND(64),32,8,0,0
- NEXT
- SPSET 1,96,0,0,0,0
- SPSET 2,40,0,0,0,0
- SPOFS 2,-16,0,0:SPANIM 2,2,15
- FOR I=10 TO 70
- SPSET I,13,3,0,0,0
- SPOFS I,99,-50,0
- NEXT
- @INIT
- BGMPLAY 23
- T=10800:S=0
- @LOOP
- IF HYC<S THEN HC=S
- IF !BGCHK(0) THEN BGOFS 0,RND(512),RND(512),360
- T=T-1:M=FLOOR(T/180):IF T<0 THEN GOTO @END
- VSYNC 1:B=BUTTON(0):DX=0:DY=0
- LOCATE 0,0:?"$=";S,"TIME=";M;" ","HI $=";HC;" "
- FOR I=10 TO 70-M
- IF SPHITSP(I,2) THEN S=S+1:BEEP 3:GOSUB @HIT
- IF !SPCHK(I) THEN GOSUB @MM
- NEXT
- IF (B AND 16) THEN GOSUB @FR
- IF !(B AND 3) GOTO @LOOP
- DY=(B AND 3)*2-3
- Y=Y+DY:IF Y<0 THEN Y=Y+192
- Y=Y%192:SPOFS 1,0,Y,0
- GOTO @LOOP
- @FR
- SPOFS 2,0,Y,0:SPOFS 2,A,Y,C/2
- RETURN
- @MM
- SPCHR I,13
- SPOFS I,99+RND(99),-20+256*RND(2),0
- SPOFS I,99+RND(99),-20+256*RND(2),99+RND(C)
- RETURN
- @HIT
- SPCHR I,248
- SPANIM I,8,6,1
- RETURN
- @END
- BGMPLAY 6
- WAIT 360
- FOR I=0 TO 16:I=BUTTON(0):NEXT
- GOTO @INIT
So far, I'm using standard graphics. It's very convenient that the graphics are all built-in. However, there is always a way to have custom made graphic. I won't list the source code, but check out the QR made for this game.
http://petitcomputer.wikia.com/wiki/Santa_Helper
Friday, December 6, 2013
Petit Computer Journal #28
Petit BrainF
Yeah, I was wondering just exactly how hard it is to implement a language that has only 8 operators involved. Not hard at all! Of course, I cheated and used graphic screens for my memory. It'll be probably a bit more complicated if I use straight memory. A bit.
The program cannot fit into one screen, but it's certainly a candidate for less than 100 lines program. It took me a couple hours because I was careless in moving the pointers.
The CYCLE subroutine is there to keep the variables within range, and the FB subroutine is there as a debugger that I use when debugging the program. So, I commented it out in the final version.
Another person, IAmAPersson (what a name!) was working on a version of the compiler as well. Check Petit Wikia for it. Maybe he posted it there by now.
I don't know the policy of this blogspot, but I know my policy and that is family friendly. I don't want to use the full name of the program. Thank you. Next time you designed a programming language, make sure you name it proper!
Yeah, I was wondering just exactly how hard it is to implement a language that has only 8 operators involved. Not hard at all! Of course, I cheated and used graphic screens for my memory. It'll be probably a bit more complicated if I use straight memory. A bit.
The program cannot fit into one screen, but it's certainly a candidate for less than 100 lines program. It took me a couple hours because I was careless in moving the pointers.
The CYCLE subroutine is there to keep the variables within range, and the FB subroutine is there as a debugger that I use when debugging the program. So, I commented it out in the final version.
Another person, IAmAPersson (what a name!) was working on a version of the compiler as well. Check Petit Wikia for it. Maybe he posted it there by now.
I don't know the policy of this blogspot, but I know my policy and that is family friendly. I don't want to use the full name of the program. Thank you. Next time you designed a programming language, make sure you name it proper!
- REM PBF BY HARRY HARDJONO
- @INIT
- ACLS:CLEAR
- GPAGE 0:GCLS
- GPAGE 1:GCLS
- MS=30000
- PTR=0
- PC=0
- MPC=0
- CSR=0
- GPAGE1:RESTORE @HELLO
- @LOADP
- READ A$
- IF A$=="" GOTO @RUNP
- FOR I=0 TO LEN(A$)-1
- GPSET PC%256,PC/256,ASC(MID$(A$,I,1))
- PC=PC+1:NEXT
- GOTO @LOADP
- @RUNP
- PC=0:GPAGE 0
- @LOOP
- GPAGE 0:VAR=GSPOIT(PTR%256,PTR/256)
- GPAGE 1:C=GSPOIT(PC%256,PC/256)
- 'GOSUB @FB
- IF C==62 THEN PTR=PTR+1:GOSUB @CYCLE:GOTO @LOOP2
- IF C==60 THEN PTR=PTR-1:GOSUB @CYCLE:GOTO @LOOP2
- IF C==43 THEN VAR=VAR+1:GOSUB @CYCLE
- IF C==45 THEN VAR=VAR-1:GOSUB @CYCLE
- IF C==46 THEN LOCATE CSR%32,CSR/32:?CHR$(VAR);:CSR=CSR+1:GOTO @LOOP2
- IF C==44 THEN GOSUB @GETCHAR
- IF C==91 THEN MPC=1:GOSUB @SR:GOTO @LOOP2
- IF C==93 THEN MPC=1:GOSUB @SL:GOTO @LOOP2
- IF C==0 GOTO @END
- GPAGE 0:GPSET PTR%256,PTR/256,VAR
- @LOOP2
- PC=PC+1:GOSUB @CYCLE
- GOTO @LOOP
- @FB
- LOCATE 0,5:?PTR,PC,VAR
- LOCATE 0,6:?CHR$(C)
- WAIT 6
- RETURN
- @CYCLE
- IF PC<0 THEN PC=PC+49152
- IF PTR<0 THEN PTR=PTR+MS
- IF VAR<0 THEN VAR=VAR+256
- PC=PC%49152
- PTR=PTR%MS
- VAR=VAR%256
- CSR=CSR%768
- RETURN
- @GETCHAR
- A$=INKEY$():IF A$=="" GOTO @GETCHAR
- VAR=ASC(A$):WAIT 30
- RETURN
- @SR
- IF VAR!=0 THEN RETURN
- PC=PC+1:GOSUB @CYCLE
- GPAGE 1:C=GSPOIT(PC%256,PC/256)
- IF C==91 THEN MPC=MPC+1
- IF C==93 THEN MPC=MPC-1
- IF MPC<=0 THEN RETURN
- GOTO @SR
- @SL
- IF VAR==0 THEN RETURN
- PC=PC+1:GOSUB @CYCLE
- GPAGE 1:C=GSPOIT(PC%256,PC/256)
- IF C==91 THEN MPC=MPC-1
- IF C==93 THEN MPC=MPC+1
- IF MPC<=0 THEN RETURN
- GOTO @SL
- @END
- WAIT 600
- END
- @PLOP
- DATA "-[>-[.-]<]"
- DATA ">>++++[.->++++++[.-]<]"
- DATA ""
- @CAT
- DATA ",[.,]"
- DATA ""
- @HELLO
- DATA "+++++ +++++ [>+++++++"
- DATA ">+++++ +++++ >+++ >+ <<<<-]"
- DATA ">++.>+.+++++++ ..+++.>++.<<"
- DATA "+++++ +++++ +++++"
- DATA ".>.+++. ------ .----- --- .>+.>."
- DATA ""
Friday, November 29, 2013
Petit Computer Journal #27
Boolean Operator Division
Have you ever wondered just how difficult it is to do division using nothing but NOT,AND,OR,XOR? Well, so do I. It turns out I also need ADD,SUB, and CMP. Also, Shift command. Still, these are easily within the domain of any assembly language programming.
A contains the number to be divided. B contains the number that divides. This computes C=A/B and A=A%B. A pretty nifty code that fits single screen challenge. Notice, that you can also do it this way (untested) on line 6:
This way, due to the calculation in Assembly language where the register holds the resulting value of the calculation.
Have you ever wondered just how difficult it is to do division using nothing but NOT,AND,OR,XOR? Well, so do I. It turns out I also need ADD,SUB, and CMP. Also, Shift command. Still, these are easily within the domain of any assembly language programming.
- CLS
- A=RND(255):B=RND(15)
- ?A,B
- C=0:D=128:E=B*D
- FOR I=1 TO 8
- IF A-E>=0 THEN A=A-E:C=C+D
- ?A,B,C,D,E
- D=D/2:E=E/2
- NEXT I
- ?C,A
- FOR I=0 TO 8
- VSYNC 1:I=BTRIG():NEXT
- GOTO @DIV2
A contains the number to be divided. B contains the number that divides. This computes C=A/B and A=A%B. A pretty nifty code that fits single screen challenge. Notice, that you can also do it this way (untested) on line 6:
- A=A-E:IF A<0 THEN A=A+E ELSE C=C+D
This way, due to the calculation in Assembly language where the register holds the resulting value of the calculation.
Friday, November 22, 2013
Petit Computer Journal #26
A Simple Interactive Story Reader
I am currently busy with Nanowrimo, and in the story, there is a program that lets student write their own interactive stories. Even knowing how little it takes to do it, I'm rather surprised at the economy of it. Here is the program in its entirety:
I am currently busy with Nanowrimo, and in the story, there is a program that lets student write their own interactive stories. Even knowing how little it takes to do it, I'm rather surprised at the economy of it. Here is the program in its entirety:
- REM INIT
- CLEAR:TITLE$=""
- DIM TEXT$[10]
- DIM PAGE$[4]
- DIM MENU$[4]
- CH=0:P$="INTRO"
- @SHOWPAGE
- RESTORE P$
- READ TITLE$
- READ TEXT$[0]:READ TEXT$[1]
- READ TEXT$[2]:READ TEXT$[3]
- READ TEXT$[4]:READ TEXT$[5]
- READ TEXT$[6]:READ TEXT$[7]
- READ TEXT$[8]:READ TEXT$[9]
- READ PAGE$[0]:READ MENU$[0]
- READ PAGE$[1]:READ MENU$[1]
- READ PAGE$[2]:READ MENU$[2]
- READ PAGE$[3]:READ MENU$[3]
- REM DISPLAY
- CLS:?TITLE$:?
- ?TEXT$[0]:?TEXT$[1]
- ?TEXT$[2]:?TEXT$[3]
- ?TEXT$[4]:?TEXT$[5]
- ?TEXT$[6]:?TEXT$[7]
- ?TEXT$[8]:?TEXT$[9]
- ?"1",MENU$[0]
- ?"1",MENU$[1]
- ?"1",MENU$[2]
- ?"1",MENU$[3]
- REM INPUT
- INPUT CH:CH=CH-1
- P$=PAGE$[CH]
- GOTO @SHOWPAGE
If the structure isn't clear to you, you need to add LABEL and DATA statements. Here's a sampling that I have. Notice, the DATA segments do go on, although each having the same structure.
- REM WINDELL
- @INTRO
- DATA "THE STORY OF WINDELL"
- DATA "ONCE UPON A TIME THERE WAS A"
- DATA "LITTLE RAT BY THE NAME OF"
- DATA "WINDELL."
- DATA " "
- DATA "SHE WAS HUNGRY AND IN SEARCH"
- DATA "OF CHEESE. WHERE DO YOU THINK"
- DATA "SHE SHOULD GO?"
- DATA " "
- DATA " "
- DATA " "
- DATA "KITCH","TO THE KITCHEN"
- DATA "BEDRO","TO THE BEDROOM"
- DATA "BADEND","STAY HOME"
- DATA "INTRO"," "
The DATA segments consist of a LABEL, 1 line TITLE, 10 lines TEXT, and 4 lines LABEL/CHOICE combo. Obviously there's also @KITCH, @BEDRO, and @BADEND DATA segments. There's also @GOODEND data segment, too!
The beauty of it is that the story can be long and involved. The size of the program stays the same regardless. This is a good introduction for computer programming, those of you who are just beginning. Obviously, it can be refined further. How about full screen text on top, and 8 or more choices on the bottom? Simple enough to enhance and experiment with.
Friday, November 15, 2013
Petit Computer Journal #25
Background Maker
A while back, I did a BGMaker program. That's a program that takes in graphic screen (GRP0) and turn it into background (SCU0) and background tiles (BGU0, BGU1, BGU2,BGU3). I had suspected it that it fits into one screen, and it did. Here is my entry to the single screen programming challenge:
- 'BGMAKER BY HARRY HARDJONO
- CLS:CLEAR:DIM TL$[1024]:MT=-1
- OX=0:OY=0:RX=31:RY=23:P$="G2"
- ?"GRAPHIC?":INPUT P$
- LOAD "GRP0:"+P$,FALSE
- FOR Y=0 TO RY:FOR X=0 TO RX
- X1=X*8:X2=X1+7:Y1=Y*8:Y2=Y1+7
- G$="":FOR YY=Y1 TO Y2
- FOR XX=X1 TO X2
- G$=G$+HEX$(GSPOIT(XX,YY)%16)
- NEXT:NEXT
- CT=-1:FOR I=MT TO 0 STEP -1
- IF G$==TL$[I] THEN CT=I
- NEXT
- IF CT==-1 THEN GOSUB @SETCHR
- BGPUT 0,OX+X,OY+Y,CT,0,0,0
- NEXT:NEXT:SAVE "SCU0:T0"
- FOR I=0 TO FLOOR(MT/256)
- B$="BGU"+STR$(I)
- SAVE B$+":B"+STR$(I):NEXT:END
- @SETCHR
- MT=MT+1:TL$[MT]=G$:CT=MT
- BU$="BGU"+STR$(FLOOR(MT/256))
- CHRSET BU$,MT%256,G$:RETURN
I also did a version with no memory. I'm not going to show it here because it's not only complicated, but also very, very slow. In fact, at one point, I actually put in an array to do it. That's when I decided to do a version with arrays instead.
Anyway, if you look at the code above, you'll see that it only works on single graphic screen. That's not what I want to have. I want to have the full 64 by 64 background tile generator. As you can see, from the variables OX, OY, P$, even though they do nothing in this version, the original design does take the full background field into account.
Here is the updated BGMAKER program that includes all available background screen. You'll need six graphic screens. They're all loaded into GRP0, but the build is done on different parts of BG screen.
'BGMAKER BY HARRY HARDJONO
CLS:CLEAR:DIM TL$[1024]:MT=-1
@PICFILE
DATA "G1","G2","G3","G6","G5","G4"
RESTORE @PICFILE
All this does is some initialization. I put the file names into DATA statements because I'm too lazy to modify the code. So I simply replaced all INPUT P$ into READ P$.
- 'LOAD GRPS
- OX=0:OY=0:RX=31:RY=23:READ P$
- LOAD "GRP0:"+P$,FALSE
- BGOFS 0,OX*8,OY*8,60:GOSUB @TILER
Okay, you can see the pattern here. OX, OY are relative shifting or original X,Y coordinate. RX,RY are the size or width and height of the screen. I mentioned changing INPUT P$ into READ P$. Once I loaded the graphic into screen 0, I shifted the background to the appropriate place and call the @TILER subroutine. Since OX,OY are zeros, I guess I don't need to shift the background (using BGOFS), but I'm doing it here for consistency.
- OX=32:OY=0:RX=31:RY=23:READ P$
- LOAD "GRP0:"+P$,FALSE
- BGOFS 0,OX*8,OY*8,60:GOSUB @TILER
- OX=0:OY=24:RX=31:RY=23:READ P$
- LOAD "GRP0:"+P$,FALSE
- BGOFS 0,OX*8,OY*8,60:GOSUB @TILER
- OX=32:OY=24:RX=31:RY=23:READ P$
- LOAD "GRP0:"+P$,FALSE
- BGOFS 0,OX*8,OY*8,60:GOSUB @TILER
- OX=0:OY=48:RX=31:RY=15:READ P$
- LOAD "GRP0:"+P$,FALSE
- BGOFS 0,OX*8,OY*8,60:GOSUB @TILER
- OX=32:OY=48:RX=31:RY=15:READ P$
- LOAD "GRP0:"+P$,FALSE
- BGOFS 0,OX*8,OY*8,60:GOSUB @TILER
As you can see, only the first line is being changed. I could have just put the whole thing into a subroutine. However, I'm not doing it because of the possibility that I don't have the whole 6 graphics to play with. If I only have four graphic screens, for example, it's easy for me to just comment out the necessary lines. This is judgement call, so you can certainly do it different. Whatever works.
- @SAVEBG
- SAVE "SCU0:T0"
- FOR I=0 TO FLOOR(MT/256)
- B$="BGU"+STR$(I)
- SAVE B$+":B"+STR$(I)
- NEXT
- WAIT 300
Even though this is labelled as a subroutine. This is just a piece of code. It's not being called by GOSUB. I simply put the label there because I don't need to use comment. I could have, but simply decided not to.
- FOR I=0 TO 99
- X=RND(512):Y=RND(512)
- BGOFS 0,X,Y,60:WAIT 60
- NEXT
- WAIT 300
This is simply the scrolling of the background. You see 60 at the end of BGOFS call. This is equivalent of one second. So, I scroll for one second, while waiting for one second. What happens if I don't wait? Then the scroll will be reset immediately.
- BGCLR
- CHRINIT "BGU0"
- CHRINIT "BGU1"
- CHRINIT "BGU2"
- CHRINIT "BGU3"
- GCLS
- END
This code is necessary to reset the states to the original state. Otherwise, you will see that your screen is overlaid with your background. Of course, during debugging, that's quite problematic for me, since oftentimes the program does not run to completion, so I end up doing these commands by hand.
- @TILER
- FOR Y=0 TO RY:FOR X=0 TO RX
- X1=X*8:X2=X1+7:Y1=Y*8:Y2=Y1+7
- GOSUB @TILESTR
- CT=-1:FOR I=MT TO 0 STEP -1
- IF G$==TL$[I] THEN CT=I
- NEXT
- IF CT==-1 THEN GOSUB @SETCHR
- BGPUT 0,OX+X,OY+Y,CT,0,0,0
- NEXT:NEXT
- RETURN
There's some confusion here. I should have put the X1,X2,Y1,Y2 line into @TILESTR as it serves no purpose here. TL$[I] is the 64 hexadecimal string that contains the tile info. What the program does is simply set CT (Current Tile) to -1. Whenever the tile is found, CT is set to I. If the tile is not found, then CT will contain the value of -1. Then we know to store the current string (G$) into the tile array. After that, it's trivial to just set CT into the background.
Notice that I'm not doing mirror comparison. The documentation mentioned X,Y rotation. I'm ignoring that here. Actually, those aren't X,Y rotation. Those are mirror flip. If you want to you may want to modify the code to include that check. It's easy enough to do.
- @TILESTR
- G$=""
- FOR YY=Y1 TO Y2
- FOR XX=X1 TO X2
- G$=G$+HEX$(GSPOIT(XX,YY)%16)
- NEXT:NEXT
- RETURN
I have to make a decision here. Graphic screens can handle 256 colors. BG tiles can only handle 16 colors. What do I do? Well, if you notice, BG colors can actually have 240 colors. 256 colors-16 transparent colors. You can differentiate the different palettes, but I simply decided not to.
If you want to differentiate the palettes, then you want to store FLOOR(GSPOIT(XX,YY)/16) value and enter it into G$ so that it doesn't match equivalent pattern but different palette.
Another factor to consider is the X,Y mirror. Now you see why there's X1,X2,Y1,Y2. This is done for ease of mirroring. Normal: Y1-Y2/X1-X2. X Mirror: Y1-Y2/X2-X1. Y Mirror: Y2-Y1/X1-X2. XY Mirror: Y2-Y1/X2-X1. Note that I haven't done so, so the above is not guaranteed to work. Making it work is left as homework for the readers. ;)
- @SETCHR
- MT=MT+1:IF MT>1023 THEN MT=1023
- TL$[MT]=G$:CT=MT
- BU$="BGU"+STR$(FLOOR(MT/256))
- CHRSET BU$,MT%256,G$
- RETURN
Nothing to it. This simply increment the MT (MaxTile) counter and set the character to the appropriate BGUX bank.
And there you go. A very easy to do Background Maker. Here is the whole completed source code for your convenience. I suppose I should do the QR. Well, maybe sometimes in the future, or maybe not. Easy enough for you to do it by hand.
- 'BGMAKER BY HARRY HARDJONO
- CLS:CLEAR:DIM TL$[1024]:MT=-1
- @PICFILE
- DATA "G1","G2","G3","G6","G5","G4"
- RESTORE @PICFILE
- 'LOAD GRPS
- OX=0:OY=0:RX=31:RY=23:READ P$
- LOAD "GRP0:"+P$,FALSE
- BGOFS 0,OX*8,OY*8,60:GOSUB @TILER
- OX=32:OY=0:RX=31:RY=23:READ P$
- LOAD "GRP0:"+P$,FALSE
- BGOFS 0,OX*8,OY*8,60:GOSUB @TILER
- OX=0:OY=24:RX=31:RY=23:READ P$
- LOAD "GRP0:"+P$,FALSE
- BGOFS 0,OX*8,OY*8,60:GOSUB @TILER
- OX=32:OY=24:RX=31:RY=23:READ P$
- LOAD "GRP0:"+P$,FALSE
- BGOFS 0,OX*8,OY*8,60:GOSUB @TILER
- OX=0:OY=48:RX=31:RY=15:READ P$
- LOAD "GRP0:"+P$,FALSE
- BGOFS 0,OX*8,OY*8,60:GOSUB @TILER
- OX=32:OY=48:RX=31:RY=15:READ P$
- LOAD "GRP0:"+P$,FALSE
- BGOFS 0,OX*8,OY*8,60:GOSUB @TILER
- @SAVEBG
- SAVE "SCU0:T0"
- FOR I=0 TO FLOOR(MT/256)
- B$="BGU"+STR$(I)
- SAVE B$+":B"+STR$(I)
- NEXT
- WAIT 300
- FOR I=0 TO 99
- X=RND(512):Y=RND(512)
- BGOFS 0,X,Y,60:WAIT 60
- NEXT
- WAIT 300
- BGCLR
- CHRINIT "BGU0"
- CHRINIT "BGU1"
- CHRINIT "BGU2"
- CHRINIT "BGU3"
- GCLS
- END
- @TILER
- FOR Y=0 TO RY:FOR X=0 TO RX
- X1=X*8:X2=X1+7:Y1=Y*8:Y2=Y1+7
- GOSUB @TILESTR
- CT=-1:FOR I=MT TO 0 STEP -1
- IF G$==TL$[I] THEN CT=I
- NEXT
- IF CT==-1 THEN GOSUB @SETCHR
- BGPUT 0,OX+X,OY+Y,CT,0,0,0
- NEXT:NEXT
- RETURN
- @TILESTR
- G$=""
- FOR YY=Y1 TO Y2
- FOR XX=X1 TO X2
- G$=G$+HEX$(GSPOIT(XX,YY)%16)
- NEXT:NEXT
- RETURN
- @SETCHR
- MT=MT+1:IF MT>1023 THEN MT=1023
- TL$[MT]=G$:CT=MT
- BU$="BGU"+STR$(FLOOR(MT/256))
- CHRSET BU$,MT%256,G$
- RETURN
Labels:
25,
background,
bgmaker,
challenge,
computer,
Journal,
maker,
Petit,
programming,
screen,
single
Subscribe to:
Posts (Atom)