606 lines
16 KiB
Plaintext
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 |
|
|
+-----------------------------------------------------------------------}
|
|
|