Qbasicnews.com

Full Version: Out of Stack Space
You're currently viewing a stripped down version of our content. View the full version with proper formatting.
Pages: 1 2
Here's my latest code for the Ascii Block Game, After awhile I get out of stack space becuase I call to many SUBS. Any Suggestions?

Code:
DECLARE SUB resetgame ()
DECLARE SUB readkeys ()
DECLARE SUB directions ()
DECLARE SUB leftright ()
DECLARE SUB mapeditor ()
DECLARE SUB menu (data$, numofitems!, mode!, detail$, sw!, sh!)
DECLARE SUB customlvl ()
DECLARE SUB credits ()
DECLARE SUB loadscr ()
DECLARE SUB game ()
DECLARE SUB move ()
DECLARE SUB check ()
DECLARE SUB loadmap (number, name$)
'$DYNAMIC
WIDTH 80, 50
CLS
ON ERROR GOTO errorcheck

TYPE type1
x AS INTEGER
y AS INTEGER
oldx AS INTEGER
oldy AS INTEGER
got AS INTEGER '1 got -- 0 dontgot
facing AS INTEGER '-1 left -- 1 right
END TYPE

DIM SHARED maph, mapw, drops, path$, level, par, cusplay, debug, ver, name$
DIM SHARED cuspath$, mappath$
resetgame
DIM SHARED guy AS type1
DIM SHARED map(1 TO mapw, 1 TO maph)

CALL menu("GameMode  Mapeditor DirectionsCredits   Close     ", 5, 1, "Ascii Block Game Ver." + STR$(ver), 80, 50)
END
errorcheck:
CLOSE
CLS
PRINT "Unrecoverable error:"
PRINT
SELECT CASE ERR
CASE 62
PRINT "Not enough Data in file"
PRINT "Possible reason:"
PRINT "The level trying to load is not compatable with version"; ver
CASE 76
PRINT "Path not found"
PRINT "Possible reason:"
PRINT "The set path is incorrect or there is a missing folder"
CASE 64
PRINT "Bad file name"
PRINT "Possible reason:"
PRINT "The filename contains illegal characters"
END SELECT
PRINT
PRINT "Aborting"
PRINT
PRINT "Press SPACE to continue"
DO: press$ = INKEY$: LOOP UNTIL press$ = CHR$(32)
END
RESUME

REM $STATIC
SUB check
IF map(guy.x, guy.y) = 3 THEN
IF cusplay = 0 THEN
   resetgame
  level = level + 1
  CALL loadmap(level, "")
ELSE
  CALL menu("GameMode  Mapeditor DirectionsCredits   Close     ", 5, 1, "Ascii Block Game Ver." + STR$(ver), 80, 50)
END IF
END IF
END SUB

SUB credits
CLS
PRINT "---Credits"
PRINT
PRINT "Game by whitetiger0990"
PRINT "Game based on Block Game made by Diroga"
PRINT
PRINT
PRINT "Press SPACE to continue"
DO: press$ = INKEY$: LOOP UNTIL press$ = CHR$(32)
CALL menu("GameMode  Mapeditor DirectionsCredits   Close     ", 5, 1, "Ascii Block Game Ver." + STR$(ver), 80, 50)
END SUB

SUB customlvl
CLS
PRINT "000 to go back"
PRINT "Name of custom level?"
INPUT name$
IF LTRIM$(RTRIM$(name$)) = "" THEN customlvl
IF name$ = "000" THEN CALL menu("Normal ModeLoad CustomGoBack     ", 3, 2, "Ascii Block Game Ver." + STR$(ver), 80, 50)
CLS
CALL loadmap(0, name$)
CALL game
END SUB

SUB directions
CLS
PRINT "---Ascii Block Game"
PRINT
PRINT "> - This is you"
PRINT
PRINT "> - You facing left"
PRINT "< - You facing right"
PRINT
PRINT CHR$(219); " - This is an impassable wall"
PRINT "B - This is a block"
PRINT "     You pick it up by facing it and pressing up arrow key"
PRINT "     You drop it in front of you with the down arrow key"
PRINT "F - This is the finish. The goal is to get to it."
PRINT
PRINT "You move left with the left arrow key"
PRINT "You move right with the right arrow key"
PRINT "You move up blocks or walls by walking into them"
PRINT
PRINT "To toggle debug press D"
PRINT "To pause press P"
PRINT "To restart the level press R"
DO: press$ = INKEY$: LOOP UNTIL press$ <> ""
CALL menu("GameMode  Mapeditor DirectionsCredits   Close     ", 5, 1, "Ascii Block Game Ver." + STR$(ver), 80, 50)
END SUB

SUB game
loadscr
DO
readkeys
check
LOOP
END SUB

SUB leftright
IF map(guy.x + guy.facing, guy.y) = 1 OR map(guy.x + guy.facing, guy.y) = 2 THEN
IF map(guy.x + guy.facing, guy.y - 1) <> 1 AND map(guy.x + guy.facing, guy.y - 1) <> 2 THEN
  IF map(guy.x, guy.y - 1) <> 1 THEN
   IF guy.got = 1 THEN
    IF map(guy.x + guy.facing, guy.y - 2) <> 1 AND map(guy.x + guy.facing, guy.y - 2) <> 3 THEN
     map(guy.x, guy.y - 1) = 0
     map(guy.x + guy.facing, guy.y - 2) = 2
    ELSE
     abort = 1
    END IF
   END IF
  IF abort = 0 THEN guy.x = guy.x + guy.facing: guy.y = guy.y - 1: loadscr
  END IF
END IF
ELSE
IF guy.got = 1 THEN
IF map(guy.x + guy.facing, guy.y - 1) <> 1 AND map(guy.x + guy.facing, guy.y - 1) <> 2 AND map(guy.x + guy.facing, guy.y - 1) <> 3 THEN
   map(guy.x, guy.y - 1) = 0: map(guy.x + guy.facing, guy.y - 1) = 2
ELSE
  abort = 1
END IF
END IF
IF abort = 0 THEN guy.x = guy.x + guy.facing: loadscr
END IF
END SUB

SUB loadmap (number, name$)
resetgame
filename$ = mappath$ + LTRIM$(STR$(number)) + ".txt"
IF number = 0 THEN filename$ = cuspath$ + ".txt": cusplay = 1
CLS
PRINT "Loading map..."
OPEN filename$ FOR BINARY AS #1
IF LOF(1) = 0 THEN
CLOSE #1
KILL filename$
CLS
IF cusplay = 0 THEN
PRINT "Next level not available"
ELSE
PRINT "Level not found"
END IF
PRINT "Press SPACE to continue"
DO: press$ = INKEY$: LOOP UNTIL press$ = CHR$(32)
CLS
IF cusplay = 0 THEN CALL menu("GameModeMapeditor DirectionsCredits   Close", 5, 1, "Ascii Block Game Ver." + STR$(ver), 80, 50) ELSE customlvl
ELSE
CLOSE #1
OPEN filename$ FOR INPUT AS #1
END IF
INPUT #1, guy.x, guy.y, guy.facing, par
FOR y = 1 TO maph
FOR x = 1 TO mapw
INPUT #1, map(x, y)
NEXT x
NEXT y

CLOSE
game
END SUB

SUB loadscr
WAIT &H3DA, 8
WAIT &H3DA, 8, 8
FOR y = 1 TO maph
FOR x = 1 TO mapw
t = TIMER
LOCATE y, x
SELECT CASE map(x, y)
CASE 0: PRINT " "
CASE 1: PRINT CHR$(219)
CASE 2: PRINT "B"
CASE 3: PRINT "F"
END SELECT
NEXT x
NEXT y
LOCATE guy.y, guy.x
SELECT CASE guy.facing
CASE -1: PRINT ">"
CASE 1: PRINT "<"
END SELECT
FOR i = maph + 2 TO 47
LOCATE i, 1: PRINT SPACE$(45)
NEXT
LOCATE maph + 3, 1
PRINT "--Stats"
IF cusplay = 0 THEN
PRINT "Level:          "; level
ELSE
PRINT "Level:           "; name$
END IF
PRINT "Carrying block: "; guy.got
PRINT "Total Drops:    "; drops
PRINT "Par:            "; par
PRINT
IF debug THEN
PRINT "--Debug Mode"
PRINT "Row:            "; guy.x
PRINT "Col:            "; guy.y
PRINT "Facing:         "; guy.facing
PRINT
PRINT map(guy.x - 1, guy.y - 1); map(guy.x, guy.y - 1); map(guy.x + 1, guy.y - 1)
PRINT map(guy.x - 1, guy.y); " * "; map(guy.x + 1, guy.y)
PRINT map(guy.x - 1, guy.y + 1); map(guy.x, guy.y + 1); map(guy.x + 1, guy.y + 1)
END IF
END SUB

SUB mapeditor
CLS
REDIM newmap(1 TO mapw, 1 TO maph)
'FOR y = 1 TO maph
' FOR x = 1 TO mapw
'  newmap(x, y) = 0
' NEXT x
'NEXT y
x = 1
y = 1

DO
press$ = INKEY$
SELECT CASE press$
  CASE CHR$(0) + CHR$(77): x = x + 1
  CASE CHR$(0) + CHR$(75): x = x - 1
  CASE CHR$(0) + CHR$(80): y = y + 1
  CASE CHR$(0) + CHR$(72): y = y - 1
END SELECT
SELECT CASE LCASE$(press$)
  CASE "1": newmap(x, y) = 0
  CASE "2": newmap(x, y) = 1
  CASE "3": newmap(x, y) = 2
  CASE "4": newmap(x, y) = 3
  CASE "i": GOSUB instructions
  CASE "s": GOSUB savemap
  CASE "o": GOSUB openmap
  CASE "p":
   LOCATE 22, 1
   INPUT "What do you want to set the par at? ", spar$
   spar = VAL(spar$)
   IF spar > 10 THEN PRINT "Par cannot go above 10 - par set at 10"
   CLS
  CASE "f":
   DO
    LOCATE 22, 1
    PRINT "Which way do you want to face?"
    INPUT "-1 for Left -- 1 for Right ", sface$
    sface = VAL(sface$)
   LOOP UNTIL sface = 1 OR sface = -1
   CLS
  CASE CHR$(32): sx = x: sy = y: startset = 1
  CASE CHR$(27): CALL menu("GameMode  Mapeditor DirectionsCredits   Close     ", 5, 1, "Ascii Block Game Ver." + STR$(ver), 80, 50)
END SELECT
IF x < 1 THEN x = 1
IF x > mapw THEN x = mapw
IF y < 1 THEN y = 1
IF y > maph THEN y = maph
FOR y1 = 1 TO maph
  FOR x1 = 1 TO mapw
   IF x1 = x AND y1 = y THEN COLOR 4 ELSE COLOR 15
   LOCATE y1, x1
   SELECT CASE newmap(x1, y1)
    CASE 0: PRINT "."
    CASE 1: PRINT CHR$(219)
    CASE 2: PRINT "B"
    CASE 3: PRINT "F"
   END SELECT
   COLOR 15
   LOCATE 30, 1: PRINT "CurPos -"; x, y
   PRINT "StartPos-"; sx, sy
   PRINT "[P]ar- "; spar; " "
   PRINT "[F]acing- "; sface
   PRINT "Press I to see the instructions"
  NEXT x1
NEXT y1
oldy = y: oldx = x
LOOP

savemap:
CLS
PRINT "Type 000 to go back"
INPUT "What name do you want to save the map as? ", lvlname$
IF lvlname$ = "000" THEN startset = 1: CLS : RETURN
filename$ = path$ + "maps\custom\" + lvlname$ + ".txt"

OPEN filename$ FOR OUTPUT AS #1

PRINT
PRINT #1, LTRIM$(STR$(sx)) + "," + LTRIM$(STR$(sy)) + "," + LTRIM$(STR$(sface)) + "," + LTRIM$(STR$(spar))
FOR y1 = 1 TO maph
FOR x1 = 1 TO mapw
  IF x1 < mapw THEN
   word$ = LTRIM$(STR$(newmap(x1, y1))) + ","
  ELSE
   word$ = LTRIM$(STR$(newmap(x1, y1)))
  END IF
  PRINT #1, word$;
NEXT x1
PRINT #1,
NEXT y1
CLOSE
CLS
RETURN

openmap:
CLS
PRINT "Type 000 to go back"
INPUT "What is the name of the map? ", lvlname$
IF lvlname$ = "000" THEN startset = 1: CLS : RETURN

filename$ = path$ + "maps\custom\" + lvlname$ + ".txt"
OPEN filename$ FOR BINARY AS #1
IF LOF(1) = 0 THEN
CLOSE #1
KILL filename$
CLS
PRINT "Level not found"
PRINT "Press SPACE to continue"
DO: press$ = INKEY$: LOOP UNTIL press$ = CHR$(32)
CLS
RETURN
ELSE
CLOSE #1
OPEN filename$ FOR INPUT AS #1
END IF
        
INPUT #1, sx, sy, sface, spar
FOR y2 = 1 TO maph
FOR x2 = 1 TO mapw
  INPUT #1, newmap(x2, y2)
NEXT x2
NEXT y2
CLOSE
CLS
RETURN

instructions:
CLS
PRINT "Press - Result"
PRINT "1 - Places Blank spot (shows up as a [.])"
PRINT "2 - Places Wall (shows up as a [" + CHR$(219) + "])"
PRINT "3 - Places Block (shows up as a [B])"
PRINT "4 - Places Finish (shows up as a [F])"
PRINT "S - Save"
PRINT "O - Open"
PRINT "P - Set par"
PRINT "F - Set which way you start or facing"
PRINT "Space bar - Set start position"
PRINT "ESC - Return to menu without saving (save before this)"
DO: press$ = INKEY$: LOOP UNTIL press$ <> ""
CLS
RETURN
END SUB

SUB menu (data$, numofitems, mode, detail$, sw, sh)
CLS
ub = numofitems
lb = 1
selected = 1
COLOR 15
LOCATE sh / 2 - ub / 2 - lb + 1 - 1, (sw / 2) - (LEN(detail$) / 2)
PRINT detail$
DO
FOR i = lb TO ub
  word$ = CHR$(219) + LTRIM$(STR$(i)) + ") " + MID$(data$, (i - 1) * (LEN(data$) / ub) + 1, LEN(data$) / ub) + " " + CHR$(219)
  LOCATE sh / 2 - INT(ub / 2 + .5) + i, (sw / 2) - (LEN(word$) / 2)

'Change the select color here
  IF selected = i THEN COLOR 4 ELSE COLOR 15
  PRINT word$
  NEXT
  press$ = INKEY$
  SELECT CASE press$
   CASE CHR$(0) + CHR$(72): selected = selected - 1
   CASE CHR$(0) + CHR$(80): selected = selected + 1
   CASE CHR$(13)
'\/\/\/\/Multiple modes for multiple menus
    SELECT CASE mode
     CASE 1:
'\/\/\/\/Here you change what each links do (like call a sub)
     SELECT CASE selected
      CASE 1: CALL menu("Normal ModeLoad CustomGoBack     ", 3, 2, "Ascii Block Game Ver." + STR$(ver), 80, 50)
      CASE 2: COLOR 15: mapeditor
      CASE 3: directions
      CASE 4: COLOR 15: credits
      CASE 5: COLOR 15: END
     END SELECT
     CASE 2:
      SELECT CASE selected
       CASE 1: COLOR 15: level = 1: CLS : CALL loadmap(level, ""): game
       CASE 2: COLOR 15: customlvl
       CASE 3: CALL menu("GameMode  Mapeditor DirectionsCredits   Close     ", 5, 1, "Ascii Block Game Ver." + STR$(ver), 80, 50)
      END SELECT
    END SELECT
  END SELECT
IF selected < lb THEN selected = ub
IF selected > ub THEN selected = lb
LOOP
END SUB

SUB readkeys
'guy.oldx = guy.x
'guy.oldy = guy.y

press$ = INKEY$

SELECT CASE press$
CASE CHR$(0) + CHR$(72) 'up/life
IF guy.got = 0 THEN
IF map(guy.x + guy.facing, guy.y) = 2 THEN
  IF map(guy.x + guy.facing, guy.y - 1) <> 2 THEN
   IF map(guy.x, guy.y - 1) <> 1 THEN
    IF map(guy.x, guy.y - 1) <> 2 THEN
     IF map(guy.x + guy.facing, guy.y - 1) <> 1 THEN
      IF map(guy.x + guy.facing, guy.y - 1) <> 2 THEN
       guy.got = 1
       map(guy.x + guy.facing, guy.y) = 0
       map(guy.x, guy.y - 1) = 2
       loadscr
      END IF
     END IF
    END IF
   END IF
  END IF
END IF
END IF

CASE CHR$(0) + CHR$(80) 'down/drop
IF guy.got = 1 THEN
IF map(guy.x + guy.facing, guy.y) <> 2 THEN
  IF map(guy.x + guy.facing, guy.y) <> 1 THEN
   IF map(guy.x + guy.facing, guy.y - 1) <> 1 THEN
    IF map(guy.x + guy.facing, guy.y - 1) <> 2 THEN
     guy.got = 0
     drops = drops + 1
     tmpbx = guy.x: tmpby = guy.y - 1
     map(tmpbx, tmpby) = 0
     tmpbx = tmpbx + guy.facing: tmpby = tmpby + 1
     map(tmpbx, tmpby) = 2
     putontop = 0
    END IF
   END IF
   ELSE putontop = 1
  END IF
ELSE putontop = 1
END IF
IF putontop = 1 THEN
IF map(guy.x + guy.facing, guy.y - 1) <> 1 THEN
  IF map(guy.x + guy.facing, guy.y - 1) <> 2 THEN
   putontop = 0
   guy.got = 0
   drops = drops + 1
   tmpbx = guy.x: tmpby = guy.y - 1
   map(tmpbx, tmpby) = 0
   tmpbx = tmpbx + guy.facing: tmpby = tmpby
   map(tmpbx, tmpby) = 2
  END IF
END IF
END IF
IF guy.got = 0 THEN
IF tmpbx <> 0 AND tmpby <> 0 THEN
  IF map(tmpbx, tmpby + 1) <> 2 AND map(tmpbx, tmpby + 1) <> 1 THEN
DO
IF map(tmpbx, tmpby + 1) <> 1 THEN
IF map(tmpbx, tmpby) + 1 <> 2 THEN
  tmpby = tmpby + 1
  map(tmpbx, tmpby - 1) = 0
  map(tmpbx, tmpby) = 2
END IF
END IF
LOOP UNTIL map(tmpbx, tmpby + 1) = 1 OR map(tmpbx, tmpby + 1) = 2
END IF
loadscr
END IF
END IF
END IF
'/\/\/
CASE CHR$(0) + CHR$(75) 'left
IF guy.facing = 1 THEN guy.facing = -1: loadscr ELSE leftright
'guy.facing = -1
'leftright

CASE CHR$(0) + CHR$(77) 'Right
IF guy.facing = -1 THEN guy.facing = 1: loadscr ELSE leftright
'guy.facing = 1
'leftright

CASE CHR$(27): level = 1: CALL menu("GameMode  Mapeditor DirectionsCredits   Close     ", 5, 1, "Ascii Block Game Ver." + STR$(ver), 80, 50)
END SELECT
SELECT CASE LCASE$(press$)
CASE "d": debug = debug XOR 1: loadscr
CASE "r":
  CLS
  PRINT "To hard for ya? :)"
  INPUT "Restart y/n - ", choice$
   IF LCASE$(choice$) = "y" THEN
    IF cusplay <> 1 THEN CALL loadmap(level, "") ELSE CALL loadmap(0, name$)
   END IF
   CLS
   loadscr
  CASE "p"
   LOCATE maph + 2, 1: PRINT "Paused"
   DO: press$ = INKEY$: LOOP UNTIL LCASE$(press$) = "p"
   press$ = ""
   LOCATE maph + 2, 1: PRINT "      "
END SELECT
DO
IF map(guy.x, guy.y + 1) <> 1 THEN
IF map(guy.x, guy.y + 1) <> 2 THEN
  guy.y = guy.y + 1
  IF guy.got = 1 THEN
   map(guy.x, guy.y - 2) = 0
   map(guy.x, guy.y - 1) = 2
  END IF
  loadscr
END IF
END IF
check
LOOP UNTIL map(guy.x, guy.y + 1) = 1 OR map(guy.x, guy.y + 1) = 2
abort = 0
END SUB

SUB resetgame
guy.got = 0
drops = 0
ver = 9
debug = 0
maph = 20
mapw = 30
drops = 0
par = 0
cusplay = 0
path$ = "E:\program\disk2\block\"
mappath$ = path$ + "maps\map"
cuspath$ = path$ + "maps\custom\"
END SUB
not because you have too many subs: when you use gosubs, try to use returns. when you call subs, try to exit the sub when your finished. becasue when you call too many subs/gosubs, you use up stack space, until you finally use it all up.
Check: FRE(-2)
doesn't that show you the amount of free stack space remaining? and fre(-1) is the amount of conventional memory remaining, right?
@barok: I know. but I kindof need help with that


@neo: how can I use that?
it can't help you in any way except tell you how much stack space you have left. it won't solve your problem.

geez, i told you how to solve your problem! make sure you exit subs your in! and return on gosubs! when your finished with the code you want to run in a gosub, use a return statment. make sure when your finished with a sub to exit it. if you want people to go through your program and fix it, your sadly mistaken.


oh, and you use a bunch of if statments... they can be made a lot neater if you use select case. easier to make, but i guess not as fast.
Quote:@neo: how can I use that?

PRINT FRE(-2)

This tells you how much stack space you have left. If your program has a lot of recursive functions, or has an error that makes subs loop indefinately, This will help you know how much space is being used up.
ok.... if I use that I know how much i have left.... how do i use that to my advantage? =\
You can tell where the program is losing it's stack space. Its likely to be an error in your program which makes the flow keep looping inside itself. I mean like if you call a sub that calls itself. That will go on indefintely, until you run out of stack space.
To help you understand a little why you run out off stack space, I can tell you that everytime you're in a sub, and call another one, all the current variables (dimed and not dimed) will go onto the stack, and then when you return to that sub, they will be taken back out. So if you call many subs from withing subs, and they contain many vars, you'll run out pretty quick.

Btw also arguments will be saved onto the stack, so instead of passing an array as an argument, make it SHARED. You can also make some kinda Finite State Machine so instead of this:

Code:
'main:
do
...  
sub1
loop

'sub1:
...
sub2

'sub2:
...
sub3

Try this:

Code:
'main:
state=1
do
...
if state=1 -> sub1
if state=2 -> sub2
if state=3 -> sub3
loop

'sub1
...

'sub2
...

I hope you get the idea. I dunno if that's even a FSM hehe...
Pages: 1 2