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

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.

PrivateSub TimerEventProcessor(ByVal myObject AsObject, _

ByVal myEventArgs As EventArgs)

EuchreTimer.Stop()

EndSub

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:

PrivateSub TableSleep()

EuchreTimer.Start()

EndSub

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:

PrivateSub TimerEventProcessor(ByVal myObject AsObject, _

ByVal myEventArgs As EventArgs)

EuchreTimer.Stop()

GoAhead = True

EndSub

PrivateSub TableSleep()

GoAhead = False

EuchreTimer.Start()

While GoAhead = False

EndWhile

EndSub

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:

PrivateSub TableSleep()

StopPumpingMessagesDuringPause = False

EuchreTimer.Start()

While GoAhead = False

Application.DoEvents()

EndWhile

EndSub

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:

AddHandlerMe.Closed, AddressOfMe.EuchreTable_Closed

Then we implement the handler:

PrivateSub EuchreTable_Closed(ByVal sender AsObject, _

ByVal e As EventArgs)

GoAhead = True

exitEuchre = True

EndSub

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

PublicSubNew(ByVal message AsString)

MyBase.New(message)

EndSub

EndClass

PrivateSub TableSleep()

StopPumpingMessagesDuringPause = False

EuchreTimer.Start()

While GoAhead = False

Application.DoEvents()

EndWhile

If exitEuchre = TrueThen

ThrowNew EuchreException(“ExitGame”)

EndIf

EndSub

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

PrivateSub NewGameInvoked()

Try

DoWhile 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

EndIf

EndTry

EndSub

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

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.

"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.