LoginLogin
Nintendo shutting down 3DS + Wii U online services, see our post

Division by 0 error in my program

Root / Programming Questions / [.]

mikl_plsCreated:
Hey everyone. This is my first post here. I'm trying to recreate a "3D starfield" program I downloaded for QBASIC a long time ago to SmileBASIC and am having some issues with doing so. I would greatly appreciate some help if anyone is able to lend some advice.
DIM STARAMT, X, DEPTH, ZOOM

PRINT "SIMPLE STARFIELD SIMULATOR"
PRINT "--------------------------"
PRINT
PRINT "By the RAZE, Feb 2000"
PRINT "Converted to SmileBASIC by Michael Wood 5/6/2019"
PRINT
PRINT "Press any button to continue."
REPEAT
UNTIL BUTTON(3)

CLS
INPUT "   Amount of stars (default: 30): ", STARAMT
INPUT "             Depth (default: 40): ", DEPTH
INPUT "             Zoom (default: 200): ", ZOOM
IF STARAMT==0 THEN STARAMT = 30
IF DEPTH==0 THEN DEPTH = 40
IF ZOOM==0 THEN ZOOM = 200

DIM STARPOS[STARAMT]
DIM STARX[STARAMT], STARY[STARAMT], STARZ[STARAMT]

FOR X = 0 TO STARAMT - 1
GOSUB @DEFPOS
NEXT

COL = 0

@LOOP
FOR X = 0 TO STARAMT-1
X3D = STARX[X]
Y3D = STARY[X]
Z3D = STARZ[X]
COL = 255
GOSUB @XYZLOC
NEXT

VSYNC 1

FOR X = 0 TO STARAMT-1
GCLS
STARZ[X] = STARZ[X] - 1
IF STARPOS[X]==1 THEN GOSOUB @DEFPOS: STARPOS[X] = 0
NEXT
GOTO @LOOP

@DEFPOS
STARX[X] = RND(30)
STARY[X] = RND(30)
IF STARX[X] == 0 THEN STARX[X] = 1
IF STARY[X] == 0 THEN STARY[X] = 1
'OTHERWISE STAR COMES STRAIGHT AHEAD AND CAUSES AN ERROR
IF STARPOS[X] <= 0 THEN
STARZ[X] = RND(50)+1
ELSEIF STARPOS[X] != 0 THEN
STARZ[X] = 50
ENDIF
RETURN

@XYZLOC
'BELOW ARE THE TWO ESSENTIAL 3D PLOTTING LINES
'FINDS THE X LOCATION ON SCREEN
X2D = ZOOM * (X3D / (Z3D + DEPTH)) + 200
Y2D = ZOOM * (Y3D / (Z3D + DEPTH)) + 120

IF X2D < 0 OR X2D > 399 OR Y2D < 0 OR Y2D > 239 THEN
STARPOS[X] = 1
RETURN
ENDIF
IF COL == 0 THEN
GPSET X2D, Y2D, RGB(COL,COL,COL)
RETURN
ENDIF
C = 255 * Z3D
COL = SQR(C) 'PROBLEM LINE
IF Z3D > 0 THEN
GPSET X2D,Y2D,RGB(COL,COL,COL)
RETURN
ENDIF

GPSET X2D,Y2D,RGB(COL,COL,COL)
RETURN

Just make sure C is not negative, C=MAX(255*Z3D,0)

What line number or function is stated in the error message? That would help more.

Z3D can be a negative number, and Square Root of a negative number is undefined in SmileBasic. You also have a divide by zero error if Z3D + DEPTH == 0. There is redundant pixel plotting in @XYZLOC, and you should be using functions and not label loops/gosub as well. I would also kick the default star amount up to 300, SmileBasic can handle it and it looks a lot better. Basically, I think your code should look like this:
DIM STARAMT, X, DEPTH, ZOOM

PRINT "SIMPLE STARFIELD SIMULATOR"
PRINT "--------------------------"
PRINT
PRINT "By the RAZE, Feb 2000"
PRINT "Converted to SmileBASIC by Michael Wood 5/6/2019"
PRINT
PRINT "Press any button to continue."
REPEAT
 VSYNC 'Don't waste battery life, add a VSYNC
UNTIL BUTTON(3)

CLS
INPUT "   Amount of stars (default: 300): ", STARAMT
INPUT "              Depth (default: 40): ", DEPTH
INPUT "              Zoom (default: 200): ", ZOOM
CLS 'Another CLS so the input text doesn't linger on screen.
IF STARAMT==0 THEN STARAMT = 300
IF DEPTH==0 THEN DEPTH = 40
IF ZOOM==0 THEN ZOOM = 200

DIM STARPOS[STARAMT]
DIM STARX[STARAMT], STARY[STARAMT], STARZ[STARAMT]

'I don't like that we are using X as an index counter, but I am going to leave it as-is. To me x should be for a x,y location by default.
FOR X = 0 TO STARAMT - 1
 DEFPOS X
NEXT X

WHILE TRUE 'Using a while loop instead of a label loop.
 FOR X = 0 TO STARAMT-1
  PLOTXYZ X, STARX[X], STARY[X], STARZ[X]
 NEXT X

 VSYNC 1
 GCLS 'Moved this out of the FOR loop it was causing only one star to appear and was a bug.

 FOR X = 0 TO STARAMT-1
  STARZ[X] = STARZ[X] - 1
  IF STARPOS[X]==1 THEN 
   DEFPOS X
   STARPOS[X] = 0 
  ENDIF
 NEXT X

 IF (BUTTON(3) AND #B) != 0 THEN BREAK
WEND

DEF DEFPOS I
 REPEAT 'Repeat loop makes sure you aren't generating a 0,0
  'This was only drawing in the bottom left quadrant we need X,Y to have both positive and negative values. I also increased the range a bit.
  STARX[X] = RND(100) - 50
  STARY[X] = RND(100) - 50
 UNTIL STARX[X] != 0 && STARY[X] != 0
 '0, 0 IS DISALLOWED OTHERWISE STAR COMES STRAIGHT AHEAD AND CAUSES AN ERROR
 IF STARPOS[X] <= 0 THEN
  STARZ[X] = RND(50)+1
 ELSEIF STARPOS[X] != 0 THEN 'The STARPOS[X] != 0 Doesn't really do anything, left it to keep the code looking the same
  STARZ[X] = 50
 ENDIF
END

'X, Y, and Z are local variables in a function. I don't like mutating globals without a reason. This lets me use shorter names too.
DEF PLOTXYZ I, X, Y, Z
 VAR SCREEN_W = 400, SCREEN_H = 240
 VAR HALF_W = SCREEN_W / 2
 VAR HALF_H = SCREEN_H / 2
 VAR X2D, Y2D 'No longer global variables

 'BELOW ARE THE TWO ESSENTIAL 3D PLOTTING LINES
 'FINDS THE X LOCATION ON SCREEN
 IF Z + DEPTH != 0 THEN
  X2D = ZOOM * (X / (Z + DEPTH)) + HALF_W 'Using variables with descriptive names to explain what the code is doing. Magic numbers are bad
  Y2D = ZOOM * (Y / (Z + DEPTH)) + HALF_H
 ELSE
  X2D = 0 'Here I am using magic numbers right after saying they are bad. I figure 0 is a good replacement for a divide by zero error
  Y2D = 0
 ENDIF

 IF X2D < 0 OR X2D >= SCREEN_W OR Y2D < 0 OR Y2D >= SCREEN_H THEN
  STARPOS[I] = 1
  RETURN
 ENDIF

 'SQUARE ROOT OF A NEGATIVE NUMBER THROWS
 'AN ERROR
 IF Z > 0 THEN
  COL = SQR(255 * Z)
 ELSE
  COL = 255 'WHITE
 ENDIF
 
 GPSET X2D, Y2D, RGB(COL,COL,COL)
END
I tried to comment my changes, but feel free to ask any questions you may have. I didn't copy it over with petite modem so let me know of any omissions or type-os too.

Just make sure C is not negative, C=MAX(255*Z3D,0)
Thank you!
What line number or function is stated in the error message? That would help more.
It's line #76.
Z3D can be a negative number, and Square Root of a negative number is undefined in SmileBasic. You also have a divide by zero error if Z3D + DEPTH == 0. There is redundant pixel plotting in @XYZLOC, and you should be using functions and not label loops/gosub as well. I would also kick the default star amount up to 300, SmileBasic can handle it and it looks a lot better. Basically, I think your code should look like this:
DIM STARAMT, X, DEPTH, ZOOM

PRINT "SIMPLE STARFIELD SIMULATOR"
PRINT "--------------------------"
PRINT
PRINT "By the RAZE, Feb 2000"
PRINT "Converted to SmileBASIC by Michael Wood 5/6/2019"
PRINT
PRINT "Press any button to continue."
REPEAT
 VSYNC 'Don't waste battery life, add a VSYNC
UNTIL BUTTON(3)

CLS
INPUT "   Amount of stars (default: 300): ", STARAMT
INPUT "              Depth (default: 40): ", DEPTH
INPUT "              Zoom (default: 200): ", ZOOM
CLS 'Another CLS so the input text doesn't linger on screen.
IF STARAMT==0 THEN STARAMT = 300
IF DEPTH==0 THEN DEPTH = 40
IF ZOOM==0 THEN ZOOM = 200

DIM STARPOS[STARAMT]
DIM STARX[STARAMT], STARY[STARAMT], STARZ[STARAMT]

'I don't like that we are using X as an index counter, but I am going to leave it as-is. To me x should be for a x,y location by default.
FOR X = 0 TO STARAMT - 1
 DEFPOS X
NEXT X

WHILE TRUE 'Using a while loop instead of a label loop.
 FOR X = 0 TO STARAMT-1
  PLOTXYZ X, STARX[X], STARY[X], STARZ[X]
 NEXT X

 VSYNC 1
 GCLS 'Moved this out of the FOR loop it was causing only one star to appear and was a bug.

 FOR X = 0 TO STARAMT-1
  STARZ[X] = STARZ[X] - 1
  IF STARPOS[X]==1 THEN 
   DEFPOS X
   STARPOS[X] = 0 
  ENDIF
 NEXT X

 IF (BUTTON(3) AND #B) != 0 THEN BREAK
WEND

DEF DEFPOS I
 REPEAT 'Repeat loop makes sure you aren't generating a 0,0
  'This was only drawing in the bottom left quadrant we need X,Y to have both positive and negative values. I also increased the range a bit.
  STARX[X] = RND(100) - 50
  STARY[X] = RND(100) - 50
 UNTIL STARX[X] != 0 && STARY[X] != 0
 '0, 0 IS DISALLOWED OTHERWISE STAR COMES STRAIGHT AHEAD AND CAUSES AN ERROR
 IF STARPOS[X] <= 0 THEN
  STARZ[X] = RND(50)+1
 ELSEIF STARPOS[X] != 0 THEN 'The STARPOS[X] != 0 Doesn't really do anything, left it to keep the code looking the same
  STARZ[X] = 50
 ENDIF
END

'X, Y, and Z are local variables in a function. I don't like mutating globals without a reason. This lets me use shorter names too.
DEF PLOTXYZ I, X, Y, Z
 VAR SCREEN_W = 400, SCREEN_H = 240
 VAR HALF_W = SCREEN_W / 2
 VAR HALF_H = SCREEN_H / 2
 VAR X2D, Y2D 'No longer global variables

 'BELOW ARE THE TWO ESSENTIAL 3D PLOTTING LINES
 'FINDS THE X LOCATION ON SCREEN
 IF Z + DEPTH != 0 THEN
  X2D = ZOOM * (X / (Z + DEPTH)) + HALF_W 'Using variables with descriptive names to explain what the code is doing. Magic numbers are bad
  Y2D = ZOOM * (Y / (Z + DEPTH)) + HALF_H
 ELSE
  X2D = 0 'Here I am using magic numbers right after saying they are bad. I figure 0 is a good replacement for a divide by zero error
  Y2D = 0
 ENDIF

 IF X2D < 0 OR X2D >= SCREEN_W OR Y2D < 0 OR Y2D >= SCREEN_H THEN
  STARPOS[I] = 1
  RETURN
 ENDIF

 'SQUARE ROOT OF A NEGATIVE NUMBER THROWS
 'AN ERROR
 IF Z > 0 THEN
  COL = SQR(255 * Z)
 ELSE
  COL = 255 'WHITE
 ENDIF
 
 GPSET X2D, Y2D, RGB(COL,COL,COL)
END
I tried to comment my changes, but feel free to ask any questions you may have. I didn't copy it over with petite modem so let me know of any omissions or type-os too.
OMG THANK YOU SO MUCH!! I'm very new to SmileBASIC, so all of y'all's help is MUCH APPRECIATED!! :D

Well, I tried all the recommendations, and it still doesn't work. I had to fix a few things (like using x as an index counter) for the program to even work. Now I get subscript out of range errors even though it says "STARAMT - 1" (still going to 300). I think I'm going to call this program a lost cause lol. I REALLY appreciate everyone's help and contributions to this!

I uploaded my version of the code, here is the key: KY4D3N3. Let me know when you have it so I can reuse the key space.

I uploaded my version of the code, here is the key: KY4D3N3. Let me know when you have it so I can reuse the key space.
I have it! YEEESSS! Thank you so much! I will have to study the code to learn what you did differently.