Entries:
Comments:
Posts:

Loading User Information from Channel 9

Something went wrong getting user information from Channel 9

Latest Achievement:

Loading User Information from MSDN

Something went wrong getting user information from MSDN

Visual Studio Achievements

Latest Achievement:

Loading Visual Studio Achievements

Something went wrong getting the Visual Studio Achievements

Jeopardy!

For Visual Studio Live 2010 in Redmond, I was asked to help plan the attendee event for the conference.  We decided to recreate the game show Jeopardy and allow 3 teams of 3 people to play: Microsoft Employees vs. Visual Studio Live Speakers vs. Visual Studio Live Attendees.  To pull this off, I decided to create some big, red buttons to allow the players to ring in, and some software to drive the game itself.

The Buzzers

I debated long and hard on whether to create hand-held buzzers as seen on Jeopardy, or to go with larger table-top buzzers.  I opted for the latter to keep things a bit simpler.  I searched around for inexpensive big red buttons, and eventually stumbled across the Staples “Easy” button which says “That was easy” when you smack it.  I figured with a little hacking, I could repurpose these into buzzers that could be used for the game.

CIMG1397

To turn this into a usable buzzer, flip it over, remove the foam black feet, batteries, and battery door as shown.

CIMG1401

Next, unscrew the four screws and disassemble the button.

CIMG1402

Inside, you’ll find a small circuit board with a rubber button mounted on top.  I was able to trace out the switch and determine the two best solder points to extend the switch with some two-conductor wire.

CIMG1405

Cut a length of the wire to suit your needs, then strip one end along with the 2 wires inside.

CIMG1409

Next, cut off the capacitor next to the white rubber button, seen below the arrow in the above picture.  This will make it a bit easier to solder the wire.

CIMG1412

Finally, solder the wire onto the two points I noted earlier.  It doesn’t matter which wire goes to which contact.  I also decided to remove the metal plate under the circuit board.  This makes the button very stiff and “clicky”, which I didn’t really like.  This isn’t necessary so you decide how you want the button to behave.

CIMG1416

Before we can close up the button some plastic needs to be cut out so the wire can feed out the back.  Cut the red plastic near the battery compartment as shown.

CIMG1420

Next, notch out the back of the grey ring, run the wire down through these notches, and close everything back up.

CIMG14212010-08-03 22.44.49

 

The other end of that wire gets connected to the Phidget 8/8/8 board.  Simply strip the wires at the other end, and insert one into a terminal marked G (ground), and the other into one of the numbered Inputs.  Again, it doesn’t matter which wire goes to which terminal.  You will wind up putting more than one wire into the Ground terminal since there are only two on the board.

The Software

There are two pieces to the game:  The main game board, and the scoring console.  I have some ideas to perhaps put these together into a single application, but for now, they are separate.

The game board application runs on a computer and can be directly duplicated to a projector for display for a large crowd.  The scoring console consists of two windows, one which will be for the operator only, and one which will be projected and shown to the players/audience.  So, you will need a machine capable of multiple monitor output for that.

I’m going to give an overview of the two applications here.  I took some shortcuts and have a few things that I’m not entirely proud of, but with this being a side project with a looming deadline, I did what I had to do.  Smile  There are some places where a more XAML-based approach could have been used, some areas where databinding could have been used more effectively, the software isn’t overly configurable, etc.  I hope to address some of these things in a future version and I will update the code/article if/when that happens.

The Game Board

This application displays the main Jeopardy board, but also has an “admin console” where the categories are edited and saved.

imageimage

Let’s start with the admin side.  The window is created with a Grid layout with 6 columns and 7 rows, the last row being a series of 6 buttons to load/save the data for round 1, 2, and 3 (Final Jeopardy).

In code, those cells are populated with a UserControl named EditorBox that I created.

private void Window_Loaded(object sender, RoutedEventArgs e)
{
    for(int col = 0; col < 6; col++)
    {
        for(int row = 0; row < 6; row++)
        {
            EditorBox tb = new EditorBox();
            tb.SetValue(Grid.RowProperty, row);
            tb.SetValue(Grid.ColumnProperty, col);
            EditorGrid.Children.Add(tb);
 
            _boxes[col,row] = tb;
        }
    }
}

The EditorBox is a very simple control which contains a checkbox to toggle the Daily Double on or off for that square, and a styled textbox.  These controls are hooked up with some data binding to two dependency properties on the control itself, which are set when the data is loaded from the XML file.

Data is stored internally in a List of Category and Clue objects.

public class Category
{
    public string Text { get; set; }
    public Clue[] Clues { get; set; }
}
 
public class Clue
{
    public string Value { get; set; }
    public string Text { get; set; }
    public bool DailyDouble { get; set; }
}

When the data is saved, the screen of EditorBoxes is enumerated and placed into Category and Clue objects.  That master list is then persisted to an XML file using the XmlSerializer.  To load the data, this process is reversed, reading the XML file, placing the data into the Category and Clue objects, and then assigned to the appropriate EditorBox controls.

The game window consists of a number of user controls.  The first is the CategoryWindow which is the white text displayed at the top of each category column.  The boxes below this window are DollarWindow controls which, when double-clicked, display a ClueWindow control.  A single CategoryWindow and five ClueWindow controls make up a CategoryStrip.  Six CategoryStrip controls arranged side-by-side make up the full GameWindow.  There is also an ImageWindow control which is used to display the Daily Double image.  All of these controls contain various dependency properties for things like the clue text, the dollar value, whether it’s a Daily Double, etc.  These items are bound when the game board is loaded, which is handled by some keyboard input.  Pressing F1 will load round 1, F2 will load round 2, and F3 will load the Final Jeopardy round.  Pressing the Tab key will display the admin editor window.

One issue I ran into when building the application was displaying the clue text so it would fill the display window, but then zoom in while keeping the same formatting and wrapping.  I was able to achieve this by wrapping a TextBlock with a Border and then a Viewbox.  The ViewBox allows the content to stretch and resize without changing the contents of the window, and the Border allows the text to be centered vertically.  I was then able to use a quick Storyboard to zoom the clue window in from its position on the board to fill the screen when the clue window is double-clicked.

<EventTrigger RoutedEvent="UserControl.Loaded" SourceName="clueWindow">
    <EventTrigger.Actions>
        <BeginStoryboard>
            <Storyboard>
                <DoubleAnimation From="0" To="1280" Duration="00:00:00.25" Storyboard.TargetProperty="Width"/>
                <DoubleAnimation From="0" To="960"  Duration="00:00:00.25" Storyboard.TargetProperty="Height"/>
                <DoubleAnimation To="0" Duration="00:00:00.25" Storyboard.TargetProperty="(Canvas.Top)"/>
                <DoubleAnimation To="0" Duration="00:00:00.25" Storyboard.TargetProperty="(Canvas.Left)"/>
            </Storyboard>
        </BeginStoryboard>
    </EventTrigger.Actions>
</EventTrigger>

Sound Effects

There are two sound effects that are played during the game.  The first is the Daily Double sound effect when the clue is revealed, and the second is the Final Jeopardy “think” music.  Both of these are stored as WAV files and copied to the output directory.  To play the sounds, the SoundPlayer object is used.  This takes a path to the WAV file to load.  Then, calling the Play method will play the sound.

SoundPlayer sp = new SoundPlayer("Sounds\\think.wav");
sp.Play();

The Score Board

As I said earlier, this application consists of two Windows, one of which is for the operator, the other of which is for display.

imageimage

The operator console consists of three Calculator user controls, one per team.  This control contains buttons to represent each dollar value on the board, along with a + and – button.  Scores can be easily added and subtracted based on correct and incorrect answers.  The score value from these columns are bound to the display window’s score area for each team.

The display window consists of three ScoreDisplay user controls, one per team.  A ScoreDisplay contains a TextBlock to display the score for the player, and a TextBlock below it which shows the player’s name.  This area changes to a blue/white gradient when the player rings in.  Above and below these displays is a wide rectangle which also changes to a blue/white gradient when the buzzers have been activated after a question is read.

The buzzers are also wired into this application using the standard Phidgets API.  When the application starts, the Phidget board is opened and an event is wired up to listen for changes to the inputs on the board.  We also read the app.config file to determine which inputs on the board are tied to which buzzer/player.  This will allow us to know when a player has pressed their big red button to buzz in.

Now, to follow the rules of Jeopardy, we have to notify the players that they are allowed to ring in, figure out who rung in first, “light up” their display, and allow them to answer.  If they are correct, the buzzers are reset and the next question is selected.  If they are incorrect, but buzzers are re-activated, however the player who answered incorrectly is locked out and cannot ring in again.

When the host finishes reading the question, the scoreboard operator presses F1 to turn on the buzzers.  This swaps the style of the Rectangle to the gradient version.  When the players ring in, the event we wired up earlier is called on each button press.  This figures out who rung in based on the input of the Phidget that was activated, lights up the appropriate boxes, and locks out the other buzzers.

void _ik_InputChange(object sender, Phidgets.Events.InputChangeEventArgs e)
{
    if(!e.Value)
        return;
 
    lock(_answerLock)
    {
        // we already have our "winner" so dump out
        if(_answering || !_canAnswer)
            return;
 
        // we're on a different thread, so invoke to the UI thread
        this.Dispatcher.Invoke(new Action(delegate 
        {
            // if player 1 can answer, and we're coming from the player 1 input on the Phidget
            if(!_player1Locked && _player1Inputs.Contains(e.Index.ToString()))
            {
                // we're in answer mode, player 1 can't ring in again, and we light up the board
                _answering = true;
                _player1Locked = true;
                _sdw.Score1Display.Answering = true;
                _sdw.BuzzersActive(false);
                Player1Status.Background = Brushes.Green;
            }
 
            if(!_player2Locked &&_player2Inputs.Contains(e.Index.ToString()))
            {
                _answering = true;
                _player2Locked = true;
                _sdw.Score2Display.Answering = true;
                _sdw.BuzzersActive(false);
                Player2Status.Background = Brushes.Green;
            }
 
            if(!_player3Locked &&_player3Inputs.Contains(e.Index.ToString()))
            {
                _answering = true;
                _player3Locked = true;
                _sdw.Score3Display.Answering = true;
                _sdw.BuzzersActive(false);
                Player3Status.Background = Brushes.Green;
            }
        }));
    }
}

When the player has responded, the proper score value is added or subtracted using the buttons on the operator screen, and one of two things happens.  If the answer was correct (the operator pressed +), the buzzers are reset for the next question.  If the answer was incorrect (the operator pressed –), that player is locked out and the buzzers are re-activated for the other players to buzz in.  If all three players guess incorrectly, or the question isn’t answered, the operator can press F3 to reset the buzzers for the next question.

Using the Application

If you download the source or binaries, you will need to install the fonts included in the archive if you want to get the full Jeopardy effect.  In addition, you will need to build the buzzers, setup the Phidgets board, etc. as described above to run the scoring console.  The app.config for the scoring console contains 3 lines, one per player, which need to be set with the number of the input port the buzzer is tied to.  So, for example, if you have Player 1’s buzzer tied to input #3, the line would look like:

<add key="Player1Inputs" value="3" />

With the fonts and hardware in place, just run each application on their respective computers.  In the game board application, press the Tab key to load the editor and enter your questions for each round.  When playing the game, press F1/F2/F3 to load round 1/2/3 appropriately.

Conclusion

And that’s really all there is to it.  Two applications, some WPF, some user controls, some buttons and a Phidgets board.  Now you can host your own Jeopardy games at the office to make training a bit more interesting, or even play a game with some friends for fun.

I will probably update this software/article a bit more as we’ll probably be doing another game at Visual Studio Live in Orlando in November, so keep an eye out for that.

Bio

Brian is a Microsoft C# MVP who has been actively developing in .NET since its early betas in 2000, and who has been developing solutions using Microsoft technologies and platforms for even longer. Along with .NET, Brian is particularly skilled in the languages of C, C++ and assembly language for a variety of CPUs. He is also well-versed in a wide variety of technologies including web development, document imaging, GIS, graphics, game development, and hardware interfacing. Brian has a strong background in developing applications for the health-care industry, as well as developing solutions for portable devices, such as tablet PCs and PDAs. Additionally, Brian has co-authored the book "Coding4Fun: 10 .NET Programming Projects for Wiimote, YouTube, World of Warcraft, and More" published O'Reilly. He previously co-authored the book "Debugging ASP.NET" published by New Riders.  Brian is also an author for MSDN's Coding4Fun website.

Follow the Discussion

Remove this comment

Remove this thread

close

Comments Closed

Comments have been closed since this content was published more than 30 days ago, but if you'd like to continue the conversation, please create a new thread in our Forums,
or Contact Us and let us know.