Coding a Euchre Game, Part 3: Timers (Matt Gertz)

Coding a Euchre Game, Part 3: Timers (Matt Gertz)

  • Comments 6

Coding a Euchre Game, Part 3: Timers

In my previous posts regarding the Euchre game creation, I discussed some issues with creating a complex form and maintaining images to be shown on it.  In this posting, I’m going to start covering some of the more esoteric controls.

Timers and Message Pumps

Try this:  go to your Programs menu in Windows and launch the “Hearts” game.  Play a few hands of it.  Go ahead, I’ll wait.

(hum-dee-dum-dee-dum)

You back yet? Notice anything a little odd about that game?  The action is just a little fast, isn’t it?  In fact it’s so fast that you can barely take it in, and it takes you a few seconds just to realize what the other players have done.  That’s just too fast for me, and although I enjoy playing Hearts on my handheld (where it’s much better paced), I simply can’t play it on my desktop – it just feels unnatural.

Hearts is fast because the AI players simply connect their actions from one to the other without pauses.  Real players don’t act like that, though.  To simulate real player hesitation, we’re going to use a timer control.

To start this, we simply drag a Timer control from the toolbox to the EuchreTable form.  You’ll note that the timer control does not actually land on the form, but instead goes to a grey area below the form.  That’s because timers don’t actually have real positions; they’re never visible, so it makes no sense to put the on the form itself.  However, you still need to be able to set properties on them,which is why they show up in that grey area.  (Controls such as these are essentially identical to the old concept of a "windowless control.")

For the timer control, we’ll change its name to “EuchreTimer” and set the Interval to 1500 (measured in milliseconds) as a default -- we can always adjust it later.  Now, in the constructor for EuchreTable, we’ll add the following line of code:

        AddHandler EuchreTimer.Tick, AddressOf TimerEventProcessor

 

which specifies the handler for the Tick event which will occur every 1.5 seconds.

Now, we don’t want the timer to be on all the time.  We just want it to fire when the AI is making a decision.  So, by default, the timer will be off, and we’ll turn it on when needed.  After it fires once, we’ll turn it off again.

    Private Sub TimerEventProcessor(ByVal myObject As Object, _

 ByVal myEventArgs As EventArgs)

        EuchreTimer.Stop()

    End Sub

 

When we want to use the timer, we simply turn it on.  Let’s put the call in a method called TimerSleep which anyone can call to start up the timer:

    Private Sub TableSleep()

        EuchreTimer.Start()

    End Sub

However, now we have a problem.  The timer doesn’t actually block the AI from proceeding – it just throws out a “tick” which will be handled.  To actually block the AI from proceeding, we’re going to need to wait for the tick.  Let’s create a member variable call GoAhead and add a check for that to the two routines we’ve defined:

    Private Sub TimerEventProcessor(ByVal myObject As Object, _

 ByVal myEventArgs As EventArgs)

        EuchreTimer.Stop()

        GoAhead = True

    End Sub

    Private Sub TableSleep()

        GoAhead = False

        EuchreTimer.Start()

        While GoAhead = False

        End While

    End Sub

 Cool, now anyone calling TableSleep will block until the value of GoAhead changes when the timer event is processed, right?  Well, no.  The timer uses the UI message pump, and with the code as it currently is, the Timer message won’t be processed.  There are other messages that we’d want to have handled as well, such as those involving painting the application.  So, we’re going to need to pump messages.  This is extremely easy to do by using the Application.DoEvents() method:

    Private Sub TableSleep()

        StopPumpingMessagesDuringPause = False

        EuchreTimer.Start()

        While GoAhead = False

            Application.DoEvents()

        End While

    End Sub

Now, what if the user exited the game (or start a new one) while the timer was functioning?  We’d like to turn off the timer immediately, rather than have them wait to exit.  We do this by handling the “Closed” event (or the New Game message, as applicable).  First, we add a handler in the constructor of EuchreTable:

    AddHandler Me.Closed, AddressOf Me.EuchreTable_Closed

 

Then we implement the handler:

    Private Sub EuchreTable_Closed(ByVal sender As Object, _

ByVal e As EventArgs)

        GoAhead = True

        exitEuchre = True

    End Sub

 

And then we’ll alter the TableSleep method to throw an exception so that we can exit the game logic, no matter where we are.  This is a realy important step – as we are in the middle of a game, it would be bad if the form went away while we were still executing in the middle of a hand! I’ve created a class called EuchreException which just makes it easier for me to determine that it’s one of my own exceptions and not a system exception:

Class EuchreException

    Inherits SystemException

    Public Sub New(ByVal message As String)

        MyBase.New(message)

    End Sub

End Class

 

    Private Sub TableSleep()

        StopPumpingMessagesDuringPause = False

        EuchreTimer.Start()

        While GoAhead = False

            Application.DoEvents()

        End While

        If exitEuchre = True Then

            Throw New EuchreException("ExitGame")

        End If

    End Sub

And in the main loop of the program (which I call “NewGameInvoked”), we catch the exception and force an exit to the application:

    Private Sub NewGameInvoked()

  Try

            Do While exitEuchre = False

 

' (this is where the game actually happens –

   I’ve omitted the actual code for brevity’s sake)

 

            Loop

        Catch ex As EuchreException

            If ex.Message = "ExitGame" Then

                exitEuchre = False

                ' Do nothing; fall through and exit the application

            End If

        End Try

    End Sub

This may seem a little complicated for a game – after all, who cares if they have to wait a couple of seconds for the game to exit?  Or why not just let the AI players operate at warp speed?  But, at the very least, we *have* to periodically check for messages or we’ll never repaint or be able to exit the app until the game ends.  You might think that we could have simply added a message pump when it’s the user’s turn and just let the user live with waiting until then, but here’s where it’s important to be a subject matter expert:  in Euchre, there are times when the player doesn’t actually play the game – their partner can declare that they’ll go it alone on the bid.  Certainly, we don’t want the user to have to sit through all of that if they really want to exit!

We’ll be revisiting this code again when we talk about modality in a few days.  In the meantime, our next stop is Tooltip and Menu controls.  See you next time!

--Matt--*

Leave a Comment
  • Please add 2 and 6 and type the answer here:
  • Post
  • PingBack from http://endlands.org/coding-a-euchre-game-part-3-timers-matt-gertz/

  • Matt Gertz is showing how to code Eucher in VB.net. What is Eucher? It is a trump style card game with

  • Thanks for this series thus far. Very interesting!

  • Thanks, Dantv!  It was a lot of fun writing the card game and the blog.  I'm also open to people suggesting future topics -- I've got about 4 lined up, but am happy to cover other "real world" scenarios as well.

  • I agree with Dantv. This is interesting and helpful.

    But shouldn't EuchreException actually inherit from ApplicationException?

  • (Sorry, I'm a bit new at blogging -- I've got to remember to do a better job checking through the comments!)

    I'm glad you brought this up!  You raise a very good point, and that was indeed the party line a few years ago.  However, guidance has changed recently.  Check out this excerpt from http://msdn2.microsoft.com/en-us/library/system.applicationexception.aspx:

    "If you are designing an application that needs to create its own exceptions, you are advised to derive custom exceptions from the Exception class. It was originally thought that custom exceptions should derive from the ApplicationException class; however in practice this has not been found to add significant value. For more information, see Best Practices for Handling Exceptions."

    I.e., it was additional overhead for no good reason -- since you control the throw and the catch, and you can have multiple catches for the different types of exceptions, there's really nothing extra the ApplicationException is doing for you.

    --Matt--*

Page 1 of 1 (6 items)