gigatron/rom/Apps/Snake/Snake_v3.gcl
2025-01-28 19:17:01 +03:00

606 lines
16 KiB
Plaintext

{-----------------------------------------------------------------------+
| |
| Snake game |
| |
+-----------------------------------------------------------------------}
gcl0x
{-----------------------------------------------------------------------+
| ROM type check >= v2 |
+-----------------------------------------------------------------------}
\romType, \romTypeValue_ROMv2-
[if<0 do _frameCount _vPCH: loop]
{-----------------------------------------------------------------------+
| Setup |
+-----------------------------------------------------------------------}
{
First setup all subroutine definitions and variables, without making
calls. This way allows use of vLR to hop from page to page during setup,
with minimal overhead. At the end, run the main loop.
}
0 HighScore=
{
Print a 5x8 character on screen with the built-in font
Char must be in the 32-127 range (this is not checked)
}
[def {PrintChar}
{Map ASCII code to offset in font table}
Char 82- [if<0 50+ i= \font32up
else i= \font82up] fontData= {Select low or high page}
i 2<< i+ {Multiply by 5}
fontData+ fontData= {Add to page address to reach bitmap data for Char}
{Draw 5 vertical slices: 5 using font data}
BgColor \sysArgs0.
Color \sysArgs1.
Pos \sysArgs4:
6+ Pos=
\SYS_VDrawBits_134 \sysFn:
$fb i= [do
fontData 0? <fontData++ \sysArgs2. 134!!
<\sysArgs4++
<i++ i if<>0loop]
ret
] PrintChar=
{ PrintText }
[def
push
Text=
[do
Text, Char= {Next character to be printed}
<Text++ {Advance text pointer}
if<>0 {Zero termination}
PrintChar!
loop]
pop ret
] PrintText=
{ DrawLine }
[def
[do if>0
i=
Color Pos.
Pos Step+ Pos=
i 1-
loop]
ret
] DrawLine=
{ Delay until next game cycle }
[def
push
{Update count down timer}
\frameCount, TimeOut- 128& [if=0
Timer [if>0 1- Timer=]
DrawTimer!
]
{Game delay, but accelerate with [A] button or any ASCII key from keyboard}
\buttonState, \buttonA& [if=0 1- else 0] Delay+ Wait!
pop ret
] TimeStep=
{ Draw count down timer }
[def
push
$820 Pos=
8 Color=
0 BgColor=
Timer Value= DrawNumber2!
TimeOut 60+ 255& TimeOut=
pop ret
] DrawTimer=
{-----------------------------------------------------------------------+
|}>_vLR++ ret{ RAM page 3 |
+-----------------------------------------------------------------------}
*=$300
{ Draw snake head }
[def
push
15 {Yellow head} Color= Head DrawBlock!
pop ret
] DrawHead=
{ Game over }
[def
push
$4435 Pos=
4 BgColor=
[def `GAME`OVER #0] PrintText!
Score HighScore- [if>0 {Improved high score?}
Control PlayerControl^ if=0 {Only accept human scores}
Score HighScore= {Update high score}
30 Color= DrawHighScore! {Highlight it}
]
PlayGameOverSound!
pop ret
] GameOver=
{Wait number of frames (range 1..255)}
[def
tmp= \frameCount, tmp+ 255& tmp=
[do \frameCount, tmp- if<>0loop]
ret
] Wait=
{ Intro }
[def
push
{Display welcome tekst}
8 {Green} Color=
0 BgColor=
$800 ClearScreen!
$838 Pos=
[def `Gigatron #0] PrintText!
pop ret
] Intro=
{ Clear screen from current position to bottom right }
[def
p=
\SYS_SetMemory_v2_54 \sysFn: {Not in ROM v1}
BgColor \sysArgs1. {Color}
p [do
\sysArgs2: {Destination}
160 \sysArgs0. {Byte count}
54!! {Make SYS call}
$100 p+ p= if>0loop] {To next line}
ret
] ClearScreen=
{ Turn left }
[def
0 stepV- tmp=
stepU stepV=
tmp stepU=
Bias [if<0 0] 60+ Bias= {Increase bias against left}
ret
] TurnL=
{ Turn right }
[def
0 stepU- tmp=
stepV stepU=
tmp stepV=
Bias [if>0 0] 60- Bias= {Increase bias against right}
ret
] TurnR=
{ Draw 2x2 pixel block in single color }
[def
Pos=
Color \vACH. {Duplicate Color low byte to high byte}
Pos: >Pos++ Pos: {Draws 2x2 pixels with DOKE}
ret
] DrawBlock=
{-----------------------------------------------------------------------+
|}>_vLR++ ret{ RAM page 4 |
+-----------------------------------------------------------------------}
*=$400
{ RunGame }
[def
push
[do
{Control is one of LaunchControl, PlayerControl or AutoControl}
Control!
{Leave a trace behind in the pixels two high bits}
{Bit 15 and bit 1 of stepU identify the direction}
>stepU, 128& tmp=
stepU 2& [if<>0 64] tmp+
27+ {Orange tail} Color= {Tail color}
>Head, Head^ 2& {Striped tail effect}
[if<>0 1] Color^ Color= {Apply effect}
Head DrawBlock! {Draws head in tail color}
Head stepU+ Head= {Make step with head first}
{ Colors: R G B
------- -- -- --
Field = 8 = 8
Wall = 26 = 2+ 8+16
Head = 15 = 3 12
Tail1 = 27 = 3+ 8+16
Tail2 = 26 = 2+ 8+16
Food = 24 = + 8+16
Poison = 62 = 2+12+48
^
Use red channel to detect unsafe steps
}
Head, 3& [if<>0 pop ret] {Hit wall, tail or poison}
Head, 24^ {Food} [if=0 {Eating the food}
$40 Timer+ SetNote!
2 \soundTimer.
Score Timer+ Score= {Add remaining seconds to score}
DrawScore!
5 AddTimer! {5 seconds bonus}
<Grow++ <Drops++
]
DrawHead!
{Update tail}
Grow [if>0
1- Grow= {Do nothing while growing}
AddFood!
else
{Tail is following the head}
Tail, tmp=
Drops [if>0 1- Drops= 62 {Poison}
else 4] {Field}
Color= Tail DrawBlock!
tmp 64& [if=0 $0100 else $0001] x=
tmp 128& [if=0 x else 0 x-]
1<< Tail+ Tail=
]
TimeStep! {Delay for next game step}
loop]
] RunGame=
{ Place random food in field }
[def
push
24 {Greenish food} Color=
[do
\SYS_Random_34 \sysFn:
34!! Pos= {Get random 16-bit number}
{
The ranges are such that 1. no food will touch the wall,
and 2. the probability of landing one step away from
the wall is half (due to truncation with 254&), and 3.
the probability in the center is double (due to partial
overlap of the low and high ranges).
}
<Pos, 128- [if<0 150+] 5+ {X in range 5..154} 254& <Pos.
>Pos, 127& 64- [if<0 102+] 21+ {Y in range 21..122} 254& >Pos.
Pos, 4^ if<>0loop] {Repeat if not free}
Pos DrawBlock!
pop ret
] AddFood=
0 Bias= {Short-term memory for auto control}
{-----------------------------------------------------------------------+
|}>_vLR++ ret{ RAM page 5 |
+-----------------------------------------------------------------------}
*=$500
{ AutoControl }
[def
push
\buttonState, $cf& $cf^ {Ignore buttonStart and buttonSelect}
[if<>0 SetupGame! pop ret] {If other button pressed, restart game}
TurnL p= {p stays set as long as left turn is ok}
TurnR q= {q stays set as long as right turn is ok}
Head stepU+ peek {Look ahead}
4^ tmp= {tmp<>0 for turning, tmp=0 for straight}
[if<>0
{24^4} 28^ [if=0 tmp=] {Go straight if food ahead}
else
Head stepU+ stepU+ peek {If clear also look two steps ahead}
62^ if=0 >tmp++ {Turn early when heading towards poison}
]
Head stepV- peek 4^ {Look left}
[if<>0 {24^4} 28^ {Something is there}
[if=0 q= >tmp++ {Food! Must turn but not right}
else 0 p=] {Not safe, don't go there}
else {Left is clear}
Head stepV- stepV- peek 4^ {Look two steps left}
if<>0 {24^4} 28^
[if=0 q= >tmp++ {Also turn for food two steps away}
else Bias 15+ Bias] {Build up some extra bias for right}
]
Head stepV+ peek 4^ {Look right}
[if<>0 {24^4} 28^ {Something is there}
[if=0 p= >tmp++ {Food! Must turn but not right}
else 0 q=] {Not safe, don't go there}
else {Right is clear}
Head stepV+ stepV+ peek 4^ {Look two steps right}
if<>0 {24^4} 28^
[if=0 p= >tmp++ {Also turn for food two steps away}
else Bias 15- Bias] {Build up some extra bias for left}
]
tmp [if=0 {If not already turning}
\entropy, 13- {Turn randomly 13 out of 128, or 5%}
[if<0 1 else 0]]
[if<>0 {Algorithm wants to turn}
p [if=0 q else {Not left means right}
q [if=0 p else {Not right means left}
\entropy, 128- {Pick direction randomly otherwise}
Bias+ {With progressive bias against last turns}
[if<0 p else q]
]
]
if<>0 call {TurnL or TurnR and update Bias}
]
pop ret
] AutoControl=
{-----------------------------------------------------------------------+
|}>_vLR++ ret{ RAM page 6 |
+-----------------------------------------------------------------------}
*=$600
[def {AddTimer}
push
Timer+ Timer=
15- [if>0 15 Timer=]
\frameCount, TimeOut=
DrawTimer!
pop ret
] AddTimer=
[def {SetupGame}
push
{Reset score}
0 Score= Timer= DrawScore!
15 Timer= DrawTimer!
DrawHighScore!
{Clear playing field}
4 {Dark green field} BgColor=
$1000 Pos= ClearScreen!
{Draw bounding box}
26 {Orange wall} Color=
+1 Step= 159 DrawLine!
$100 Step= 111 DrawLine!
-1 Step= 159 DrawLine!
-$100 Step= 110 DrawLine!
+1 Step= 158 DrawLine!
$100 Step= 109 DrawLine!
-1 Step= 157 DrawLine!
-$100 Step= 109 DrawLine!
{Play messages}
GetReady!
{Place snake}
$4430 Head= Tail=
DrawHead!
0 Grow= Drops=
Timer= 15 AddTimer!
$0002 stepU= $0200 stepV= {Start right}
{Place 25 foods}
25 [do i= AddFood! i 1- if>0loop]
LaunchControl Control=
pop ret
] SetupGame=
[def
push
$802 Pos=
Score HighScore- [if>0 30 else 8] Color=
0 BgColor=
Score Value= DrawNumber4!
{Progressive game speed}
Score 100- [if<0 4 else
200- [if<0 3 else 2]] Delay=
pop ret
] DrawScore=
{-----------------------------------------------------------------------+
|}$08a0 \vLR: ret{ RAM page 8 |
+-----------------------------------------------------------------------}
*=$8a0
[def {LaunchControl}
{
XXX better strategy: run both PlayerControl and AutoControl.
The first one to take control will take over the game...
}
push
$449c Head- [if=0
{Switch to autopilot when reaching this position}
30 {XXX} Color=
$0802 Pos=
[def `AUTO #0] PrintText!
AutoControl Control= call
else
{Disable launch control once a button is pressed}
\buttonState, $cf& {~buttonStart|~buttonSelect} $cf^ [if<>0 PlayerControl Control= call]
]
pop ret
] LaunchControl=
[def { DrawNumber2 - Value Radix }
push
$20 Char=
10 Radix= DrawNextDigit!
$30 Char=
1 Radix= DrawNextDigit!
pop ret
] DrawNumber2=
{-----------------------------------------------------------------------+
|}>_vLR++ ret{ RAM page 9 |
+-----------------------------------------------------------------------}
*=$9a0
[def { DrawNextDigit -- Value Radix }
push
Value Radix- [if>=0
Value=
$31 Char=
Value [do
Radix-
if>=0
Value=
<Char++
loop
]
PrintChar!
$30 Char=
else
PrintChar!
]
pop ret
] DrawNextDigit=
[def { DrawNumber4 - Value Radix }
push
$20 Char=
1000 Radix= DrawNextDigit!
100 Radix= DrawNextDigit!
10 Radix= DrawNextDigit!
$30 Char=
1 Radix= DrawNextDigit!
pop ret
] DrawNumber4=
{-----------------------------------------------------------------------+
|}>_vLR++ ret{ RAM page 10 |
+-----------------------------------------------------------------------}
*=$aa0
[def
push
$874 Pos= 0 BgColor=
[def `HI` #0] PrintText!
HighScore Value= DrawNumber4!
pop ret
] DrawHighScore=
[def {SetupChannel}
i= 255| 255^ $fa| p= {Pointer to channel struct}
<i, i= {Extract note}
0 p. <p++ {.wavA}
1 p. <p++ {.wavX: 0 "Noise", 1 Triangle, 2 Pulse, 3 Sawtooth}
\notesTable i+ 0? p. <p++ {.keyL}
\notesTable i+ 1? p. <p++ {.keyH}
ret
] SetupChannel=
{-----------------------------------------------------------------------+
|}>_vLR++ ret{ RAM page 11 |
+-----------------------------------------------------------------------}
*=$ba0
{ Setup note in all channels, but don't play it yet }
[def
push
1<< tmp=
$100 tmp| {Channel 1} SetupChannel!
$200 tmp| {Channel 2} SetupChannel!
$300 tmp| {Channel 3} SetupChannel!
$400 tmp| {Channel 4} SetupChannel!
pop ret
] SetNote=
[def
push
$49 j= [do
j SetNote!
5 \soundTimer.
[do \soundTimer, if<>0loop]
j 1- j= $30^ if<>0loop]
pop ret
] PlayGameOverSound=
{-----------------------------------------------------------------------+
|}>_vLR++ ret{ RAM page 12 |
+-----------------------------------------------------------------------}
*=$ca0
{ Display the get ready messages }
[def
push
$4533
p= Pos= 24 Color= Messages PrintText! 45 Wait! {Eat food}
p Pos= 62 Color= Text PrintText! 45 Wait! {Not poison}
p Pos= 15 Color= Text PrintText! 45 Wait! {Get ready}
p Pos= Text PrintText!
pop ret
] GetReady=
{-----------------------------------------------------------------------+
|}>_vLR++ ret{ RAM page 13 |
+-----------------------------------------------------------------------}
*=$da0
[def
stepU tmp= {Default keep running in same direction}
\buttonState, $fe^ {~buttonRight} [if=0 $0002 tmp=]
\buttonState, $fd^ {~buttonLeft} [if=0 $fffe tmp=]
\buttonState, $fb^ {~buttonDown} [if=0 $0200 tmp=]
\buttonState, $f7^ {~buttonUp} [if=0 $fe00 tmp=]
{Only turn if not reversing 180 degrees}
tmp stepU+ [if<>0 tmp stepU=]
{Note: `stepV' is not used once under player control}
ret
] PlayerControl=
{-----------------------------------------------------------------------+
|}>_vLR++ ret{ RAM page 14 |
+-----------------------------------------------------------------------}
*=$ea0
[def ``EAT`FOOD #0
`NOT`POISON #0
`GET`READY! #0
``````````` #0] Messages=
{-----------------------------------------------------------------------+
| Run |
+-----------------------------------------------------------------------}
Intro!
$49 SetNote!
[do
SetupGame!
RunGame!
120 \soundTimer.
7 {Orange} Color= Head DrawBlock!
GameOver!
60 Wait!
loop
]
{-----------------------------------------------------------------------+
| End |
+-----------------------------------------------------------------------}