REM #################################################################### REM BREAKOUT REM REM One hundred percent BASIC, for the AKGL BASIC interpreter. REM ../akbasic/build-akgl/basic breakout.bas REM REM The ball and the paddle are sprites, defined from DATA in this listing. REM The bricks, the HUD and the messages are characters in the text grid. REM Nothing here loads an asset and nothing here uses DRAW, BOX or CIRCLE: REM the drawing verbs do not survive a frame in this interpreter, while the REM text grid and the sprites are both redrawn from persistent state every REM frame. See README.md for the whole list of things that shaped this. REM REM Three rules this listing never breaks, each one earned the hard way: REM * A row of text is written whole, from column 0. CHAR terminates the REM row where it stops, so writing at column N erases N+1 onwards. REM * Mixed + and - are parenthesised. This parser reads a - b + c as REM a - (b + c), and matches only ONE unparenthesised AND or OR. REM * The main loop is a GOTO loop. Every loop and every GOSUB takes one REM of 32 scopes, and a state change jumping out of a DO would leak one REM every time; thirty-two lives later the program stops. REM REM Two more rules were true when this was written and are not any more. REM They are recorded because the shape of the listing is still theirs: REM * Every name is created once, in the pool block below. A name first REM created inside a GOSUB or a FOR used to cost a value slot that was REM never handed back, out of 4096 for the whole run, and this game died REM after twenty-five seconds because of it. A scalar is free now; only REM the DIMs below still have to be up here. TODO.md section 6 item 30. REM * Writing PAST the terminator of a short row used to draw nothing at REM all. The gap is padded with spaces now. TODO.md section 6 item 32. REM REM Written by Claude Opus (1M) REM #################################################################### LABEL SETUP SCNCLR REM --- geometry ------------------------------------------------------- REM Nothing here is assumed. RGR(1) and RGR(2) are the window in pixels, REM RGR(3) and RGR(4) are one character cell, and RWINDOW gives the text REM grid in columns and rows -- so the game fits whatever window the host REM opened and whatever font it loaded. Everything below is derived from REM these six, including the ball's speed relative to the wall. REM REM The first version of this file had CW# = 16 and CH# = 16 written out, REM measured by hand against the bundled C64_Pro_Mono at 16 points, because REM there was no way to ask -- and that was the one thing in the listing REM that would break on a different font. RGR(3) and RWINDOW were added to REM the interpreter because of it. SCW# = RGR(1) SCH# = RGR(2) CW# = RGR(3) CH# = RGR(4) COLS# = RWINDOW(1) ROWS# = RWINDOW(0) REM --- the brick wall, in cells --------------------------------------- BRW# = 4 BCOLS# = 10 BROWS# = 6 BTOP# = 4 BLEFT# = (COLS# - (BRW# * BCOLS#)) / 2 REM --- the play area, in pixels --------------------------------------- TOPY# = 2 * CH# MAXX# = SCW# - 8 PW# = 144 PY# = 528 LOSEY# = PY# + 32 MSGROW# = 35 TITROW# = 20 PSPD# = 10 REM --- pools ---------------------------------------------------------- REM Every name in the program is created HERE. REM REM One reason still stands. A GOSUB gets its own scope: assignment walks up REM the chain and finds an outer variable, but a name first seen inside a REM subroutine is created in the subroutine and dies at RETURN -- so HIT# REM and BI# would mean nothing to the caller if they were not already out REM here, and HITTEST would answer into two variables nobody can read. REM REM The other has been fixed. A scalar used to cost a value slot for good -- REM the pool is a bump allocator with no free, and recycling a variable took REM fresh slots, so 4096 creations ended the run whenever they happened. A REM scalar now lives in the variable itself and costs the pool nothing; only REM the DIMs still spend, which is why they are the part that has to be up REM here. The rest of this block is habit, and harmless. DIM BR#(60) DIM SB#(63) DIM SP#(63) DIM LAY$(6) DIM BSG$(6) SCORE# = 0 HIGH# = 0 LIVES# = 3 LEVEL# = 1 LEFTN# = 0 BSPD# = 4 STATE# = 0 DEMO# = 0 PAUSED# = 0 HELD# = 0 PDIR# = 0 PDEC# = 0 PX# = 0 BX# = 0 BY# = 0 BVX# = 0 BVY# = 0 OX# = 0 OY# = 0 K# = 0 HIT# = 0 PVX# = 0 DOFF# = 0 STALL# = 0 NUDGE# = 0 BI# = 0 BR2# = 0 RB# = 0 CB# = 0 RC# = 0 CC2# = 0 TX# = 0 TY# = 0 PTS# = 0 V# = 0 L# = 0 C2# = 0 D# = 0 M# = 0 X2# = 0 X3# = 0 Z# = 0 BB# = 0 RX# = 0 N# = 0 MROW# = 0 RMAX# = 2 RND# = 0 SND# = 0 P$ = "" H$ = "" S$ = "" T$ = "" MSG$ = "" BSEG$ = "" FRAME$ = "" I# = 0 R# = 0 R2# = 0 C# = 0 CC# = 0 KI# = 0 T# = 0 REM Brick faces, one per wall row. CHAR ignores its colour argument -- REM the sink draws in one colour -- so the rows are told apart by shape. BSG$(0) = "[##]" BSG$(1) = "[##]" BSG$(2) = "[==]" BSG$(3) = "[==]" BSG$(4) = "[--]" BSG$(5) = "[--]" REM --- the seed ------------------------------------------------------- REM There is no RND in this dialect. TI# is jiffies off the host's clock REM and is host uptime rather than zero-based, which makes it a fine seed. SEED# = TI# REM The ceiling, drawn once. TOPY# is the bottom edge of this row, so the REM ball turns against something the player can see rather than against an REM invisible line two rows down from the top of the window. FRAME$ = "=" * COLS# CHAR 1, 0, 1, FRAME$ GOSUB MKSPR GOSUB SNDPROBE GOTO TITLE REM #################################################################### REM SPRITES REM #################################################################### LABEL MKSPR FOR I# = 0 TO 62 READ SB#(I#) NEXT I# FOR I# = 0 TO 62 READ SP#(I#) NEXT I# SPRSAV SB#, 1 SPRITE 1, 1, 2 SPRSAV SP#, 2 SPRSAV SP#, 3 SPRSAV SP#, 4 SPRITE 2, 1, 15, 0, 1, 0 SPRITE 3, 1, 15, 0, 1, 0 SPRITE 4, 1, 15, 0, 1, 0 RETURN REM #################################################################### REM SOUND REM REM SOUND refuses on a machine with no audio device, and an untrapped REM refusal would end the game. Ask once, remember the answer, and never REM ask again -- the game is silent where there is nothing to play on. REM #################################################################### LABEL SNDPROBE SND# = 1 TRAP NOAUDIO SOUND 1, 2000, 1 TRAP RETURN LABEL NOAUDIO SND# = 0 RESUME NEXT LABEL BEEPB IF SND# = 1 THEN SOUND 1, 12000, 2 RETURN LABEL BEEPP IF SND# = 1 THEN SOUND 2, 6000, 3 RETURN LABEL BEEPW IF SND# = 1 THEN SOUND 3, 9000, 1 RETURN LABEL BEEPL IF SND# = 1 THEN SOUND 1, 1500, 20 RETURN REM #################################################################### REM TITLE AND ATTRACT MODE REM REM The title screen plays itself. It is also how this game gets tested REM without a human at the keyboard. REM #################################################################### LABEL TITLE DEMO# = 1 SCORE# = 0 LIVES# = 3 LEVEL# = 1 BSPD# = 4 PAUSED# = 0 GOSUB LOADLAY GOSUB BUILDW GOSUB DRAWW GOSUB DRAWHUD GOSUB TITTEXT GOSUB SERVE STATE# = 0 GOTO TICK LABEL TITTEXT MROW# = TITROW# MSG$ = "B R E A K O U T" GOSUB SHOWAT MROW# = TITROW# + 2 MSG$ = "PRESS SPACE TO PLAY" GOSUB SHOWAT MROW# = TITROW# + 4 MSG$ = "LEFT AND RIGHT MOVE P PAUSES Q QUITS" GOSUB SHOWAT MROW# = TITROW# + 6 MSG$ = "ATTRACT MODE" GOSUB SHOWAT RETURN LABEL CLRTIT MROW# = TITROW# GOSUB CLRAT MROW# = TITROW# + 2 GOSUB CLRAT MROW# = TITROW# + 4 GOSUB CLRAT MROW# = TITROW# + 6 GOSUB CLRAT RETURN REM #################################################################### REM GAME AND LEVEL SETUP REM #################################################################### LABEL NEWGAME DEMO# = 0 SCORE# = 0 LIVES# = 3 LEVEL# = 1 BSPD# = 4 PAUSED# = 0 GOSUB CLRTIT GOSUB NEWLEV STATE# = 0 GOTO TICK LABEL NEWLEV GOSUB LOADLAY GOSUB BUILDW GOSUB DRAWW GOSUB DRAWHUD GOSUB SERVE MSG$ = "PRESS SPACE TO LAUNCH" GOSUB SHOWMSG RETURN REM Three layouts, then round again with a faster ball. RESTORE takes a REM label -- it wants a line and a label evaluates to one -- so a layout is REM simply a named place in the DATA stream. LABEL LOADLAY N# = MOD((LEVEL# - 1), 3) IF N# = 0 THEN RESTORE LAY1 IF N# = 1 THEN RESTORE LAY2 IF N# = 2 THEN RESTORE LAY3 FOR R# = 0 TO 5 READ LAY$(R#) NEXT R# RETURN LABEL BUILDW LEFTN# = 0 FOR R# = 0 TO 5 S$ = LAY$(R#) GOSUB BUILDR NEXT R# RETURN LABEL BUILDR FOR C# = 0 TO 9 T$ = MID(S$, C#, 1) I# = (R# * BCOLS#) + C# BR#(I#) = 0 IF T$ = "1" THEN BR#(I#) = 1 IF T$ = "1" THEN LEFTN# = LEFTN# + 1 NEXT C# RETURN LABEL DRAWW FOR R# = 0 TO 5 BR2# = R# GOSUB DRAWBR NEXT R# RETURN REM One brick row, rebuilt whole and written in a single CHAR. Anything REM else would truncate the row at the first character it wrote. LABEL DRAWBR S$ = "" IF BLEFT# > 0 THEN S$ = " " * BLEFT# BSEG$ = BSG$(BR2#) FOR CC# = 0 TO 9 I# = (BR2# * BCOLS#) + CC# IF BR#(I#) = 1 THEN S$ = S$ + BSEG$ IF BR#(I#) = 0 THEN S$ = S$ + " " NEXT CC# R2# = BTOP# + BR2# CHAR 1, 0, R2#, S$ RETURN REM Put the ball back on the paddle, pointing up, left or right at random. LABEL SERVE PX# = (SCW# - PW#) / 2 HELD# = 1 IF DEMO# = 1 THEN HELD# = 0 BX# = PX# + ((PW# / 2) - 4) BY# = PY# - 10 RMAX# = 2 GOSUB RANDOM BVX# = BSPD# IF RND# = 0 THEN BVX# = 0 - BSPD# BVY# = 0 - BSPD# PDEC# = 0 GOSUB SHOWSPR RETURN REM A linear congruential generator, because this dialect has no RND. REM The multiply stays inside int64 for any seed under 2^31. LABEL RANDOM SEED# = MOD(((SEED# * 1103515245) + 12345), 2147483648) RND# = MOD((SEED# / 65536), RMAX#) RETURN REM #################################################################### REM THE MAIN LOOP REM REM A GOTO loop rather than DO/LOOP on purpose: every loop and every REM GOSUB pushes one of 32 scopes, and a state change that jumped out of REM a DO would leak one every time. This loop pushes nothing. REM #################################################################### LABEL TICK GOSUB READKEY IF STATE# <> 0 THEN GOTO DISPATCH IF PAUSED# = 1 THEN GOTO TICKEND GOSUB MOVEPAD IF HELD# = 1 THEN GOSUB HOLDBAL IF HELD# = 0 THEN GOSUB MOVEBAL GOSUB SHOWSPR LABEL TICKEND SLEEP 0.02 GOTO TICK LABEL DISPATCH IF STATE# = 1 THEN GOTO LOSTLIF IF STATE# = 2 THEN GOTO LEVELUP IF STATE# = 3 THEN GOTO BYE IF STATE# = 5 THEN GOTO NEWGAME STATE# = 0 GOTO TICK REM #################################################################### REM INPUT REM REM GET is the only non-blocking read there is, and there is no key-up REM event and no held-key state. What there IS is the host's own key REM repeat, which arrives as ordinary key-down events about once per tick. REM So a keystroke refills a countdown and the paddle coasts to a stop REM when the repeats stop: held reads as held, tapped reads as a nudge. REM #################################################################### LABEL READKEY FOR KI# = 1 TO 8 GET K# IF K# <> 0 THEN GOSUB HANDKEY NEXT KI# RETURN LABEL HANDKEY IF K# = 1073741904 THEN PDIR# = 0 - 1 IF K# = 1073741904 THEN PDEC# = 12 IF K# = 1073741903 THEN PDIR# = 1 IF K# = 1073741903 THEN PDEC# = 12 IF K# = 32 THEN GOSUB KEYSPC IF K# = 112 THEN GOSUB KEYPAU IF K# = 113 THEN STATE# = 3 IF K# = 27 THEN STATE# = 3 RETURN LABEL KEYSPC IF DEMO# = 1 THEN STATE# = 5 IF DEMO# = 1 THEN RETURN IF HELD# = 0 THEN RETURN HELD# = 0 GOSUB CLRMSG RETURN LABEL KEYPAU IF DEMO# = 1 THEN RETURN PAUSED# = 1 - PAUSED# IF PAUSED# = 1 THEN MSG$ = "PAUSED" IF PAUSED# = 1 THEN GOSUB SHOWMSG IF PAUSED# = 0 THEN GOSUB CLRMSG RETURN REM #################################################################### REM THE PADDLE REM #################################################################### LABEL MOVEPAD IF DEMO# = 1 THEN GOSUB DEMOPAD IF DEMO# = 1 THEN GOTO PADCLMP IF PDEC# < 1 THEN GOTO PADCLMP PDEC# = PDEC# - 1 PX# = PX# + (PDIR# * PSPD#) LABEL PADCLMP IF PX# < 0 THEN PX# = 0 M# = SCW# - PW# IF PX# > M# THEN PX# = M# RETURN LABEL DEMOPAD TX# = (BX# + 4) - (PW# / 2) TX# = TX# + DOFF# D# = TX# - PX# IF D# > PSPD# THEN D# = PSPD# M# = 0 - PSPD# IF D# < M# THEN D# = M# PX# = PX# + D# RETURN LABEL HOLDBAL BX# = PX# + ((PW# / 2) - 4) BY# = PY# - 10 RETURN REM #################################################################### REM THE BALL REM REM X first and then Y, each move tested on its own, so a ball arriving REM at the corner of a brick reverses one axis rather than both. REM #################################################################### LABEL MOVEBAL OX# = BX# BX# = BX# + BVX# IF BX# < 0 THEN GOSUB WALLL IF BX# > MAXX# THEN GOSUB WALLR GOSUB XBRICK OY# = BY# BY# = BY# + BVY# IF BY# < TOPY# THEN GOSUB WALLT GOSUB YBRICK GOSUB PADHIT IF BY# > LOSEY# THEN STATE# = 1 STALL# = STALL# + 1 IF STALL# > 500 THEN NUDGE# = 1 RETURN LABEL WALLL BX# = 0 BVX# = 0 - BVX# GOSUB BEEPW RETURN LABEL WALLR BX# = MAXX# BVX# = 0 - BVX# GOSUB BEEPW RETURN LABEL WALLT BY# = TOPY# BVY# = 0 - BVY# GOSUB BEEPW RETURN REM The leading edge, not the centre: a ball tested by its centre sinks REM four pixels into a brick before it turns, which looks like the brick REM was hit late and the one beside it was not hit at all. LABEL XBRICK TX# = BX# IF BVX# > 0 THEN TX# = BX# + 7 TY# = BY# + 4 GOSUB HITTEST IF HIT# = 0 THEN RETURN BX# = OX# BVX# = 0 - BVX# GOSUB KILLBR RETURN LABEL YBRICK TX# = BX# + 4 TY# = BY# IF BVY# > 0 THEN TY# = BY# + 7 GOSUB HITTEST IF HIT# = 0 THEN RETURN BY# = OY# BVY# = 0 - BVY# GOSUB KILLBR RETURN REM Which brick, if any, holds the pixel (TX#, TY#). This is the only REM place in the game that converts pixels into cells. LABEL HITTEST HIT# = 0 RC# = TY# / CH# IF RC# < BTOP# THEN RETURN RB# = RC# - BTOP# IF RB# > (BROWS# - 1) THEN RETURN CC2# = TX# / CW# IF CC2# < BLEFT# THEN RETURN CB# = (CC2# - BLEFT#) / BRW# IF CB# > (BCOLS# - 1) THEN RETURN BI# = (RB# * BCOLS#) + CB# BR2# = RB# IF BR#(BI#) = 1 THEN HIT# = 1 RETURN LABEL KILLBR BR#(BI#) = 0 LEFTN# = LEFTN# - 1 STALL# = 0 PTS# = (BROWS# - BR2#) * 10 SCORE# = SCORE# + PTS# IF DEMO# = 0 THEN GOSUB HISCORE GOSUB DRAWBR GOSUB DRAWHUD GOSUB BEEPB IF LEFTN# < 1 THEN STATE# = 2 RETURN REM Paddle bounce. Five zones across the face, so where you catch it REM decides where it goes -- the whole game, really. LABEL PADHIT IF BVY# < 1 THEN RETURN BB# = BY# + 8 IF BB# < PY# THEN RETURN IF BY# > (PY# + 10) THEN RETURN RX# = PX# + PW# IF (BX# + 8) < PX# THEN RETURN IF BX# > RX# THEN RETURN BY# = PY# - 8 BVY# = 0 - BSPD# Z# = ((BX# + 4) - PX#) / (PW# / 5) IF Z# < 0 THEN Z# = 0 IF Z# > 4 THEN Z# = 4 PVX# = BVX# BVX# = (Z# - 2) * 3 IF BVX# <> 0 THEN GOTO PADAIM REM Dead centre. Keep the direction it came in on and give it a shallow REM angle: a ball with no sideways speed at all rises and falls down one REM column for ever, which is how attract mode used to hang itself. BVX# = 2 IF PVX# < 0 THEN BVX# = 0 - 2 LABEL PADAIM IF NUDGE# = 1 THEN GOSUB UNSTICK IF DEMO# = 1 THEN GOSUB DEMOAIM GOSUB BEEPP RETURN REM Ten seconds without touching a brick means the ball has found an orbit REM that misses the wall -- straight up and straight back down the same REM empty column, usually. Give it a different angle, but do it HERE, on a REM bounce the player is already watching, rather than in mid-flight where REM it would look like the ball hit something that was not there. LABEL UNSTICK NUDGE# = 0 STALL# = 0 RMAX# = 4 GOSUB RANDOM BVX# = (RND# * 3) - 6 IF BVX# = 0 THEN BVX# = 3 RETURN REM Attract mode aims off-centre by a random amount, so the machine plays REM like something with a hand on the paddle rather than a mirror. LABEL DEMOAIM RMAX# = 81 GOSUB RANDOM DOFF# = RND# - 40 RETURN REM #################################################################### REM DRAWING REM #################################################################### REM The sprite position is its top-left corner in device pixels, and the REM paddle is three 48-wide segments laid end to end. Each coordinate is REM worked out into a variable first: MOVSPR reads a leading sign as REM "move by this much" rather than "move to here". LABEL SHOWSPR MOVSPR 1, BX#, BY# MOVSPR 2, PX#, PY# X2# = PX# + 48 MOVSPR 3, X2#, PY# X3# = PX# + 96 MOVSPR 4, X3#, PY# RETURN LABEL DRAWHUD V# = SCORE# GOSUB PAD6 H$ = " SCORE " + P$ H$ = (H$ + " LIVES ") + LIVES# H$ = (H$ + " LEVEL ") + LEVEL# V# = HIGH# GOSUB PAD6 H$ = (H$ + " HIGH ") + P$ CHAR 1, 0, 0, H$ RETURN LABEL PAD6 P$ = "" + V# DO WHILE LEN(P$) < 6 P$ = "0" + P$ LOOP RETURN LABEL SHOWMSG MROW# = MSGROW# GOSUB SHOWAT RETURN LABEL CLRMSG MROW# = MSGROW# GOSUB CLRAT RETURN REM Centre MSG$ on row MROW#. No padding on the right is needed: CHAR REM terminates the row where the message ends, which erases the rest. LABEL SHOWAT L# = LEN(MSG$) C2# = (COLS# - L#) / 2 S$ = MSG$ IF C2# > 0 THEN S$ = (" " * C2#) + MSG$ CHAR 1, 0, MROW#, S$ RETURN LABEL CLRAT CHAR 1, 0, MROW#, " " RETURN REM #################################################################### REM WHAT HAPPENS WHEN THE BALL GETS PAST YOU REM #################################################################### LABEL LOSTLIF GOSUB BEEPL IF DEMO# = 1 THEN GOTO DEMOSRV LIVES# = LIVES# - 1 GOSUB DRAWHUD IF LIVES# < 1 THEN GOTO GAMEOVR GOSUB SERVE MSG$ = "BALL LOST -- PRESS SPACE" GOSUB SHOWMSG STATE# = 0 GOTO TICK LABEL DEMOSRV GOSUB SERVE HELD# = 0 STATE# = 0 GOTO TICK LABEL LEVELUP MSG$ = "LEVEL CLEARED" GOSUB SHOWMSG SLEEP 1.5 LEVEL# = LEVEL# + 1 IF BSPD# < 7 THEN BSPD# = BSPD# + 1 GOSUB NEWLEV IF DEMO# = 1 THEN GOSUB CLRMSG STATE# = 0 GOTO TICK LABEL GAMEOVR MSG$ = "GAME OVER -- SPACE PLAYS AGAIN, Q QUITS" GOSUB SHOWMSG LABEL GOWAIT GET K# IF K# = 32 THEN GOTO NEWGAME IF K# = 113 THEN GOTO BYE IF K# = 27 THEN GOTO BYE SLEEP 0.05 GOTO GOWAIT LABEL BYE SPRITE 1, 0 SPRITE 2, 0 SPRITE 3, 0 SPRITE 4, 0 SCNCLR PRINT "BREAKOUT" PRINT "FINAL SCORE " + SCORE# PRINT "HIGH SCORE " + HIGH# SLEEP 2 QUIT REM The machine does not get on the scoreboard: attract mode keeps score REM so the wall behaves, but only a player's score is ever the high one. LABEL HISCORE IF SCORE# > HIGH# THEN HIGH# = SCORE# RETURN REM #################################################################### REM SPRITE PATTERNS REM REM 63 bytes each: three per row, twenty-one rows, most significant bit REM leftmost. A clear bit is transparent, which is what lets an 8x8 ball REM live in the corner of a 24x21 pattern with nothing drawn around it. REM REM byte 0 byte 1 byte 2 REM 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 REM ^ column 0 ^ column 23 REM #################################################################### REM Ball: an 8x8 disc in the top-left corner, so its pixel rectangle is REM the sprite position and eight pixels each way. No offset arithmetic. REM . . # # # # . . 60 REM . # # # # # # . 126 REM # # # # # # # # 255 DATA 60, 0, 0 DATA 126, 0, 0 DATA 255, 0, 0 DATA 255, 0, 0 DATA 255, 0, 0 DATA 255, 0, 0 DATA 126, 0, 0 DATA 60, 0, 0 DATA 0, 0, 0 DATA 0, 0, 0 DATA 0, 0, 0 DATA 0, 0, 0 DATA 0, 0, 0 DATA 0, 0, 0 DATA 0, 0, 0 DATA 0, 0, 0 DATA 0, 0, 0 DATA 0, 0, 0 DATA 0, 0, 0 DATA 0, 0, 0 DATA 0, 0, 0 REM Paddle segment: the full 24 wide and 6 deep, expanded to 48 wide by REM SPRITE's xexpand bit. Three of them make one 144 pixel paddle. DATA 255, 255, 255 DATA 255, 255, 255 DATA 255, 255, 255 DATA 255, 255, 255 DATA 255, 255, 255 DATA 255, 255, 255 DATA 0, 0, 0 DATA 0, 0, 0 DATA 0, 0, 0 DATA 0, 0, 0 DATA 0, 0, 0 DATA 0, 0, 0 DATA 0, 0, 0 DATA 0, 0, 0 DATA 0, 0, 0 DATA 0, 0, 0 DATA 0, 0, 0 DATA 0, 0, 0 DATA 0, 0, 0 DATA 0, 0, 0 DATA 0, 0, 0 REM #################################################################### REM LEVEL LAYOUTS REM REM Six rows of ten bricks. 1 is a brick, 0 is a hole. RESTORE picks the REM layout by line number, so adding a fourth is a block and one IF. REM #################################################################### LABEL LAY1 REM Level 1: the whole wall. DATA "1111111111" DATA "1111111111" DATA "1111111111" DATA "1111111111" DATA "1111111111" DATA "1111111111" LABEL LAY2 REM Level 2: a barrel, open at the corners. DATA "0011111100" DATA "0111111110" DATA "1111111111" DATA "1111111111" DATA "0111111110" DATA "0011111100" LABEL LAY3 REM Level 3: gaps to shoot through. DATA "1010101010" DATA "0101010101" DATA "1111001111" DATA "1100110011" DATA "1011111101" DATA "0110110110"