Array subscripts
Author |
Message |
Clayton
|
Posted: Sun Aug 13, 2006 1:50 pm Post subject: Array subscripts |
|
|
right now i am working on a game (see my post in [Turing Source Code]) and im having a problem with getting rid of any balls that have been clicked on. I am using classes and i have an array of my type "bouncy", in it is the instance field (pointer to my ball class) and a point_taken field (dont worry about it), basically what i am trying to do is have a ball disappear when a ball is clicked, then free the pointer reference to that object, thus saving memory (as i dont need that ball anymore), then resize my array to one less element (having pushed the ball to be "deleted" to the upper bounds of the array) then carry on with the rest of the program, however i am having trouble in my "delete_ball" procedure, i get the error "Array subscript is out of range" when i try to "push" the ball to be deleted to the upper bounds of the array. I dont know why i am getting this error, and it would be appreciated if you guys could look at my code and see if you can figure out why its happening, thx in advance for your help
Turing: |
type bouncy :
record
instance : ^balls
points_taken : boolean
end record
var ball : flexible array 1 .. 0 of bouncy
procedure delete_ball (object_to_be_deleted : int)
var temp : bouncy
for lp : object_to_be_deleted .. upper (ball )
temp := ball (lp )
%-------ERROR-------%
ball (lp ) := ball (lp + 1)
%-------ARRAY SUBSCRIPT IS OUT OF RANGE-------%
ball (lp + 1) := temp
end for
free balls, ball (upper (ball )).instance
new ball, upper (ball ) - 1
end delete_ball
%down here in my main loop i run a check to see if the ball is clicked, if it
%is i run this proc, but i keep getting that error...
|
|
|
|
|
|
|
Sponsor Sponsor
|
|
|
Cervantes
|
Posted: Sun Aug 13, 2006 4:39 pm Post subject: (No subject) |
|
|
'lp' reaches a max value of `upper (ball)'. So `lp +1', which is the subscript in the line producing the error, ranges to `upper (ball) + 1', which is out of range of the array.
Solution: modify the range of your for loop to only reach `upper (ball) - 1'. |
|
|
|
|
|
Clayton
|
Posted: Sun Aug 13, 2006 5:15 pm Post subject: (No subject) |
|
|
aha thank you very much Cervantes, that was a bit of an oopsie on my part, i thought i had put upper(ball)-1 oh well |
|
|
|
|
|
Clayton
|
Posted: Sun Aug 13, 2006 5:57 pm Post subject: (No subject) |
|
|
ok now im having another problem, the delete_ball proc is working fine, however, after you click two or three balls (sometimes even the first) Turing crashes with an Array subscript is out of range error, here is my code so far :
code: |
class balls
import Draw, Mouse
export initialize, move, draw, draw_
var size, x, y, colur : int
var x_vel, y_vel : int
var draw_ : boolean := true
proc initialize (x_, y_, size_, color_, xVel, yVel : int)
x := x_
y := y_
size := size_
x_vel := xVel
y_vel := yVel
if color_ >= 1 and color_ <= maxcolor then
colur := color_
else
colur := brightred
end if
end initialize
proc move
x += x_vel
y += y_vel
if x + size >= maxx then
x := maxx - size
x_vel *= -1
end if
if x - size <= 0 then
x := size
x_vel *= -1
end if
if y + size >= maxy then
y := maxy - size
y_vel *= -1
end if
if y - size <= 0 then
y := size
y_vel *= -1
end if
end move
function check_clicked : boolean
%check to see if any of the ball have been clicked
var mx, my, mb : int
Mouse.ButtonChoose ("multibutton")
if Mouse.ButtonMoved ("updown") then
Mouse.Where (mx, my, mb)
if mx > x - size and mx < x + size and my > y - size and my < y + size and mb >= 1 then
result true
end if
end if
result false
end check_clicked
proc draw
if check_clicked then
draw_ := false
end if
if draw_ then
Draw.FillOval (x, y, size, size, colur)
end if
end draw
end balls
%-----------------------------------------------------------------------------------------------
View.Set ("offscreenonly,graphics")
type bouncy :
record
instance : ^balls
points_taken : boolean
end record
var ball : flexible array 1 .. 0 of bouncy
var mouse_x, mouse_y, mouse_buttons : int
var points, ball_amount : int := 0
var keys : array char of boolean
const ball_color : int := brightred
const min_size : int := 10
const max_size : int := 20
const timer : int := 60000 %60 seconds (60 * 1000 ;) )
var tim : int := Time.Elapsed
var tim2 : int := Time.Elapsed - timer
%----------------------------------------------------------------------------------------------
procedure lose
var font : int := Font.New ("times new roman:20:bold")
Draw.Cls
Font.Draw ("You Lose!!", maxx div 2 - 100, maxy - 25, font, brightred)
locatexy (maxx div 2 - 100, maxy - 50)
put "You created ", upper (ball), " balls, however you had ", ball_amount, " ball(s) left"
end lose
procedure win
var font : int := Font.New ("times new roman:20:bold")
Draw.Cls
Font.Draw ("You Win!!", maxx div 2 - 150, maxy - 25, font, brightblue)
locatexy (maxx div 2 - 150, maxy - 50)
put "You created ", upper (ball), " balls and clicked all of them in 60 seconds"
locatexy (maxx div 2 - 150, maxy - 75)
put "giving you a score of ", points, " points, Congratulations."
end win
procedure delete_ball (object_to_be_deleted : int)
var temp : bouncy
for lp : object_to_be_deleted .. upper (ball) - 1
temp := ball (lp)
ball (lp) := ball (lp + 1)
ball (lp + 1) := temp
end for
free balls, ball (upper (ball)).instance
new ball, upper (ball) - 1
end delete_ball
procedure new_ball (x, y, size, colur : int)
new ball, upper (ball) + 1
new balls, ball (upper (ball)).instance
ball (upper (ball)).points_taken := false
ball (upper (ball)).instance -> initialize (x, y, size, colur, Rand.Int (1, 7), Rand.Int (1, 7))
ball_amount += 1
end new_ball
%-----------------------------------------------------------------------------------------------
%Main Program
new_ball (maxx div 2, maxy div 2, 20, ball_color)
loop
Draw.Cls
Mouse.Where (mouse_x, mouse_y, mouse_buttons)
tim2 := Time.Elapsed - timer
put "Balls: ", ball_amount, " Points: ", points, " Time: ", round ((tim - tim2) / 1000)
put upper (ball)
Input.KeyDown (keys)
%create more balls by holding down the enter key
if keys (KEY_ENTER) then
new_ball (Rand.Int (max_size, maxx - max_size), Rand.Int (max_size, maxy - max_size), Rand.Int (min_size, max_size), ball_color)
Input.Flush
end if
Time.DelaySinceLast (30)
for i : 1 .. upper (ball)
%move and draw the ball
ball (i).instance -> move
ball (i).instance -> draw
end for
for j : 1 .. upper (ball)
%the if not ball (j).instance -> draw_ is if the balls have been drawn and clicked on
%-------ERROR-------%
if not ball (j).instance -> draw_ and not ball (j).points_taken then
%-------ARRAY SUBSCRIPT IS OUT OF RANGE-------%
%do this to stop points to keep being gained every iteration of the loop
ball (j).points_taken := true
ball_amount -= 1
delete_ball (j)
points += 10
end if
end for
%timer
exit when tim - tim2 <= 0
View.Update
end loop
if ball_amount >= 1 then
lose
else
win
end if
|
i have no idea why this is happening, any help would be appreciated |
|
|
|
|
|
Cervantes
|
Posted: Mon Aug 14, 2006 7:40 am Post subject: (No subject) |
|
|
You deleted a ball from the array, so now the array has one less element. But the range doesn't change.
Let's say your array was from 1 .. 5. You delete an element, so it is now 1 .. 4. But the for loop doens't re-evaluate `upper (ball)', so it is still going 1..5. Hence the error.
You may wish to read this for some ideas. |
|
|
|
|
|
Clayton
|
Posted: Mon Aug 14, 2006 10:21 am Post subject: (No subject) |
|
|
thank you very much Cervantes, if you werent a mod id give you bits, but...
anyways on to my next question, this is my current code so far:
code: |
class balls
import Draw, Mouse
export initialize, move, draw, draw_
var size, x, y, colur : int
var x_vel, y_vel : int
var draw_ : boolean := true
proc initialize (x_, y_, size_, color_, xVel, yVel : int)
x := x_
y := y_
size := size_
x_vel := xVel
y_vel := yVel
if color_ >= 1 and color_ <= maxcolor then
colur := color_
else
colur := brightred
end if
end initialize
proc move
x += x_vel
y += y_vel
if x + size >= maxx then
x := maxx - size
x_vel *= -1
end if
if x - size <= 0 then
x := size
x_vel *= -1
end if
if y + size >= maxy then
y := maxy - size
y_vel *= -1
end if
if y - size <= 0 then
y := size
y_vel *= -1
end if
end move
function check_clicked : boolean
%check to see if any of the ball have been clicked
var mx, my, mb : int
Mouse.ButtonChoose ("multibutton")
if Mouse.ButtonMoved ("updown") then
Mouse.Where (mx, my, mb)
if mx > x - size and mx < x + size and my > y - size and my < y + size and mb >= 1 then
result true
end if
end if
result false
end check_clicked
proc draw
if check_clicked then
draw_ := false
end if
if draw_ then
Draw.FillOval (x, y, size, size, colur)
end if
end draw
end balls
%-----------------------------------------------------------------------------------------------
View.Set ("offscreenonly,graphics")
type bouncy :
record
instance : ^balls
points_taken : boolean
end record
var ball : flexible array 1 .. 0 of bouncy
var elements_to_be_removed : flexible array 1 .. 0 of int
var mouse_x, mouse_y, mouse_buttons : int
var points, ball_amount : int := 0
var keys : array char of boolean
const ball_color : int := brightblue
const min_size : int := 10
const max_size : int := 20
const timer : int := 60000 %60 seconds (60 * 1000 ;) )
var tim : int := Time.Elapsed
var tim2 : int := Time.Elapsed - timer
%----------------------------------------------------------------------------------------------
procedure lose
var font : int := Font.New ("times new roman:20:bold")
Draw.Cls
Font.Draw ("You Lose!!", maxx div 2 - 100, maxy - 25, font, brightred)
locatexy (maxx div 2 - 100, maxy - 50)
put "You created ", upper (ball), " balls, however you had ", ball_amount, " ball(s) left"
end lose
procedure win
var font : int := Font.New ("times new roman:20:bold")
Draw.Cls
Font.Draw ("You Win!!", maxx div 2 - 150, maxy - 25, font, brightblue)
locatexy (maxx div 2 - 150, maxy - 50)
put "You created ", upper (ball), " balls and clicked all of them in 60 seconds"
locatexy (maxx div 2 - 150, maxy - 75)
put "giving you a score of ", points, " points, Congratulations."
end win
procedure delete_ball (object_to_be_deleted : int)
var temp : bouncy
for lp : object_to_be_deleted .. upper (ball) - 1
temp := ball (lp)
ball (lp) := ball (lp + 1)
ball (lp + 1) := temp
end for
free balls, ball (upper (ball)).instance
new ball, upper (ball) - 1
end delete_ball
procedure new_ball (x, y, size, colur : int)
new ball, upper (ball) + 1
new balls, ball (upper (ball)).instance
ball (upper (ball)).points_taken := false
ball (upper (ball)).instance -> initialize (x, y, size, colur, Rand.Int (1, 7), Rand.Int (1, 7))
ball_amount += 1
end new_ball
%-----------------------------------------------------------------------------------------------
%Main Program
new_ball (maxx div 2, maxy div 2, 20, ball_color)
loop
Draw.Cls
Mouse.Where (mouse_x, mouse_y, mouse_buttons)
tim2 := Time.Elapsed - timer
put "Balls: ", upper (ball), " Points: ", points, " Time: ", round ((tim - tim2) / 1000)
Input.KeyDown (keys)
%create more balls by holding down the enter key
if keys (KEY_ENTER) then
new_ball (Rand.Int (max_size, maxx - max_size), Rand.Int (max_size, maxy - max_size), Rand.Int (min_size, max_size), ball_color)
ball_amount += 1
Input.Flush
end if
Time.DelaySinceLast (30)
for i : 1 .. upper (ball)
%move and draw the ball
ball (i).instance -> move
ball (i).instance -> draw
end for
new elements_to_be_removed, 0 %start an empty array
for j : 1 .. upper (ball)
%the if not ball (j).instance -> draw_ is if the balls have been drawn and clicked on
if not ball (j).instance -> draw_ and not ball (j).points_taken then
%do this to stop points to keep being gained every iteration of the loop
ball (j).points_taken := true
%used for the delete_ball procedure because it screws up if i have delete_ball(j) in here
new elements_to_be_removed, upper (elements_to_be_removed) + 1
elements_to_be_removed (upper (elements_to_be_removed)) := j %removes ball j in next for loop
points += 10
end if
end for
for i : 1 .. upper (elements_to_be_removed)
delete_ball (elements_to_be_removed (i))
end for
%timer
exit when tim - tim2 <= 0
View.Update
end loop
if ball_amount >= 1 then
lose
else
win
end if
|
now if you run that, and create a whole bunch of balls (using the enter key), you will notice that you dont have to just click on a ball, you can just hold down the button and move the mouse around and you can "click" on all of the balls, i would like to know how to fix this, i have so far tried using a combination of Mouse.ButtonMoved and Mouse.ButtonChoose but it isnt working, any help would be appreciated |
|
|
|
|
|
Cervantes
|
Posted: Mon Aug 14, 2006 11:54 am Post subject: (No subject) |
|
|
Just use a variable to keep track of the mouse buttons previous state.
So if the previous state says it the mouse button was up, and the current state says the mouse button is down, then you know the mouse was just clicked, and you should register that, removing a ball under the mouse if there is one.
On the other hand, if the previous state says the mouse button was down, and the current state says the mouse button is down, then the mouse is being held down, and this should not be registered as a click. |
|
|
|
|
|
Clayton
|
Posted: Mon Aug 14, 2006 12:07 pm Post subject: (No subject) |
|
|
so have something like this?
code: |
var button, oldbutton :int := 0
var x,y:int
Mouse.Where (x,y,button)
if button = 1 and oldbutton = 0 then
oldbutton = 1
%do stuff
elsif button = 0 and oldbutton = 1 then
oldbutton := 0
end if
|
|
|
|
|
|
|
Sponsor Sponsor
|
|
|
Clayton
|
Posted: Mon Aug 14, 2006 12:25 pm Post subject: (No subject) |
|
|
okay i got that to work, kinda. for some strange reason, doing it this way, you can only click and clear a ball in the order they appeared on the screen, i dont know why this is happening, but i think it has something to do with the way im calling my check_clicked procedure now, heres my up to date code:
code: |
class balls
import Draw, Mouse
export initialize, move, draw, draw_, check_clicked
var size, x, y, colur : int
var x_vel, y_vel : int
var draw_ : boolean := true
proc initialize (x_, y_, size_, color_, xVel, yVel : int)
x := x_
y := y_
size := size_
x_vel := xVel
y_vel := yVel
if color_ >= 1 and color_ <= maxcolor then
colur := color_
else
colur := brightred
end if
end initialize
proc move
x += x_vel
y += y_vel
if x + size >= maxx then
x := maxx - size
x_vel *= -1
end if
if x - size <= 0 then
x := size
x_vel *= -1
end if
if y + size >= maxy then
y := maxy - size
y_vel *= -1
end if
if y - size <= 0 then
y := size
y_vel *= -1
end if
end move
procedure check_clicked (mx, my, mb : int)
%check to see if the ball has been clicked
if mx > x - size and mx < x + size and my > y - size and my < y + size and mb >= 1 then
draw_ := false
else
draw_ := true
end if
end check_clicked
proc draw
if draw_ then
Draw.FillOval (x, y, size, size, colur)
end if
end draw
end balls
%-----------------------------------------------------------------------------------------------
View.Set ("offscreenonly,graphics")
type bouncy :
record
instance : ^balls
points_taken : boolean
end record
var ball : flexible array 1 .. 0 of bouncy
var elements_to_be_removed : flexible array 1 .. 0 of int
var mouse_x, mouse_y, mouse_buttons, oldbutton : int := 0
var points, ball_amount : int := 0
var keys : array char of boolean
const ball_color : int := brightblue
const min_size : int := 10
const max_size : int := 20
const timer : int := 60000 %60 seconds (60 * 1000 ;) )
var tim : int := Time.Elapsed
var tim2 : int := Time.Elapsed - timer
%----------------------------------------------------------------------------------------------
procedure lose
var font : int := Font.New ("times new roman:20:bold")
Draw.Cls
Font.Draw ("You Lose!!", maxx div 2 - 100, maxy - 25, font, brightred)
locatexy (maxx div 2 - 100, maxy - 50)
put "You created ", ball_amount, " balls, however you had ", upper (ball), " ball(s) left"
end lose
procedure win
var font : int := Font.New ("times new roman:20:bold")
Draw.Cls
Font.Draw ("You Win!!", maxx div 2 - 150, maxy - 25, font, brightblue)
locatexy (maxx div 2 - 150, maxy - 50)
put "You created ", ball_amount, " balls and clicked all of them in 60 seconds"
locatexy (maxx div 2 - 150, maxy - 75)
put "giving you a score of ", points, " points, Congratulations."
end win
procedure check_click (instance : int)
if mouse_buttons = 1 and oldbutton = 0 then
oldbutton := 1
ball (instance).instance -> check_clicked (mouse_x, mouse_y, mouse_buttons)
elsif mouse_buttons = 0 and oldbutton = 1 then
oldbutton := 0
end if
end check_click
procedure delete_ball (object_to_be_deleted : int)
var temp : bouncy
for lp : object_to_be_deleted .. upper (ball) - 1
temp := ball (lp)
ball (lp) := ball (lp + 1)
ball (lp + 1) := temp
end for
free balls, ball (upper (ball)).instance
new ball, upper (ball) - 1
end delete_ball
procedure new_ball (x, y, size, colur : int)
new ball, upper (ball) + 1
new balls, ball (upper (ball)).instance
ball (upper (ball)).points_taken := false
ball (upper (ball)).instance -> initialize (x, y, size, colur, Rand.Int (1, 7), Rand.Int (1, 7))
ball_amount += 1
end new_ball
%-----------------------------------------------------------------------------------------------
%Main Program
new_ball (maxx div 2, maxy div 2, 20, ball_color)
loop
Draw.Cls
tim2 := Time.Elapsed - timer
put "Balls: ", upper (ball), " Points: ", points, " Time: ", round ((tim - tim2) / 1000)
put "Button: ", mouse_buttons, " Old Button: ", oldbutton
Input.KeyDown (keys)
%create more balls by holding down the enter key
if keys (KEY_ENTER) then
new_ball (Rand.Int (max_size, maxx - max_size), Rand.Int (max_size, maxy - max_size), Rand.Int (min_size, max_size), ball_color)
Input.Flush
end if
Time.DelaySinceLast (30)
for i : 1 .. upper (ball)
%move and draw the ball
Mouse.Where (mouse_x, mouse_y, mouse_buttons)
check_click (i)
ball (i).instance -> move
ball (i).instance -> draw
end for
new elements_to_be_removed, 0 %start an empty array
for j : 1 .. upper (ball)
%the if not ball (j).instance -> draw_ is if the balls have been drawn and clicked on
if not ball (j).instance -> draw_ and not ball (j).points_taken then
%do this to stop points to keep being gained every iteration of the loop
ball (j).points_taken := true
%used for the delete_ball procedure because it screws up if i have delete_ball(j) in here
new elements_to_be_removed, upper (elements_to_be_removed) + 1
elements_to_be_removed (upper (elements_to_be_removed)) := j %removes ball j in next for loop
points += 10
end if
end for
for i : 1 .. upper (elements_to_be_removed)
delete_ball (elements_to_be_removed (i))
end for
%timer
exit when tim - tim2 <= 0
View.Update
end loop
if upper (ball) >= 1 then
lose
else
win
end if
|
any help would be appreciated |
|
|
|
|
|
TheOneTrueGod
|
Posted: Mon Aug 14, 2006 1:46 pm Post subject: (No subject) |
|
|
I believe it is because of this:
code: |
if mouse_buttons = 1 and oldbutton = 0 then
oldbutton := 1
ball (instance).instance -> check_clicked (mouse_x, mouse_y, mouse_buttons)
elsif mouse_buttons = 0 and oldbutton = 1 then
oldbutton := 0
end if
|
If you click on ball(2), it checks ball 1, sets oldbutton to 1, realises you havn't clicked on ball 1, then moves on. Now it checks ball 2, but oldbutton = 1 and button = 1, so nothing happens.
What I normally do is:
code: |
%Pseudocode
loop
oldbutton := button
mousewhere(mx,my,button)
%Do stuff, but never change button or oldbutton variables
View.Update
delay
cls
end loop
|
Also, You have a lot of global-ish variables there -- You should consider trying to eliminate them, and have the procedures run purely on parameters (Actually, for the "Clicked On" procedures, these should be functions that return booleans ) |
|
|
|
|
|
TheOneTrueGod
|
Posted: Mon Aug 14, 2006 1:53 pm Post subject: (No subject) |
|
|
Sorry for the double post, but I just came across this post by you in the application section, and thought it fit perfectly here as a message to you
Superfreak wrote:
i noticed in your code you are using randint, i ask you : why? randint is a procedure that changes the value of your variable, why not use Rand.Int, a function? generally speaking, when you have a choice between a function or a procedure to do something, take the function
|
|
|
|
|
|
Ultrahex
|
Posted: Mon Aug 14, 2006 3:29 pm Post subject: (No subject) |
|
|
Just To Notify You, if you check here first i have supplied you with i think all the information your going to need i accidently posted in the turing source code area oh well
anyhow
http://www.compsci.ca/v2/viewtopic.php?t=13292
thats link to your post over there and my info is there with examples and all!
peace |
|
|
|
|
|
Clayton
|
Posted: Mon Aug 14, 2006 3:37 pm Post subject: (No subject) |
|
|
well actually if you look at the originaly code the check_clicked procedure in my balls class was originally a fcn, however, when i added in the button,oldbutton variables to control clicking, i couldnt figure out immediately how to use it with a function without changing the draw_ variable from within the class outside of the class (a very, very bad practice ) so ive temporarily made it a procedure for now.
as for my problem i figured it out, it was pretty much like you said TOTG, i played with it a bit and got it to work, my check_click procedure looks like this now (outside of the class):
code: |
procedure check_click
Mouse.Where (mouse_x, mouse_y, mouse_buttons)
if mouse_buttons = 1 and oldbutton = 0 then
for i : 1 .. upper (ball)
oldbutton := 1
ball (i).instance -> check_clicked (mouse_x, mouse_y, mouse_buttons)
end for
elsif mouse_buttons = 0 and oldbutton = 1 then
oldbutton := 0
end if
end check_click
|
and it works fine right now (even if it isnt the prettiest ) |
|
|
|
|
|
NikG
|
Posted: Mon Aug 14, 2006 5:16 pm Post subject: (No subject) |
|
|
SuperFreak82 wrote: code: | procedure check_click
Mouse.Where (mouse_x, mouse_y, mouse_buttons)
if mouse_buttons = 1 and oldbutton = 0 then
for i : 1 .. upper (ball)
oldbutton := 1
ball (i).instance -> check_clicked (mouse_x, mouse_y, mouse_buttons)
end for
elsif mouse_buttons = 0 and oldbutton = 1 then
oldbutton := 0
end if
end check_click | and it works fine right now (even if it isnt the prettiest )
It may work, but it's not as easy to understand. Do what Cervantes, TOTG, and even I have recommended in your other post: code: | procedure check_click
oldbutton := mouse_buttons
Mouse.Where (mouse_x, mouse_y, mouse_buttons)
if mouse_buttons = 1 and oldbutton = 0 then
for i : 1 .. upper (ball)
ball (i).instance -> check_clicked (mouse_x, mouse_y, mouse_buttons)
end for
end if
end check_click |
|
|
|
|
|
|
Clayton
|
Posted: Mon Aug 14, 2006 8:31 pm Post subject: (No subject) |
|
|
whats so hard to understand about mine? the way i see it mine is actually easier to understand because what is happening is more apparent |
|
|
|
|
|
|
|