Any way to trim all these IF statements?

Hi, I'm a VB6 hobby programmer and I'm just getting in to VB2005. I installed the VB Express ed. last night.

I'm writing a simple game and have run into a coding issue.

I have 15 buttons on a form. As part of the game, you click the buttons to essentially move them around.
After each click, I run a subroutine to check if the user has "won" the game.
In order to check for a win condition, I check to make sure each of the 15 buttons are in the right location on the form.

This has caused me to have an "if statement" mess of code. The way I wrote it is like this:



Private Sub CheckWin()

If ESX = 114 And ESY = 139 Then 'Initial check for empty square

If B1.Top = 28 And B2.Top = 28 And B3.Top = 28 And B4.Top = 28 And B1.Left = 3 And B2.Left = 40 And B3.Left = 77 And B4.Left = 114 Then

If B5.Top = 65 And B6.Top = 65 And B7.Top = 65 And B8.Top = 65 And B5.Left = 3 And B6.Left = 40 And B7.Left = 77 And B8.Left = 114 Then

If B9.Top = 102 And B10.Top = 102 And B11.Top = 102 And B12.Top = 102 And B9.Left = 3 And B10.Left = 40 And B11.Left = 77 And B12.Left = 114 Then

If B13.Top = 139 And B14.Top = 139 And B15.Top = 139 And B13.Left = 3 And B14.Left = 40 And B15.Left = 77 Then

MsgBox("You Win!")

End If

End If

End If

End If

End If

End Sub


 


As you can see, it looks like a mess. I didn't really have to make 5 IF statements, I could have just had one super long AND AND AND AND AND. But I figured if I do it this way, if the first IF fails, it will exit and not do every check, thus saving a smidgeon of processing time.
However, I know it's good programming practice to not have more then 2 or 3 embeded if statements.

So then, with less code, how can I make sure the left and top attributes of 15 buttons match my requirements for a win condition
Keep in mind, I'm ok using it this way, but eventually I may have a bigger layout and thus a lot more buttons, it could get messy.

Thanks all, this is my first post here!

(P.S. for a little more info. Note that each set of 4 buttons has the same TOP. So B1 - B4 are 28; B5-B8 are 65; etc... Also each 4 going down the grid, have the same LEFT. So B1,B5,B9,B13 are 3; B2,6,10,14 are 40; etc... So in other words, it is a perfect "table" of 4x4. The bottom right cell will be empty, thus the very first IF statement to check that.)

And with all this info, can you guess what game I'm making :)


Answer this question

Any way to trim all these IF statements?

  • khandar

    Another take on this:  rather than checking all of these locations at the end, you could simply track the changes as they happen as bits in a UShort.  You presumably are handling some event which “moves” the buttons.  You can detect then and there if a given square is in the right stop and, if so, set an appropriate value (or clear it, if it is not in the right place).  SO for example, in your fourth button:

    If B4.Top = 28 Then
     Me.TrackingUShort = TrackingUShort Or 1 << 3  ‘  Shift this three spaces to be in the "4" spot
    Else
     Me.TrackingUShort = TrackingUShort And Not 1 << 3
    End If

    And do this for each handler, changing the bit being set appropriately.  Then, at the end, you could just check for Me.TrackingUShort = &H7FFF (i.e., all 15 slots set to true) – if that’s a true statement, then everything is in place.

    --Matt Gertz--*
    VB Compiler Dev Lead



  • m.zirino

    Great, using the "point" method, I was able to turn that glob of IF madness up there, into this:


    Dim tic As Integer = 0

    For i = 1 To 15

    If Not ctrl(i).location = Pos(i) Then Exit Sub Else tic = tic + 1

    Next i

    If tic = 15 Then

    ButtonBar.Items(0).Image = Images.Images(1)

    ButtonBar.Items(0).Enabled = False

    My.Computer.Audio.Play(My.Resources.win, AudioPlayMode.Background)

    MsgBox("you win'd")

    End If


     


    The next question of mine, has to do with program resources, of the CPU/RAM kind.

    The last suggestion there, about using a 15 point counter, I've thought about that. Also not knowing how to implement it. So the question then becomes a matter of resources and CPU time.

    Think of it this way. This is just for fun, because I highly doubt it has much impact on my little game.
    But anyhoo, compare the two methods with each other.

    Using this point method, I've got all kinds of variables, I've got the declaration of the collection, as well as 16 declarations for each point. Then I create the collection and add each point variable into the collection. This is a lot of variables and also extra code for creating the collection in the first place. On the other hand, having my points as variables let's me easily change them if I ever make different sizes etc..without hard coding the point values like I had before.

    BUT, using the counter system as in the last suggestion, is there less overhead and code setting that up Is it less processing to check a single block on each click, and set a marker, versus checking ALL blocks on every click It would seem obvious, but I'm not so sure.

    What do you guys think I should do Stick with the loop and collections Or try the marker system

    (P.S. when this is over I'll let you all download my awesome new game. lol)

  • Dennis G.

    I think the easiest and cleanest way to accomplish your goal would be to inherit the button class.
    I would then add the following:

    Private _LocID as Nullable(Of Integer)

    Public Property LocID() as Nullable(Of Integer)
              Get
                        Return _LocID
              End Get
              Set(ByVal value As Nullable(Of Integer)
                        _LocID = value
              End Set
    End Property

    Public Function IsInPlace() as Booleen
              If Not LocID().HasValue Then Return False

              If LocID() = Me.Top
                        Return True
              Else
                        Return False
              End If
    End Function

     

    In the code for the form you could have your win condition checker run through like this:

    Private _Winner as Booleen = True

    Private Property Winner() as Booleen

    Private Function IsWinner() as Booleen
              If Not Button1.IsInPlace() Then Return False
              If Not Button2.IsInPlace() Then Return False
              If Not Button3.IsInPlace() Then Return False
              If Not Button4.IsInPlace() Then Return False
              Return True
    End Function

     

    The easiest way ever is to create a Component that you select an array of generic buttons with those properties and have the code behind check the status. (Just like a validation checker). I have some code laying around somewhere that does just something like that I just can't find it. If I do I'll be sure to shoot it up.


  • Drickus

    Dim MyLocation1 As New Point(3, 28)

    Dim MyLocation2 as New Point(40,28)

    If
    Me.Button1.Location = MyLocation1 And Me.Button2.Location =MyLocation2
    Then

    messagebox.show("You Win")

    End
    If



  • Newbe

    Hi,

     Vigilante wrote:
    I didn't really have to make 5 IF statements, I could have just had one super long AND AND AND AND AND. But I figured if I do it this way, if the first IF fails, it will exit and not do every check, thus saving a smidgeon of processing time.


    This isn't really related to your question, but that quote made me wonder...
    At least in C, if you have several AND or OR statements inside an IF, it only checks the necessary conditions to tell if that statement is true or false.
    For instance, imagine you have


    If (Condition1 and Condition2)
     


    In C, if Condition1 returned false, it wouldn't even evaluate Condition2.
    Likewise for OR - if Condition1 was true, even if Condition2 was false it would "enter" the if clause, and as such it wouldn't even evaluate Condition2.

    Does anybody know if the same is true for VB.Net

    Thanks!

  • Kunle

    anog- That is called a short circuit. PHP does that also. But not sure if VB does or not. If so, it probably wouldn't hurt to have just one long IF statement.


    Dman1- I've never heard of that function, is that new to .NET If that works, it would certainly trim some of the code, but essentially would be doing the same thing. Which is fine, but less code is good for me. I will give that a try tonight and see what happens. Thanks.

    I was also wondering, then, if this could be used in a loop, rather then as a long IF statement. Can your function, along with my collection, be iterated through
    So instead of "If btn(0).location = xx AND btn(1).location = xx.....". Maybe could I loop through it if the counters match Like maybe:

    For i = 0 to 14
    if not btn(i).location = loc(i) then
     exit sub
    else
     you win
    end if

    Just quazi code but you get the idea. That would make it so much cleaner. And I'm all for clean, tight code.

    thanks again.

  • borgy tan

    It looks to me as you are looking for each button to have a specific form location and that's how you are recognizing them.

    Why not create a button class where you have Button(n)

    You could give each location a number or actually you could number the locations such that when the buttons are in the right place

    Btn 0 = Loc 0
    Btn 1 = Loc 1
    Btn 2 = Loc 3

    .
    .
    .
    Btn n = Loc N

    If you did this...

    For dim I as byte = 1 to 15

       If button0 <> loc(i) then return

    next

    MsgBox "You win"



  • Colin F

    This is the eternal question of “pay later” vs. “pay now.” How you answer it depends on what you’re doing. In the case you’ve described, I would always go for “pay now,” because it scales so well. The user is not going to notice an additional microsecond of bit-setting, since the time for the UI to respond simply by moving the button around dwarfs it by comparison. By delaying it, you’re essentially not only doing all that bit-flipping at the last second, but you’re spending time getting to the memory, accessing it as (probably) an integer, and so on. For 15 buttons this is totally immaterial. Imagine, though, that you had a much more complex puzzle, and then this sort of thing adds up. Bigger puzzle Just increase your bitfield from a UShort to a UInteger or ULong, and life is good – the handler logic is the same, and no voluminous IF statements.



    I’ve worked on a number of similar problems during my career, and I’ve found that *most* of the time it’s better to cache the information in a bitfield simply because I never know how the product (in my case, Visual Basic) will be used at the enterprise level – I get it done at the point where it’s background noise, so I don’t have to sweat it later in one big (possibly noticeable) blob. The only exceptions are when another process is truly blocked on my getting “out” of whatever I’m doing, particularly if I’m in a loop, if (and only if) the bit setting is more expensive.



    Hope this helps,

    --Matt--*

  • JedG

    Yes I see the point of using the bit field. I'm going to see if I can implement that.

    Though my puzzle will probably not grow or even change, nor will I see a measurable performance difference. I would like to follow best practice programming. I didn't know VB had a built-in function like this, or I would have had to write another method to handle the changing of the bit field.

    I'll try to implement this and see what happens.

    Thanks.

  • Fraser Putnam

    VB.Net has short circuit keywords if you want that behavior:

    OrElse   &  AndAlso


    ms-help://MS.VSCC.v80/MS.MSDN.v80/MS.VisualStudio.v80.en/dv_vbalr/html/ca474e13-567d-4b1d-a18b-301433705e57.htm

  • alkaline

     Vigilante wrote:

    Dman1- I've never heard of that function, is that new to .NET If that works, it would certainly trim some of the code, but essentially would be doing the same thing. Which is fine, but less code is good for me. I will give that a try tonight and see what happens. Thanks.

    I was also wondering, then, if this could be used in a loop, rather then as a long IF statement. Can your function, along with my collection, be iterated through
    So instead of "If btn(0).location = xx AND btn(1).location = xx.....". Maybe could I loop through it if the counters match Like maybe:

    thanks again.


    Location is a method of the control object that is a "point" and yes you can iterate through the buttons and locations as you mentioned in code.

  • MikeFesta

    Sounds good, except I don't know how!

    I did figure out one thing though. I can create a new object collection and add all my buttons to it. Such as this:


    Dim ctrl As Collection


     


    And then I fill it up will my buttons like this:


    ctrl = New Collection

    ctrl.Add(B1, "1")

    ctrl.Add(B2, "2")

    ctrl.Add(B3, "3")

    ctrl.Add(B4, "4")


    ............

     


    And then I can reference them like this:



    ctrl(I).left
    ctrl(I).top

     


    However, for what you are saying, how would VB make the connection of "top" and "left" values to a single variable called "loc1" which has both values contained in it such as "if ctrl(I) = loc(I) Then...". I don't get it.

    Like if I was coding, say, "move btn1 to loc1". This is still a function I have to write to handle changing left and top values. So I don't see the connection.

    It is a primitive way to make a game, but it's very simple. Just taking the actual button object and move it around by left and top attributes. In order to win, B1 HAS to be at 3,28; and B2 HAS to be at 40,28; and so on. So I guess I'm not seeing the automation of your idea.

    If you could maybe create an example, say with a 2x2 grid for simplicity sake. Or tell me how I can use my object collection to do it. That would be great. Cause like I said, this IF mess works, I just think there must be a cleaner way to do it.

  • Any way to trim all these IF statements?