
-----------------------------------
Clayton
Sun Aug 13, 2006 1:50 pm

Array subscripts
-----------------------------------
right now i am working on a game (see my post in 
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...


-----------------------------------
Cervantes
Sun Aug 13, 2006 4:39 pm


-----------------------------------
'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
Sun Aug 13, 2006 5:15 pm


-----------------------------------
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 :D

-----------------------------------
Clayton
Sun Aug 13, 2006 5:57 pm


-----------------------------------
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 :


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_ = maxx then
            x := maxx - size
            x_vel *= -1
        end if
        if x - size = maxy then
            y := maxy - size
            y_vel *= -1
        end if
        if y - size  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 = 1 then
    lose
else
    win
end if



i have no idea why this is happening, any help would be appreciated :D

-----------------------------------
Cervantes
Mon Aug 14, 2006 7:40 am


-----------------------------------
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 [url=http://www.compsci.ca/v2/viewtopic.php?t=7390]this for some ideas.

-----------------------------------
Clayton
Mon Aug 14, 2006 10:21 am


-----------------------------------
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:

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_ = maxx then
            x := maxx - size
            x_vel *= -1
        end if
        if x - size = maxy then
            y := maxy - size
            y_vel *= -1
        end if
        if y - size  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 = 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 :D

-----------------------------------
Cervantes
Mon Aug 14, 2006 11:54 am


-----------------------------------
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
Mon Aug 14, 2006 12:07 pm


-----------------------------------
so have something like this?

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


-----------------------------------
Clayton
Mon Aug 14, 2006 12:25 pm


-----------------------------------
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:


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_ = maxx then
            x := maxx - size
            x_vel *= -1
        end if
        if x - size = maxy then
            y := maxy - size
            y_vel *= -1
        end if
        if y - size  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 = 1 then
    lose
else
    win
end if


any help would be appreciated :D

-----------------------------------
TheOneTrueGod
Mon Aug 14, 2006 1:46 pm


-----------------------------------
I believe it is because of this:


    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:


%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 :P)

-----------------------------------
TheOneTrueGod
Mon Aug 14, 2006 1:53 pm


-----------------------------------
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 :P


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
Mon Aug 14, 2006 3:29 pm


-----------------------------------
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 :P
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
Mon Aug 14, 2006 3:37 pm


-----------------------------------
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):


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 :D)

-----------------------------------
NikG
Mon Aug 14, 2006 5:16 pm


-----------------------------------
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_clickand it works fine right now (even if it isnt the prettiest :D)
It may work, but it's not as easy to understand.  Do what Cervantes, TOTG, and even I have recommended in your other post: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
Mon Aug 14, 2006 8:31 pm


-----------------------------------
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 :?
