Application.DoEvents and Throwing Dice

J

Jeff Gaines

Having read the thread on Application.DoEvents I wonder if anybody has any
thoughts on how to deal with a dice throwing routine? Currently I use:

private void ThrowDice()
// Sets dice numbers and draws dice
{
int count;

while (m_KeepRolling)
{
Random rnd1 = new Random(unchecked((int)DateTime.Now.Ticks));
Random rnd2 = new Random(~unchecked((int)DateTime.Now.Ticks));
for (count = JDice5Common.DICE1; count <= JDice5Common.DICE5; count++)
{
if (!m_DiceHeld[count])
{
m_DiceArray[count] = rnd2.Next(1, 7);
DrawDice(count);
Application.DoEvents();
}
}
}
for (count = JDice5Common.DICE1; count <= JDice5Common.DICE5; count++)
DrawDice(count);
}

m_KeepRolling is a module wide variable which is toggled by clicking the
'Throw Dice' button. Using Application.DoEvents() allows the dice to be
re-drawn with their new values and, I think, gives the OS a chance to
catch up.

What would other people do with this?
 
F

Fred Mellender

Use a Timer (UI toolbox, components; System.Windows.Forms.Timer). When the
timer fires, throw the dice and draw them. Use Timer.Change(...) to set
interval to random number after a throw. Turn off (on) timer when user
decides to stop (start) throwing (via Change).

Timer will execute on its on thread so that UI is not starved. See the
documentation in the Help.
 
F

Family Tree Mike

Jeff Gaines said:
Having read the thread on Application.DoEvents I wonder if anybody has any
thoughts on how to deal with a dice throwing routine? Currently I use:

private void ThrowDice()
// Sets dice numbers and draws dice
{
int count;

while (m_KeepRolling)
{
Random rnd1 = new Random(unchecked((int)DateTime.Now.Ticks));
Random rnd2 = new Random(~unchecked((int)DateTime.Now.Ticks));
for (count = JDice5Common.DICE1; count <= JDice5Common.DICE5; count++)
{
if (!m_DiceHeld[count])
{
m_DiceArray[count] = rnd2.Next(1, 7);
DrawDice(count);
Application.DoEvents();
}
}
}
for (count = JDice5Common.DICE1; count <= JDice5Common.DICE5; count++)
DrawDice(count);
}

m_KeepRolling is a module wide variable which is toggled by clicking the
'Throw Dice' button. Using Application.DoEvents() allows the dice to be
re-drawn with their new values and, I think, gives the OS a chance to
catch up.

What would other people do with this?

You could put the ThrowDice logic in a background worker that does a report
progress. The report progress handler handles updates on the UI thread to
display the die values. I suspect you are using Application.DoEvents because
the code is running on the UI thread, right?

Mike
 
J

Jeff Gaines

On 24/04/2009 in message <[email protected]> Peter
Duniho wrote:

Many thanks Fred, Mike and Peter :)
I don't understand what the value of generating new dice values at top
speed is. I also don't understand why you create two Random instances,
but use only one.

I want to show the dice 'rolling', i.e. re-draw them as their values
change. I used to play the game (based on Yahtzee) with my late Father In
Law who was convinced he could select the numbers he needed by pressing
the 'Stop' button at the right time. He was also convinced that as I wrote
the program it produced better scores for me!

Also, you should create and use a single Random instance, allocated once
outside the loop (or even better, outside the method). For sure, you
should abandon the method you have now, which basically creates two random
number generators that are intrinsically linked, resulting in your random numbers being correlated (meaning they aren't random any more). Of course, you aren't using both Random instances right now, but if you were, it would be a problem. And creating a new Random() instance with each iteration of the loop, seeded with the time, removes nearly all semblance of randomness altogether.

That's interesting. I first wrote the game 15 years or so ago. When I
first wrote it in C# I had Petzold next to me (at least one of his books)
and he said that to get reasonably random numbers you should seed the
generator twice. I'll see if I can find the book.
Interestingly, having followed your advice, the game appears to throw more
pairs and higher values. I may set up a test bed and set it running to
gather some statistics as appearances can often be deceptive.
Also, we have no idea what "DrawDice()" actually does, so it's difficult
to offer advice regarding that method.

It just puts the appropriate image of a die in a picture box and then
refreshes the Picture Box.
Now, all that said...either of the approaches mentioned so far would be a
big improvement over what you've got now. Which one is most appropriate
for you depends on what you're really doing. A Timer would be more
appropriate if you actually don't want to run the dice at full speed. The
BackgroundWorker is better if you really do want to run at full speed. Though even the BackgroundWorker will be fine for timed iterations, just by putting a call to Thread.Sleep() for the desired interval.

(I actually would prefer a regular Thread instance rather than
BackgroundWorker, but BackgroundWorker can in fact be simpler to use, and
if it's just the one, and you don't have a high demand for other uses of
the ThreadPool, it's probably fine).

I have added a 'JDiceThrower' class with a Background Worker that raises
an event as it creates new random numbers (so that the main app can
re-draw the dice) and a final event when throwing is finished. Creation of
random numbers continues until a flag is set by the main app. As I want to
continually create new random numbers until the player presses the 'stop'
button I can't think of an alternative to that.

Thanks again, I will keep working on it...
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top