Coding a Euchre Game, Part 5: Modality is a crutch (Matt Gertz)

Coding a Euchre Game, Part 5: Modality is a crutch


It’s really tempting to rely on modal dialogs in a program.  It forces the user to pay attention to what you feel is the most important thing.  However, modal dialogs can also be frustrating to users and you should be careful about when you use them.  To illustrate this point, let’s consider the Euchre game again.  At some point, the user is going to be asked to play a card.  Ideally, you don’t want the user to do anything else because there isn’t anything else the user can do – right?  Well, no exactly.  Remember that the user needs to be able to start a new game, or possibly close the game.  I talked a bit about this earlier in parts three and four of this series, but only as far as adding a message pump while the AI was operating.  In this case, though, it’s not the AI that’s active, but the player.  If the user is presented with a modal dialog, he or she is effective barred from closing the app (or restarting it), unless you do something lame like add a “quit game” or “new game” button to the dialog which the user would have to look at on every hand.  Furthermore, the modal dialog just won’t look as good for gameplay – the user will be expecting to click on a card instead, as that’s far more intuitive.


We need a better system; some way to get the user to click the card and yet give them a chance to quit or restart the game as well.  Furthermore, we want them to click a valid card – Euchre is a game where you have to play the same suit that was led (if you have such a card). This all actually turns out to be easy; we’ll just enable the relevant cards and add a message pump to handle events:


            If Seat = Seats.Player Then ‘ This is the human being player


 


               ‘ First, turn on the “play a card” sign:


                Table.SelectLabel.Text = My.Resources.Notice_PlayACard


 


              ‘ Assume the player has no cards which follow suit


                Dim AnyValid As Boolean = False


 


              ‘ Check each remaining card in the player’s hand


              ‘ to see if it would follow suit


                Dim i As Integer


                For i = 0 To 4


                    If Me.CardsHeldThisHand(i) IsNot Nothing Then


                        ‘ Note that the leader can play any card;


                        he/she selects the suit to be followed.


If Table.LeaderThisTrick <> Seat Then


                            If Not CardBelongsToLedSuit(Table, _


 Me.CardsHeldThisHand(i)) Then


‘ Wrong suit – disable the card


                                Table.TableTopCards(Seats.Player, _


i).Enabled = False


                            Else


                                AnyValid = True


                            End If


                        End If


                    Else


                        ‘ Disable any labels representing


‘ already-played cards.


                        Table.TableTopCards(Seats.Player, i).Enabled = False


                    End If


                Next


 


                If AnyValid = False Then ‘ Nothing of suit –


‘ can play whatever you want,


‘ so re-enable unplayed cards


                    For i = 0 To 4


                        If Not Me.CardsHeldThisHand(i) Is Nothing Then


                            Table.TableTopCards(Seats.Player, _


i).Enabled = True


                        End If


                    Next


                End If


 


                Table.SelectLabel.Visible = True


                Table.SelectLabel.Update()


                Table.PlayerIsPlayingACard = True


                Table.SetPlayerCursorToHand(True)


 


                ‘ Do a message pump here:


                Do While Table.PlayerIsPlayingACard = True


                    My.Application.DoEvents()


                    If Table.Exiting = True Then


                        Dim e As New EuchreException(“ExitGame”)


                        Throw e


                    End If


                    If Table.Restarting = True Then


                        Dim e As New EuchreException(“NewGame”)


                        Throw e


                    End If


                Loop


                Table.SelectLabel.Visible = False


                For i = 0 To 4


                    Table.TableTopCards(Seats.Player, i).Enabled = True


                Next i


                Table.Update()


                index = Table.SelectedCard ‘ Get the card the player picked


            Else


                ‘ Not a human being – let the AI pick a card


                index = AutoPlayACard(Table)


            End If


        End If


 


And then we’d go on to use the value of “index” to graphically play the selected card, mark it as played, etc.  Now, the above relies on the member variable PlayerIsPlayingACard in order to exit the message pump.  So, as you’ve probably guessed, there’s an event handler out there which will change this value whenever the “Click” event happens on a card:   


    Private Sub PlayerCard_Click(ByVal sender As System.Object, _


ByVal e As System.EventArgs) _


        Handles PlayerCard1.Click, PlayerCard2.Click,


PlayerCard3.Click, PlayerCard4.Click, PlayerCard5.Click


        If PlayerIsDroppingACard = True OrElse _


PlayerIsPlayingACard = True Then


            If sender Is PlayerCard1 Then


                SelectedCard = 0


            ElseIf sender Is PlayerCard2 Then


                SelectedCard = 1


            ElseIf sender Is PlayerCard3 Then


                SelectedCard = 2


            ElseIf sender Is PlayerCard4 Then


                SelectedCard = 3


            ElseIf sender Is PlayerCard5 Then


                SelectedCard = 4


            End If


 


            PlayerIsDroppingACard = False


            PlayerIsPlayingACard = False


            SetPlayerCursorToHand(False)


        End If


    End Sub


 


To create this handler, I just double-clicked on the first player card label on the form to create the handler for that card, typed in the other four labels’ click events to the handles clause in the method signature, and filled in the code.  (I also renamed the handler from PlayerCard1_Click to just PlayerCard_Click to indicate to me that it handles all of the player card clicks, but that’s just a cosmetic change and is not essential.)


However, in Euchre, sometimes the user feedback needs to be more complicated than simply clicking a card.  The user needs to be able to “bid” on trump at the beginning of the round.  I’ll need to explain a bit of the rules here to make that clearer: in Euchre, you use a deck of 24 cards (9 through A) and have four players.  Each player gets 5 cards, and this leaves 4 remaining.  The 4 are referred to as the “Kitty,” and the top one is turned up.  Each player gets a turn to decide if that want that card’s suit to be trump (i.e., will his or her team capture at least 3 of the 5 hands if that suit is trump).  If the player wants that card’s suit to be trump, the dealer picks it up and discards one of their own cards, meaning that the dealer will have at least one trump card.  If nobody wants that card’s suit to be trump, then there is another bidding round where players either pass or select any of the remaining trump suits (in order) until a suit is selected.  If everyone passes, then either the deal passes to the next player, or the current dealer is “stuck” selecting trump, depending on the rules you prefer.


So, we need a way to let the user bid on the trump.  We might be tempted to use a modal dialog here, but if we do, we’re blocking the close and new game events (as well as any ability to reposition the main window while the modal dialog is up).  Modeless dialogs won’t be much help, either, since we’d have to set up some sort of messaging between them and the main form.  So, what do we do?  Well, let’s take a lesson from the “play a card” scenario.  Ideally, we need a bid control which will be enabled when the bidding occurs; otherwise, it should be invisible.  We’ll use the most powerful control we’ve got – the user control.


The user control is so powerful because it’s a control that you define yourself.  It’s like having a mini-form living inside another form.  To create a user control, simply choose “Add User Control” from the “Project” menu.  You’ll get the typical form editor coming up, to which you just add in whatever controls you need.  My two bidding user controls are attached to this post, so go ahead & take a look.  Either of them is one control as far as the main form is concerned, although internally they’re made up of multiple controls.  The user controls show up in the form’s toolbox after building the project using Project/Build, so I’ll do that, then I’ll drag both controls to the center of the form.  To make sure that they don’t trigger any events before they should, I’ll mark them as Enabled = False, and I’ll hide them with “Visible=False.”   It’s then a simple thing to enable them in the main application during the bid, as I do here for the smaller one on the first bidding round:


            Table.BidControl.Visible = True


            Table.BidControl.Enabled = True


            Table.BidControl.BringToFront()


            Table.BidControl.Update()


            ‘ Do a message pump here:


            Do While Table.PlayerIsBidding = True


                My.Application.DoEvents()


                If Table.Exiting = True Then


                    Dim e As New EuchreException(“ExitGame”)


                    Throw e


                End If


                If Table.Restarting = True Then


                    Dim e As New EuchreException(“NewGame”)


                    Throw e


                End If


            Loop


 


And when “PlayerIsBidding” changes to False (as we’ll code it to do when the OK button is pressed on the bid control), we’ll simply get the state of the user control and then disable & hide it.  The player’s cards are disabled at this point in the game, so we don’t have to worry about handling any errant clicks on them. Thus, no modality is required; we got the user to focus on the job at hand while not inconveniencing them with a locked main window. 


That’s not to say that modal windows are “bad” – it’s just a matter of knowing when to use them for maximum effectiveness.  In the completed VBEuchre game which I’ll be attaching in the final post, you’ll note that I use them for issues that would involve data loss, such as ending the game or starting a new one (“Are you sure you want to exit?”), or for cases where the dialog is likely to be up only rarely and briefly (such as the “About…” dialog and the “New Game” dialog).


In the next part of this series, I’ll be talking about including sound in your application, including text-to-speech.  See you then!

EuchreBidDialogs.jpg