Tuesday, September 2, 2025

Maze generator in GW-BASIC

 A while back I was looking at maze making algorithms written in Basic. I was not very happy with any of the ones I could find at the time, they where quite dumb and inefficient. So I decided to write my own. The geometry of the maze is controlled by the MAPX, MAPY and MAPZ variables. The code below is set to generate just one floor.

10 REM Maze Generator in GW-BASIC
11 REM Uses bits for walls: 1=West, 2=North, 4=East, 8=South, 16=Up
12 REM 0 means no walls (connected to all adjacent rooms)
13 REM by Conradtrout 2025

20 DEFINT A-Z
30 MAPX = 5: MAPY = 5: MAPZ = 1
40 DIM MAZE(MAPX,MAPY,MAPZ)
50 DIM CURPATH(MAPX*MAPY, 2)
60 RANDOMIZE TIMER

70 REM Initialize/reset maze
80 FOR TZ = 1 TO MAPZ
90 FOR TY = 1 TO MAPY
100 FOR TX = 1 TO MAPX: MAZE(TX,TY,TZ) = 0: NEXT TX
110 NEXT TY
120 NEXT TZ
130 GOSUB 900 ' Generate map
140 FLOOR=1
150 GOSUB 1100 ' Print map
160 PRINT "FINISHED ON X:";X;"Y:";Y
170 PRINT "PRESS ANY KEY...": AKEY$ = INPUT$(1)
180 IF FLOOR = MAPZ THEN 499
190 FLOOR = FLOOR + 1: GOTO 150

499 END

500 REM Maze Generator start sub-routine
510 CURSTEP = 0
520 REM Jumps back here.
530 CURDIRS = 0
540 IF X > 1 THEN IF MAZE(X-1,Y,FLOOR) = 0 THEN CURDIRS = CURDIRS OR 1
550 IF X < MAPX THEN IF MAZE(X+1,Y,FLOOR) = 0 THEN CURDIRS = CURDIRS OR 4
560 IF Y > 1 THEN IF MAZE(X,Y-1,FLOOR) = 0 THEN CURDIRS = CURDIRS OR 2
570 IF Y < MAPY THEN IF MAZE(X,Y+1,FLOOR) = 0 THEN CURDIRS = CURDIRS OR 8
580 IF CURDIRS = 0 THEN GOSUB 800: IF EMPTYROOMS = 0 THEN RETURN ' Finished!

600 REM Randomly choosing where to go. Steps backward if we must.
610 REM Reserved for a cond. stop. Never mind this line. :-)
620 HEADING = 2 ^ INT(RND * 4)
630 IF (CURDIRS AND HEADING) <> 0 THEN GOSUB 700: GOTO 520 ' Moved cursor forward.
640 CURDIRS = CURDIRS AND (NOT HEADING)
650 IF CURDIRS > 0 THEN 620 ' If not true then move cursor back.
660 CURSTEP = CURSTEP - 1: X = CURPATH(CURSTEP, 1): Y = CURPATH(CURSTEP, 2)
670 GOTO 520

700 REM Move cursor forward (into unvisited room)
710 CURPATH(CURSTEP, 1) = X: CURPATH(CURSTEP, 2) = Y: OLDX = X: OLDY = Y
720 IF HEADING = 1 THEN X = X-1: RHEADING = 4: GOTO 760
730 IF HEADING = 2 THEN Y = Y-1: RHEADING = 8: GOTO 760
740 IF HEADING = 4 THEN X = X+1: RHEADING = 1: GOTO 760
750 IF HEADING = 8 THEN Y = Y+1: RHEADING = 2: GOTO 760
751 STOP ' Should never happen. Hopefully. :-)
760 CURSTEP = CURSTEP+1
770 MAZE(X,Y,FLOOR) = MAZE(X,Y,FLOOR) OR RHEADING
780 MAZE(OLDX,OLDY,FLOOR) = MAZE(OLDX,OLDY,FLOOR) OR HEADING
790 RETURN

800 REM Check for unvisited rooms
810 EMPTYROOMS = 0
820 FOR TY = 1 TO MAPY
830 FOR TX = 1 TO MAPX
840 IF MAZE(TX,TY,FLOOR) = 0 THEN EMPTYROOMS = -1: RETURN
850 NEXT TX
860 NEXT TY
870 RETURN

900 REM Start of map gen.
910 X=1+INT(RND*MAPX): Y=1+INT(RND*MAPY)
920 FOR FLOOR = 1 TO MAPZ
930 PRINT "GENERATING FLOOR"; FLOOR; "..."
940 GOSUB 500: REM Run Mazegen sub-routine
950 FOR TY = 1 TO MAPY
960 FOR TX = 1 TO MAPX: MAZE(TX,TY,FLOOR) = 15 AND NOT MAZE(TX,TY,FLOOR): NEXT TX
970 NEXT TY
980 IF FLOOR < MAPZ THEN MAZE(X,Y,FLOOR) = MAZE(X,Y,FLOOR) OR 16
990 NEXT FLOOR
1000 RETURN

1100 REM Print the maze
1110 CLS: PRINT "+";
1120 FOR TX = 1 TO MAPX: PRINT "---+";: NEXT TX
1130 PRINT
1140 FOR TY = 1 TO MAPY
1150 PRINT "|";
1160 FOR TX = 1 TO MAPX
1170 IF (MAZE(TX,TY,FLOOR) AND 16) <> 0 THEN R$="U" ELSE R$=" "
1180 IF FLOOR > 1 THEN IF (MAZE(TX,TY,FLOOR-1) AND 16) <> 0 THEN R$="D"
1190 IF (MAZE(TX,TY,FLOOR) AND 4) = 0 THEN PRINT " ";R$;"  "; ELSE PRINT " ";R$;" |";
1200 NEXT TX
1210 PRINT
1220 PRINT "+";
1230 FOR TX = 1 TO MAPX
1240 IF (MAZE(TX,TY,FLOOR) AND 8) = 0 THEN PRINT "   +"; ELSE PRINT "---+";
1250 NEXT TX
1260 PRINT
1270 NEXT TY
1280 PRINT
1290 REM RETURN
1300 PRINT "Maze Array (Bit Values):"
1310 FOR TY = 1 TO MAPY
1320 FOR TX = 1 TO MAPX
1330 PRINT TAB(2+(TX-1)*4); MID$(STR$(MAZE(TX,TY,FLOOR)),2);
1340 NEXT TX
1350 PRINT
1360 NEXT TY
1370 RETURN

Saturday, April 19, 2025

Sprites in GW-BASIC 4

Previously we've stored our graphics data in a very verbose manner. This is not ideal since GW-BASIC is an interpreted language and keeps the entire source code of our program in memory as the it runs. If you keep wasting memory this way your program will eventually crash and GW-BASIC will give you the "out of string space" error message. I know that from personal experience.

So, what to do about it? Preferably we would place all graphics in a separate data file that we load into memory as the program requires them.

But we could also keep using DATA statements but convert the graphics data to the format GW-BASIC's PUT and GET statements uses before our program runs. I'm not going explain how to do that here since it's quite the complicated process with several steps. Another time maybe. But, I will however show you what our program will look like using that approach:
10 SCREEN 1
20 DEFINT A-Z
30 GFXSIZE = 33
50 DIM PLAYERGFX(GFXSIZE): DIM PLAYERMASK(GFXSIZE)
51 DIM MONSTERGFX(GFXSIZE): DIM MONSTERMASK(GFXSIZE)
52 DIM GRASSGFX(GFXSIZE): DIM ICEGFX(GFXSIZE)
53 DIM TOADSGFX(GFXSIZE): DIM TOADSMASK(GFXSIZE)
60 ' This time around we don't need to draw the sprites to the screen
61 ' and then use GET to put them into arrays. We simply put the DATA values
62 ' directly into the arrays with the READ statement.
70 FOR T = 0 TO GFXSIZE: READ PLAYERGFX(T): NEXT T
71 FOR T = 0 TO GFXSIZE: READ PLAYERMASK(T): NEXT T
72 FOR T = 0 TO GFXSIZE: READ MONSTERGFX(T): NEXT T
73 FOR T = 0 TO GFXSIZE: READ MONSTERMASK(T): NEXT T
74 FOR T = 0 TO GFXSIZE: READ GRASSGFX(T): NEXT T
75 FOR T = 0 TO GFXSIZE: READ ICEGFX(T): NEXT T
76 FOR T = 0 TO GFXSIZE: READ TOADSGFX(T): NEXT T
77 FOR T = 0 TO GFXSIZE: READ TOADSMASK(T): NEXT T

100 FOR Y = 0 TO 7
105 GY = Y * 16
110 FOR X = 0 TO 19
115 GX = X * 16
120 IF X &gt 0 AND X &lt 19 AND Y &gt 2 AND Y &lt 7 THEN PUT (GX, GY), ICEGFX, PSET ELSE PUT (GX, GY), GRASSGFX, PSET
130 NEXT X
140 NEXT Y
150 PUT (32, 56), PLAYERMASK, AND
160 PUT (32, 56), PLAYERGFX, OR
170 PUT (128, 80), MONSTERMASK, AND: PUT (128, 80), MONSTERGFX, OR
180 PUT (192, 32), MONSTERMASK, AND: PUT (192, 32), MONSTERGFX, OR
181 PUT (240, 16), TOADSMASK, AND: PUT (240, 16), TOADSGFX, OR
182 PUT (160, 0), TOADSMASK, AND: PUT (160, 0), TOADSGFX, OR
183 PUT (64, 32), TOADSMASK, AND: PUT (64, 32), TOADSGFX, OR

300 LOCATE 17: PRINT "A simple program for demonstrating
310 PRINT "16x16 tiled graphics in GW-BASIC."
400 END

890 ' The sprite data follow below.

900 ' player
901 DATA 32, 16, 0, 0, 10752, 160, -30208, 168, 10754, 170
902 DATA 16131, 255, 5376, 85, 21504, 81, 29952, 165, 16128, 255
903 DATA 16128, 175, -20736, 255, -21758, 254, 27137, 16633, 10752, 168
904 DATA 10240, 40, 10752, 42
905 ' player mask
906 DATA 32, 16, 255, -253, 252, -256, 240, 16128, 240, 16128
907 DATA 240, 16128, 240, 16128, 252, 16128, 252, 16128, 252, 16128
908 DATA 252, 16128, 240, 16128, 240, 3840, 240, 3840, 240, 3840
909 DATA 255, 16128, 255, 16128
910 ' snake
911 DATA 32, 16, 0, 0, 0, -32767, 0, 24576, 256, 24661
912 DATA 1280, -32598, 1536, 0, 1280, 88, 256, 86, 0, 22
913 DATA 5376, 86, 21765, 90, 22037, 168, 6161, 0, 20501, 0
914 DATA 16405, 0, 0, 0
915 ' snake mask
916 DATA 32, 16, -1, 4080, -1, 1008, -3841, 768, -16129, 768
917 DATA -16129, 768, -16129, 3840, -16129, 16128, -16129, 16128, 255, 16128
918 DATA 192, 16128, 0, 16128, 0, 16128, 0, -256, 0, -1
919 DATA 768, -1, 3840, -1
920 ' grass
921 DATA 32, 16, 256, 4096, 272, 0, 16, 4, 16384, 1028
922 DATA 16384, 1024, 4, 64, 4, 16448, 4096, 16640, 4112, 260
923 DATA 16, 4, 256, 0, 257, 1024, 1, 1024, 4160, 4
924 DATA 4160, 4, 0, 4096
925 ' ice
926 DATA 32, 16, 23893, 21853, 21877, 30069, -10795, -10923, 22359, 22359
927 DATA 23893, 21853, 30069, 30069, -10795, -10923, 22359, 22357, 23893, 21853
928 DATA 21877, 30069, -10795, -10923, 22359, 22359, 23893, 23901, 30069, 30069
929 DATA -10795, -10923, 22359, 22357
930 ' toadstool
931 DATA 32, 16, 0, 0, 0, 0, 0, 0, -22016, 186
932 DATA -17918, -24406, -21750, -22357, -22005, -22358, -31998, -24336, 768, 240
933 DATA 768, 240, 768, 240, 768, 240, 768, 240, 1792, 244
934 DATA 256, 80, 0, 0
935 ' toadstool mask
936 DATA 32, 16, -1, -1, -1, -1, 252, 16128, 240, 768
937 DATA 192, 0, 192, 0, 192, 0, 192, 0, 240, 768
938 DATA -3841, -253, -3841, -253, -3841, -253, -16129, -256, -16129, -256
939 DATA -16129, -256, -3841, -253

Sprites in GW-BASIC 3

Sprites that we intend to draw on top of previously drawn graphics will often look nicer if we use masking. With masks we can tell the GW-BASIC what previously drawn pixels we want to keep in the spot where we intend to place new graphics.

This is often useful since character or item graphics we've made aren't always going to be shaped exactly like squares of 16 by 16 pixels.
10 SCREEN 1
20 DEFINT A-Z
30 GFXSIZE = 33
50 DIM PLAYERGFX(GFXSIZE): DIM PLAYERMASK(GFXSIZE)
60 DIM MONSTERGFX(GFXSIZE): DIM MONSTERMASK(GFXSIZE)
65 DIM GRASSGFX(GFXSIZE): DIM ICEGFX(GFXSIZE)
70 GOSUB 800: GET (0, 0)-(15, 15), PLAYERGFX: CLS
71 GOSUB 800: GET (0, 0)-(15, 15), PLAYERMASK: CLS
72 GOSUB 800: GET (0, 0)-(15, 15), MONSTERGFX: CLS
73 GOSUB 800: GET (0, 0)-(15, 15), MONSTERMASK: CLS
74 GOSUB 800: GET (0, 0)-(15, 15), GRASSGFX: CLS
75 GOSUB 800: GET (0, 0)-(15, 15), ICEGFX: CLS

100 FOR Y = 0 TO 7
105 GY = Y * 16
110 FOR X = 0 TO 19
115 GX = X * 16
120 IF X > 0 AND X < 19 AND Y > 2 AND Y < 7 THEN PUT (GX, GY), ICEGFX, PSET ELSE PUT (GX, GY), GRASSGFX, PSET
130 NEXT X
140 NEXT Y
150 PUT (32, 56), PLAYERMASK, AND
151 ' A mask is just made up of black and white pixels.
152 ' When we PUT the mask we must use the AND operator.
153 ' AND, behaving like a bitwise AND operator, will leave the pixels
154 ' already drawn to screen alone where the mask has a white pixel.
155 ' Any pixel already drawn to screen where the mask has a black
156 ' pixel will be cleared from the screen.
160 PUT (32, 56), PLAYERGFX, OR
161 ' Now when drawing the actual sprite to screen, we use the
162 ' OR operator.
163 ' OR adds the bit values of the sprite to where you PUT it,
164 ' retaining whatever pixels the mask didn't remove.
170 PUT (128, 80), MONSTERMASK, AND: PUT (128, 80), MONSTERGFX, OR
180 PUT (192, 32), MONSTERMASK, AND: PUT (192, 32), MONSTERGFX, OR

300 LOCATE 17: PRINT "A simple program for demonstrating
310 PRINT "16x16 tiled graphics in GW-BASIC."
400 END

800 ' Subroutine that read the DATA statement values
801 ' and then draw pixels with PSET
810 FOR y = 0 TO 15
820 FOR x = 0 TO 15
830 READ c
840 PSET (x, y), c
850 NEXT x
860 NEXT y
870 RETURN

900 ' player gfx
901 DATA 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
902 DATA 0,0,0,0,0,2,2,2,2,2,0,0,0,0,0,0
903 DATA 0,0,0,0,2,0,2,2,2,2,2,0,0,0,0,0
904 DATA 0,0,0,2,0,2,2,2,2,2,2,2,0,0,0,0
905 DATA 0,0,0,3,0,3,3,3,3,3,3,3,0,0,0,0
906 DATA 0,0,0,0,0,1,1,1,1,1,1,1,0,0,0,0
907 DATA 0,0,0,0,1,1,1,0,1,1,0,1,0,0,0,0
908 DATA 0,0,0,0,1,3,1,1,2,2,1,1,0,0,0,0
909 DATA 0,0,0,0,0,3,3,3,3,3,3,3,0,0,0,0
910 DATA 0,0,0,0,0,3,3,3,2,2,3,3,0,0,0,0
911 DATA 0,0,0,0,2,2,3,3,3,3,3,3,0,0,0,0
912 DATA 0,0,0,2,2,2,2,3,3,3,3,2,0,0,0,0
913 DATA 0,0,0,1,1,2,2,2,3,3,2,1,1,0,0,0
914 DATA 0,0,0,0,0,2,2,2,2,2,2,0,0,0,0,0
915 DATA 0,0,0,0,0,2,2,0,0,2,2,0,0,0,0,0
916 DATA 0,0,0,0,0,2,2,2,0,2,2,2,0,0,0,0

920 ' player mask
921 DATA 3,3,3,3,0,0,0,0,0,0,0,3,3,3,3,3
922 DATA 3,3,3,0,0,0,0,0,0,0,0,0,3,3,3,3
923 DATA 3,3,0,0,0,0,0,0,0,0,0,0,0,3,3,3
924 DATA 3,3,0,0,0,0,0,0,0,0,0,0,0,3,3,3
925 DATA 3,3,0,0,0,0,0,0,0,0,0,0,0,3,3,3
926 DATA 3,3,0,0,0,0,0,0,0,0,0,0,0,3,3,3
927 DATA 3,3,3,0,0,0,0,0,0,0,0,0,0,3,3,3
928 DATA 3,3,3,0,0,0,0,0,0,0,0,0,0,3,3,3
929 DATA 3,3,3,0,0,0,0,0,0,0,0,0,0,3,3,3
930 DATA 3,3,3,0,0,0,0,0,0,0,0,0,0,3,3,3
931 DATA 3,3,0,0,0,0,0,0,0,0,0,0,0,3,3,3
932 DATA 3,3,0,0,0,0,0,0,0,0,0,0,0,0,3,3
933 DATA 3,3,0,0,0,0,0,0,0,0,0,0,0,0,3,3
934 DATA 3,3,0,0,0,0,0,0,0,0,0,0,0,0,3,3
935 DATA 3,3,3,3,0,0,0,0,0,0,0,0,0,3,3,3
936 DATA 3,3,3,3,0,0,0,0,0,0,0,0,0,3,3,3

940 ' snake gfx
941 DATA 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
942 DATA 0,0,0,0,0,0,0,0,0,0,0,1,2,0,0,0
943 DATA 0,0,0,0,0,0,0,0,0,0,0,0,1,2,0,0
944 DATA 0,0,0,0,0,0,0,1,1,1,1,1,1,2,0,0
945 DATA 0,0,0,0,0,0,1,1,2,2,2,2,2,0,0,0
946 DATA 0,0,0,0,0,0,1,2,0,0,0,0,0,0,0,0
947 DATA 0,0,0,0,0,0,1,1,1,1,2,0,0,0,0,0
948 DATA 0,0,0,0,0,0,0,1,1,1,1,2,0,0,0,0
949 DATA 0,0,0,0,0,0,0,0,0,1,1,2,0,0,0,0
950 DATA 0,0,0,0,0,1,1,1,1,1,1,2,0,0,0,0
951 DATA 0,0,1,1,1,1,1,1,1,1,2,2,0,0,0,0
952 DATA 0,1,1,1,1,1,1,2,2,2,2,0,0,0,0,0
953 DATA 0,1,0,1,0,1,2,0,0,0,0,0,0,0,0,0
954 DATA 0,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0
955 DATA 0,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0
956 DATA 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0

960 ' snake mask
961 DATA 3,3,3,3,3,3,3,3,3,3,0,0,0,0,3,3
962 DATA 3,3,3,3,3,3,3,3,3,3,0,0,0,0,0,3
963 DATA 3,3,3,3,3,3,0,0,0,0,0,0,0,0,0,3
964 DATA 3,3,3,3,3,0,0,0,0,0,0,0,0,0,0,3
965 DATA 3,3,3,3,3,0,0,0,0,0,0,0,0,0,0,3
966 DATA 3,3,3,3,3,0,0,0,0,0,0,0,0,0,3,3
967 DATA 3,3,3,3,3,0,0,0,0,0,0,0,0,3,3,3
968 DATA 3,3,3,3,3,0,0,0,0,0,0,0,0,3,3,3
969 DATA 3,3,3,3,0,0,0,0,0,0,0,0,0,3,3,3
970 DATA 3,0,0,0,0,0,0,0,0,0,0,0,0,3,3,3
971 DATA 0,0,0,0,0,0,0,0,0,0,0,0,0,3,3,3
972 DATA 0,0,0,0,0,0,0,0,0,0,0,0,0,3,3,3
973 DATA 0,0,0,0,0,0,0,0,0,0,0,0,3,3,3,3
974 DATA 0,0,0,0,0,0,0,0,3,3,3,3,3,3,3,3
975 DATA 0,0,0,0,0,0,0,3,3,3,3,3,3,3,3,3
976 DATA 0,0,0,0,0,0,3,3,3,3,3,3,3,3,3,3

980 ' grass
981 DATA 0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0
982 DATA 0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0
983 DATA 0,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0
984 DATA 0,0,0,0,1,0,0,0,0,0,1,0,0,0,1,0
985 DATA 0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0
986 DATA 0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0
987 DATA 0,0,1,0,0,0,0,0,1,0,0,0,1,0,0,0
988 DATA 0,0,0,0,0,1,0,0,0,0,0,0,1,0,0,1
989 DATA 0,1,0,0,0,1,0,0,0,0,1,0,0,0,0,1
992 DATA 0,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0
993 DATA 0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0
994 DATA 0,0,0,1,0,0,0,1,0,0,0,0,0,0,1,0
995 DATA 0,0,0,1,0,0,0,0,0,0,0,0,0,0,1,0
996 DATA 1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,0
997 DATA 1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,0
998 DATA 0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0

1000 ' ice
1010 DATA 1,1,1,1,1,1,3,1,1,1,3,1,1,1,1,1
1011 DATA 1,3,1,1,1,1,1,1,1,3,1,1,1,3,1,1
1012 DATA 3,1,1,1,3,1,1,1,1,1,1,1,3,1,1,1
1013 DATA 1,1,1,3,1,1,1,3,1,1,1,3,1,1,1,3
1014 DATA 1,1,1,1,1,1,3,1,1,1,3,1,1,1,1,1
1015 DATA 1,3,1,1,1,3,1,1,1,3,1,1,1,3,1,1
1016 DATA 3,1,1,1,3,1,1,1,1,1,1,1,3,1,1,1
1017 DATA 1,1,1,3,1,1,1,3,1,1,1,1,1,1,1,3
1018 DATA 1,1,1,1,1,1,3,1,1,1,3,1,1,1,1,1
1019 DATA 1,3,1,1,1,1,1,1,1,3,1,1,1,3,1,1
1020 DATA 3,1,1,1,3,1,1,1,1,1,1,1,3,1,1,1
1021 DATA 1,1,1,3,1,1,1,3,1,1,1,3,1,1,1,3
1022 DATA 1,1,1,1,1,1,3,1,1,1,3,1,1,1,3,1
1023 DATA 1,3,1,1,1,3,1,1,1,3,1,1,1,3,1,1
1024 DATA 3,1,1,1,3,1,1,1,1,1,1,1,3,1,1,1
1025 DATA 1,1,1,3,1,1,1,3,1,1,1,1,1,1,1,3
Geesh... really starting to become cluttered in here with DATA statements. We'll look at that next.

Sprites in GW-BASIC 2

Let's add some grass and other terrain for the gnome and the snakes to stand on.
10 SCREEN 1
20 DEFINT A-Z
30 GFXSIZE = 33
50 DIM PLAYERGFX(GFXSIZE)
60 DIM MONSTERGFX(GFXSIZE)
70 DIM GRASSGFX(GFXSIZE): DIM ICEGFX(GFXSIZE)
80 GOSUB 800 ' Call to subroutine that reads and draws pixels
90 GET (0, 0)-(15, 15), PLAYERGFX ' Grabs graphics and puts it in an array
100 CLS ' Clear the screen
110 GOSUB 800 ' Read and draw next sprite
120 GET (0, 0)-(15, 15), MONSTERGFX
130 CLS
140 GOSUB 800: GET (0, 0)-(15, 15), GRASSGFX: CLS
150 GOSUB 800: GET (0, 0)-(15, 15), ICEGFX: CLS

200 FOR Y = 0 TO 7
205 GY = Y * 16
210 FOR X = 0 TO 19
215 GX = X * 16
220 IF X > 0 AND X < 19 AND Y > 2 AND Y < 7 THEN PUT (GX, GY), ICEGFX, PSET ELSE PUT (GX, GY), GRASSGFX, PSET
230 NEXT X
240 NEXT Y

300 PUT (32, 56), PLAYERGFX, PSET
310 PUT (128, 80), MONSTERGFX, PSET
320 PUT (192, 32), MONSTERGFX, PSET
330 LOCATE 17: PRINT "A simple program for demonstrating
340 PRINT "16x16 tiled graphics in GW-BASIC."

400 END

800 ' Subroutine that read the DATA statement values
801 ' and then draw pixels with PSET
810 FOR y = 0 TO 15
820 FOR x = 0 TO 15
830 READ c
840 PSET (x, y), c
850 NEXT x
860 NEXT y
870 RETURN

890 ' The sprite data follows below.
891 ' Each value represent the color of an individual pixel.
892 ' Since we're in CGA 320x200 we have four colors to work with.
893 ' They are: 0 = black, 1 = cyan, 2 = magenta, 3 = white

900 ' player gfx
901 DATA 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
902 DATA 0,0,0,0,0,2,2,2,2,2,0,0,0,0,0,0
903 DATA 0,0,0,0,2,0,2,2,2,2,2,0,0,0,0,0
904 DATA 0,0,0,2,0,2,2,2,2,2,2,2,0,0,0,0
905 DATA 0,0,0,3,0,3,3,3,3,3,3,3,0,0,0,0
906 DATA 0,0,0,0,0,1,1,1,1,1,1,1,0,0,0,0
907 DATA 0,0,0,0,1,1,1,0,1,1,0,1,0,0,0,0
908 DATA 0,0,0,0,1,3,1,1,2,2,1,1,0,0,0,0
909 DATA 0,0,0,0,0,3,3,3,3,3,3,3,0,0,0,0
910 DATA 0,0,0,0,0,3,3,3,2,2,3,3,0,0,0,0
911 DATA 0,0,0,0,2,2,3,3,3,3,3,3,0,0,0,0
912 DATA 0,0,0,2,2,2,2,3,3,3,3,2,0,0,0,0
913 DATA 0,0,0,1,1,2,2,2,3,3,2,1,1,0,0,0
914 DATA 0,0,0,0,0,2,2,2,2,2,2,0,0,0,0,0
915 DATA 0,0,0,0,0,2,2,0,0,2,2,0,0,0,0,0
916 DATA 0,0,0,0,0,2,2,2,0,2,2,2,0,0,0,0

940 ' snake gfx
941 DATA 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
942 DATA 0,0,0,0,0,0,0,0,0,0,0,1,2,0,0,0
943 DATA 0,0,0,0,0,0,0,0,0,0,0,0,1,2,0,0
944 DATA 0,0,0,0,0,0,0,1,1,1,1,1,1,2,0,0
945 DATA 0,0,0,0,0,0,1,1,2,2,2,2,2,0,0,0
946 DATA 0,0,0,0,0,0,1,2,0,0,0,0,0,0,0,0
947 DATA 0,0,0,0,0,0,1,1,1,1,2,0,0,0,0,0
948 DATA 0,0,0,0,0,0,0,1,1,1,1,2,0,0,0,0
949 DATA 0,0,0,0,0,0,0,0,0,1,1,2,0,0,0,0
950 DATA 0,0,0,0,0,1,1,1,1,1,1,2,0,0,0,0
951 DATA 0,0,1,1,1,1,1,1,1,1,2,2,0,0,0,0
952 DATA 0,1,1,1,1,1,1,2,2,2,2,0,0,0,0,0
953 DATA 0,1,0,1,0,1,2,0,0,0,0,0,0,0,0,0
954 DATA 0,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0
955 DATA 0,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0
956 DATA 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0

980 ' grass
981 DATA 0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0
982 DATA 0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0
983 DATA 0,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0
984 DATA 0,0,0,0,1,0,0,0,0,0,1,0,0,0,1,0
985 DATA 0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0
986 DATA 0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0
987 DATA 0,0,1,0,0,0,0,0,1,0,0,0,1,0,0,0
988 DATA 0,0,0,0,0,1,0,0,0,0,0,0,1,0,0,1
989 DATA 0,1,0,0,0,1,0,0,0,0,1,0,0,0,0,1
992 DATA 0,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0
993 DATA 0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0
994 DATA 0,0,0,1,0,0,0,1,0,0,0,0,0,0,1,0
995 DATA 0,0,0,1,0,0,0,0,0,0,0,0,0,0,1,0
996 DATA 1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,0
997 DATA 1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,0
998 DATA 0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0

1000 ' ice
1010 DATA 1,1,1,1,1,1,3,1,1,1,3,1,1,1,1,1
1011 DATA 1,3,1,1,1,1,1,1,1,3,1,1,1,3,1,1
1012 DATA 3,1,1,1,3,1,1,1,1,1,1,1,3,1,1,1
1013 DATA 1,1,1,3,1,1,1,3,1,1,1,3,1,1,1,3
1014 DATA 1,1,1,1,1,1,3,1,1,1,3,1,1,1,1,1
1015 DATA 1,3,1,1,1,3,1,1,1,3,1,1,1,3,1,1
1016 DATA 3,1,1,1,3,1,1,1,1,1,1,1,3,1,1,1
1017 DATA 1,1,1,3,1,1,1,3,1,1,1,1,1,1,1,3
1018 DATA 1,1,1,1,1,1,3,1,1,1,3,1,1,1,1,1
1019 DATA 1,3,1,1,1,1,1,1,1,3,1,1,1,3,1,1
1020 DATA 3,1,1,1,3,1,1,1,1,1,1,1,3,1,1,1
1021 DATA 1,1,1,3,1,1,1,3,1,1,1,3,1,1,1,3
1022 DATA 1,1,1,1,1,1,3,1,1,1,3,1,1,1,3,1
1023 DATA 1,3,1,1,1,3,1,1,1,3,1,1,1,3,1,1
1024 DATA 3,1,1,1,3,1,1,1,1,1,1,1,3,1,1,1
1025 DATA 1,1,1,3,1,1,1,3,1,1,1,1,1,1,1,3
Hmm. The PUT command draws over old graphics on the screen indiscriminately making black boxes around our little gnome and his snake friends. We'll tackle that next!

Sprites in GW-BASIC

Simple example that draws some 16x16 sprites in 4 color CGA mode to the screen.
10 SCREEN 1
20 DEFINT A-Z
30 GFXSIZE = 33 ' This value is the size of an array containing one sprite.
31 ' The GFXSIZE value calculated from the bit-depth (4 colors = 2 bit) and
32 ' the dimensions of the graphics.
33 '
34 ' In this case the tile is 16x16x2 which results in the calculation
35 ' looking like this: ((4 + INT((16 * 2 + 7) / 8) * 16) + .5) \ 2
36 ' Let's not get into what all of that means right now.
37 '
38 ' Note: In GW-BASIC you have to store each sprite in separate arrays.
39 ' In QBASIC, on the other hand, you can store several sprites in a single
40 ' array and use an index to specify the one you want to draw when calling
41 ' the PUT command.

50 DIM PLAYERGFX(GFXSIZE)
60 DIM MONSTERGFX(GFXSIZE)
70 GOSUB 800 ' Call to subroutine that reads and draws pixels
80 GET (0, 0)-(15, 15), PLAYERGFX ' Grabs graphics and puts it in an array
90 CLS ' Clear the screen
100 GOSUB 800 ' Read and draw next sprite
110 GET (0, 0)-(15, 15), MONSTERGFX
120 CLS

150 PUT (32, 56), PLAYERGFX, PSET
160 PUT (128, 80), MONSTERGFX, PSET
170 PUT (192, 32), MONSTERGFX, PSET
180 LOCATE 17: PRINT "A simple program for demonstrating
190 PRINT "16x16 tiled graphics in GW-BASIC."

300 END

800 ' Subroutine that read the DATA statement values
801 ' and then draw pixels with PSET
810 FOR y = 0 TO 15
820 FOR x = 0 TO 15
830 READ c
840 PSET (x, y), c
850 NEXT x
860 NEXT y
870 RETURN

890 ' The sprite data follows below.
901 ' Each value represent the color of an individual pixel.
892 ' Since we're in CGA 320x200 we have four colors to work with.
893 ' They are: 0 = black, 1 = cyan, 2 = magenta, 3 = white

900 ' player gfx
901 DATA 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
902 DATA 0,0,0,0,0,2,2,2,2,2,0,0,0,0,0,0
903 DATA 0,0,0,0,2,0,2,2,2,2,2,0,0,0,0,0
904 DATA 0,0,0,2,0,2,2,2,2,2,2,2,0,0,0,0
905 DATA 0,0,0,3,0,3,3,3,3,3,3,3,0,0,0,0
906 DATA 0,0,0,0,0,1,1,1,1,1,1,1,0,0,0,0
907 DATA 0,0,0,0,1,1,1,0,1,1,0,1,0,0,0,0
908 DATA 0,0,0,0,1,3,1,1,2,2,1,1,0,0,0,0
909 DATA 0,0,0,0,0,3,3,3,3,3,3,3,0,0,0,0
910 DATA 0,0,0,0,0,3,3,3,2,2,3,3,0,0,0,0
911 DATA 0,0,0,0,2,2,3,3,3,3,3,3,0,0,0,0
912 DATA 0,0,0,2,2,2,2,3,3,3,3,2,0,0,0,0
913 DATA 0,0,0,1,1,2,2,2,3,3,2,1,1,0,0,0
914 DATA 0,0,0,0,0,2,2,2,2,2,2,0,0,0,0,0
915 DATA 0,0,0,0,0,2,2,0,0,2,2,0,0,0,0,0
916 DATA 0,0,0,0,0,2,2,2,0,2,2,2,0,0,0,0

940 ' snake gfx
941 DATA 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
942 DATA 0,0,0,0,0,0,0,0,0,0,0,1,2,0,0,0
943 DATA 0,0,0,0,0,0,0,0,0,0,0,0,1,2,0,0
944 DATA 0,0,0,0,0,0,0,1,1,1,1,1,1,2,0,0
945 DATA 0,0,0,0,0,0,1,1,2,2,2,2,2,0,0,0
946 DATA 0,0,0,0,0,0,1,2,0,0,0,0,0,0,0,0
947 DATA 0,0,0,0,0,0,1,1,1,1,2,0,0,0,0,0
948 DATA 0,0,0,0,0,0,0,1,1,1,1,2,0,0,0,0
949 DATA 0,0,0,0,0,0,0,0,0,1,1,2,0,0,0,0
950 DATA 0,0,0,0,0,1,1,1,1,1,1,2,0,0,0,0
951 DATA 0,0,1,1,1,1,1,1,1,1,2,2,0,0,0,0
952 DATA 0,1,1,1,1,1,1,2,2,2,2,0,0,0,0,0
953 DATA 0,1,0,1,0,1,2,0,0,0,0,0,0,0,0,0
954 DATA 0,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0
955 DATA 0,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0
956 DATA 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0

Tuesday, April 15, 2025

Mystify

Let's start off with something simple. Here's a rather spartan clone of the old screen-saver called "Mystify."

We'll start with the CGA version:
10 REM Mystify - CGA version
11 RANDOMIZE TIMER
12 KEY OFF
15 SCREEN 1: CLS
20 DEFINT A-Z
30 MAXLINES = 11
40 DIM Lines(MAXLINES, 4)
50 startwidth = 10 + INT(RND * 40)
60 x1 = INT(RND * (320 - startwidth))
70 y1 = INT(RND * (200 - startwidth))
80 y2 = y1 + startwidth
90 x2 = x1 + startwidth
100 steps = 10
110 SWAP y1, y2
120 x1inc = 2 + INT(RND * steps)
130 y1inc = 2 + INT(RND * steps)
140 x2inc = 2 + INT(RND * steps)
150 y2inc = 2 + INT(RND * steps)
190 c = 1
300 akey$ = "a"
310 REM Make a new line
320 FOR l = (MAXLINES - 1) TO 1 STEP -1
330 FOR a = 0 TO 4
340 Lines(l + 1, a) = Lines(l, a)
350 NEXT a
360 NEXT l
370 Lines(1, 0) = x1: Lines(1, 1) = y1
380 Lines(1, 2) = x2: Lines(1, 3) = y2
390 Lines(1, 4) = c
400 LINE (Lines(MAXLINES, 0), Lines(MAXLINES, 1))-(Lines(MAXLINES, 2), Lines(MAXLINES, 3)), 0
410 FOR l = 1 TO MAXLINES - 1
420 LINE (Lines(l, 0), Lines(l, 1))-(Lines(l, 2), Lines(l, 3)), Lines(l, 4)
430 NEXT l
440 REM SOUND 0, 1
450 c = (c + 1) MOD 3 + 1 ' CGA color range ensured by MOD operator
460 x1 = x1 + x1inc
470 y1 = y1 + y1inc
480 x2 = x2 + x2inc
490 y2 = y2 + y2inc
500 IF x1 < 0 THEN x1inc = -x1inc: x1 = 0
510 IF x1 > 319 THEN x1inc = -x1inc: x1 = 319
520 IF y1 < 0 THEN y1inc = -y1inc: y1 = 0
530 IF y1 > 199 THEN y1inc = -y1inc: y1 = 199
540 IF x2 < 0 THEN x2inc = -x2inc: x2 = 0
550 IF x2 > 319 THEN x2inc = -x2inc: x2 = 319
560 IF y2 < 0 THEN y2inc = -y2inc: y2 = 0
570 IF y2 > 199 THEN y2inc = -y2inc: y2 = 199
580 akey$ = INKEY$
590 IF akey$ <> CHR$(27) THEN 300
600 SCREEN 0: WIDTH 80
610 CLS
620 PRINT "Good-bye!"
630 END
And now for a the EGA version:
10 REM Mystify - EGA version
11 RANDOMIZE TIMER
12 KEY OFF
15 SCREEN 7, , 1, 0: CLS
20 DEFINT A-Z
30 MAXLINES = 16
40 DIM Lines(MAXLINES, 4)
50 startwidth = 10 + INT(RND * 40)
60 x1 = INT(RND * (319 - startwidth))
70 y1 = INT(RND * (199 - startwidth))
80 y2 = y1 + startwidth
90 x2 = x1 + startwidth
100 steps = 5
110 SWAP y1, y2
120 x1inc = 2 + INT(RND * steps)
130 y1inc = 2 + INT(RND * steps)
140 x2inc = 2 + INT(RND * steps)
150 y2inc = 2 + INT(RND * steps)
170 c1 = 1: c = c1
180 cinc = 1
190 turns = 0
300 akey$ = "a"
310 REM Make a new line
320 FOR l = (MAXLINES - 1) TO 1 STEP -1
330 FOR a = 0 TO 4: Lines(l + 1, a) = Lines(l, a): NEXT a
340 NEXT l
350 Lines(1, 0) = x1: Lines(1, 1) = y1
360 Lines(1, 2) = x2: Lines(1, 3) = y2
370 Lines(1, 4) = c
400 PCOPY 1, 0
410 CLS 1
420 FOR l = 1 TO MAXLINES
430 LINE (Lines(l, 0), Lines(l, 1))-(Lines(l, 2), Lines(l, 3)), (Lines(l, 4) + l) MOD 16
440 NEXT l
450 REM SOUND 0, 1
460 turns = turns + 1
470 chgcol = (turns MOD 2) = 0
480 IF chgcol THEN c = c + cinc: IF (c = 15 OR c = c1) THEN cinc = -cinc
490 x1 = x1 + x1inc: y1 = y1 + y1inc: x2 = x2 + x2inc: y2 = y2 + y2inc
500 IF x1 < 0 THEN x1inc = -x1inc: x1 = 0
510 IF x1 > 319 THEN x1inc = -x1inc: x1 = 319
520 IF y1 < 0 THEN y1inc = -y1inc: y1 = 0
530 IF y1 > 199 THEN y1inc = -y1inc: y1 = 199
540 IF x2 < 0 THEN x2inc = -x2inc: x2 = 0
550 IF x2 > 319 THEN x2inc = -x2inc: x2 = 319
560 IF y2 < 0 THEN y2inc = -y2inc: y2 = 0
570 IF y2 > 199 THEN y2inc = -y2inc: y2 = 199
580 akey$ = INKEY$
590 IF akey$ <> CHR$(27) THEN 300
600 SCREEN 0: WIDTH 80
610 CLS
620 PRINT "Good-bye!"
630 END
And there you have it. Pretty horrible, ey?

Saturday, April 12, 2025

Hello, World

 Hello, World

 This blog I will use to share code snippets and sometimes entire games written in GW-BASIC and QBASIC. Just for fun.


  10 RANDOMIZE TIMER
  20 PRINT "Hello, this is a code snippet test!"
  30 PRINT "This should appear in a black monospaced font."
  40 GOTO 20

Maze generator in GW-BASIC

 A while back I was looking at maze making algorithms written in Basic. I was not very happy with any of the ones I could find at the time, ...