Retro Code Sample: Press Your Luck

I was going through my portfolio recently and realized that I have an entry for my Press Your Luck game but I’ve only described how it works, never taken a deep dive into the code.

The current version (if you can call something no longer in use “current”) runs entirely on the client side.  There is one HTML file (with inline jQuery), one CSS file, an XML file with configuration values, and a handful of images and sounds.

Some parts of these files have been modified for display purposes.  None of the changes impact functionality.

We’ll start with the config file…

<?xml version="1.0" encoding="utf-8" ?>
<images>
  <image>
    <thumb>http://pressyourluck.info/images/prize_choices_thumb.jpg</thumb>
    <large>http://pressyourluck.info/images/prize_choices_full.jpg</large>
    <type>prize</type>
  </image>
  <image>
    <thumb>http://pressyourluck.info/images/prize_giftcard_thumb.jpg</thumb>
    <large>http://pressyourluck.info/images/prize_giftcard_full.jpg</large>
    <type>prize</type>
  </image>
  <image>
    <thumb>http://pressyourluck.info/images/prize_lunch_thumb.jpg</thumb>
    <large>http://pressyourluck.info/images/prize_lunch_full.jpg</large>
    <type>prize</type>
  </image>
  <image>
    <thumb>http://pressyourluck.info/images/prize_mug_thumb.jpg</thumb>
    <large>http://pressyourluck.info/images/prize_mug_full.jpg</large>
    <type>prize</type>
  </image>
  <image>
    <thumb>http://pressyourluck.info/images/prize_parking_thumb.jpg</thumb>
    <large>http://pressyourluck.info/images/prize_parking_full.jpg</large>
    <type>prize</type>
  </image>
  <image>
    <thumb>http://pressyourluck.info/images/prize_swagbag_thumb.jpg</thumb>
    <large>http://pressyourluck.info/images/prize_swagbag_full.jpg</large>
    <type>prize</type>
  </image>
  <image>
    <thumb>http://pressyourluck.info/images/prize_whammy1_thumb.jpg</thumb>
    <large>http://pressyourluck.info/images/prize_whammy1_full.jpg</large>
    <type>whammy</type>
  </image>
</images>

We’re defining a set of images, the tiles that make up the game board.  Each has a thumbail (the image displayed on the standard game board) and a full-size image (the one displayed in the center when that tile is selected by the player) and we define their URLs here.  We also define whether this is a prize image or a whammy, which determines what sound plays when that tile is selected.

Fairly simple.  Now we move on to the CSS…

body {
	margin: 0;
	padding: 0;
	background-image: url(./images/bg_page.jpg);
	background-repeat: no-repeat;
	background-position: center;
	background-color: #1a1a1a;
}

div#board {
	position: relative;
	margin: 30px auto 30px auto;
	padding: 0;
	width: 1002px;
	height: 800px;
	border: 5px solid #000;
	border-radius: 12px;
	background-color: #000;
}

div#board div {
	width: 167px;
	height: 160px;
	
}

div#board div img {
	position: relative;
	width: 150px;
	height: 140px;
	margin: 8px 6px 8px 7px;
	padding: 0;
	border: 2px solid #000;
}

div#board div.inactive {
	background-image: url(./images/bg_item_inactive.jpg);
	opacity: 0.35;
}

div#board div.active {
	background-color: yellow;
	background-image: url(./images/bg_item_active.gif);
	opacity: 1;
}

div#board div#item0 {
	position: absolute;
	left: 0;
	top: 0;
}

div#board div#item1 {
	position: absolute;
	left: 167px;
	top: 0;
}

div#board div#item2 {
	position: absolute;
	left: 334px;
	top: 0;
}

div#board div#item3 {
	position: absolute;
	left: 501px;
	top: 0;
}

div#board div#item4 {
	position: absolute;
	left: 668px;
	top: 0;
}

div#board div#item5 {
	position: absolute;
	left: 835px;
	top: 0;
}

div#board div#item6 {
	position: absolute;
	left: 0;
	top: 160px;
}

div#board div#item7 {
	position: absolute;
	left: 835px;
	top: 160px;
}

div#board div#item8 {
	position: absolute;
	left: 0;
	top: 320px;
}

div#board div#item9 {
	position: absolute;
	left: 835px;
	top: 320px;
}

div#board div#item10 {
	position: absolute;
	left: 0;
	top: 480px;
}

div#board div#item11 {
	position: absolute;
	left: 835px;
	top: 480px;
}

div#board div#item12 {
	position: absolute;
	left: 0;
	top: 640px;
}

div#board div#item13 {
	position: absolute;
	left: 167px;
	top: 640px;
}

div#board div#item14 {
	position: absolute;
	left: 334px;
	top: 640px;
}

div#board div#item15 {
	position: absolute;
	left: 501px;
	top: 640px;
}

div#board div#item16 {
	position: absolute;
	left: 668px;
	top: 640px;
}

div#board div#item17 {
	position: absolute;
	left: 835px;
	top: 640px;
}

div#board div#item_middle {
	position: absolute;
	left: 167px;
	top: 160px;
	width: 668px;
	height: 520px;
}

div#board div#item_middle img {
	position: relative;
	width: 648px;
	height: 460px;
	margin: 10px;
	padding: 0;
	border: 0;
}

div#board div#item_middle img.prize {
	width: 514px;
	height: 480px;
	margin: 0 77px 0 77px;
}

div#sound {
	width: 0;
	height: 0;
	overflow: hidden;
}

More pretty simple stuff. The page has a background. There’s a div that contains all the game elements. Those are positioned as needed. The tiles have a background image for their active and inactive states. The sound controls are hidden.

Now we get to the fun, the HTML and jQuery. Here’s the full page, we’ll break down the important parts afterwards…

<!DOCTYPE HTML>
<html xmlns="http://www.w3.org/1999/xhtml">
  <head>
    <meta http-equiv="content-type" content="text/html; charset=utf-8" />
    <title>Bravo Press Your Luck</title>
    <style type="text/css" media="screen">
      @import "./styles.css";
    </style>
    <link href="./jquery-ui/css/custom-theme/jquery-ui-1.8.16.custom.css" rel="stylesheet" type="text/css" />
    <script src="./jquery-ui/js/jquery-1.6.2.min.js" type="text/javascript"></script>
    <script src="./jquery-ui/js/jquery-ui-1.8.16.custom.min.js" type="text/javascript"></script>
  </head>
  <body>
    <div id="board">
      <div id="item0" class="inactive">
        <img src="./images/space.gif" id="image0" />
      </div>
      <div id="item1" class="inactive">
        <img src="./images/space.gif" id="image1" />
      </div>
      <div id="item2" class="inactive">
        <img src="./images/space.gif" id="image2" />
      </div>
      <div id="item3" class="inactive">
        <img src="./images/space.gif" id="image3" />
      </div>
      <div id="item4" class="inactive">
        <img src="./images/space.gif" id="image4" />
      </div>
      <div id="item5" class="inactive">
        <img src="./images/space.gif" id="image5" />
      </div>
      <div id="item6" class="inactive">
        <img src="./images/space.gif" id="image6" />
      </div>
      <div id="item7" class="inactive">
        <img src="./images/space.gif" id="image7" />
      </div>
      <div id="item8" class="inactive">
        <img src="./images/space.gif" id="image8" />
      </div>
      <div id="item9" class="inactive">
        <img src="./images/space.gif" id="image9" />
      </div>
      <div id="item10" class="inactive">
        <img src="./images/space.gif" id="image10" />
      </div>
      <div id="item11" class="inactive">
        <img src="./images/space.gif" id="image11" />
      </div>
      <div id="item12" class="inactive">
        <img src="./images/space.gif" id="image12" />
      </div>
      <div id="item13" class="inactive">
        <img src="./images/space.gif" id="image13" />
      </div>
      <div id="item14" class="inactive">
        <img src="./images/space.gif" id="image14" />
      </div>
      <div id="item15" class="inactive">
        <img src="./images/space.gif" id="image15" />
      </div>
      <div id="item16" class="inactive">
        <img src="./images/space.gif" id="image16" />
      </div>
      <div id="item17" class="inactive">
        <img src="./images/space.gif" id="image17" />
      </div>
      <div id="item_middle">
        <img src="./images/space.gif" id="image_middle" />
      </div>
    </div>

    <div id="sound">
      <audio id="player_board" name="player_board" preload="auto" loop="loop">
        <source src="./sounds/board.ogg" />
        <source src="./sounds/board.mp3" />
      </audio>
      <audio id="player_buzz" name="player_buzz" preload="auto">
        <source src="./sounds/buzz.ogg" />
        <source src="./sounds/buzz.mp3" />
      </audio>
      <audio id="player_whammy" name="player_whammy" preload="auto">
        <source src="./sounds/whammy.ogg" />
        <source src="./sounds/whammy.mp3" />
      </audio>
    </div>

    <script type="text/javascript">
    //<![CDATA[
      $(document).ready(function () {
        var images = define_board_images();
        window.game_data = build_game_boards(images);
        load_game_board();
      });

      function define_board_images () {
        var images = new Array();

        $.ajax({
          url: 'config.xml',
          dataType: 'xml',
          async: false,
          success: function (data) {
            $($(data)).find('image').each(function () {
              images.push({'thumb': $(this).find('thumb').text(), 'large': $(this).find('large').text(), 'type': $(this).find('type').text()});
            });
          }
        });

        return images;
      }

      function build_game_boards (images) {
        var boards = new Array();

        do {
          var set = new Array();

          do {
            images = array_shuffle(images);
            var n = 0;

            do {
              set.push(images[n]);
              n++;
            } while ((set.length < 18) && (n < images.length));
          } while (set.length < 18);

          boards.push(set);
        } while (boards.length < 50);

        return boards;
      }

      function load_game_board () {
        $('div#board div.active').removeClass('active');
        $('img#image_middle').removeClass('prize');
        $('img#image_middle').attr('src', './images/logo.jpg');

        window.active_set = -1;
        window.active_space = -1;

        print_set(get_random_set(false));

        $(document).keydown(function (e) {
          if ((e.keyCode == 32) || (e.keyCode == 33) || (e.keyCode == 34) || (e.keyCode == 66)) {
            e.preventDefault();
            start_game();
          }
        });

        $(document).bind('touchend', function (e) {
          e.preventDefault();
          start_game();
        });
      }

      function get_random_set (no_repeat) {
        var r = -1;

        if (no_repeat) {
          do {
            r = Math.floor(Math.random() * window.game_data.length);
          } while (r == window.active_set);
          window.active_set = r;
        } else {
          r = Math.floor(Math.random() * window.game_data.length);
        }
        return window.game_data[r];
      }

      function print_set (data) {
        $('div#board div img').each(function (index) {
          if ($(this).attr('id') != 'image_middle') {
            $(this).attr('src', data[index].thumb)
            $(this).attr('data-type', data[index].type)
            $(this).attr('data-large', data[index].large)
          }
        });
      }

      function move_active_space () {
        var r = -1;

        do {
          r = Math.floor(Math.random() * 18);
        } while (r == window.active_space);

        window.active_space = r;
        var new_selector = 'div#item' + r;

        $('div.active').removeClass('active');
        $(new_selector).addClass('active');
      }

      function start_game () {
        window.active_set = -1;
        window.active_space = -1;

        $(document).unbind();

        $(document).keydown(function (e) {
          if ((e.keyCode == 32) || (e.keyCode == 33) || (e.keyCode == 34) || (e.keyCode == 66)) {
            e.preventDefault();
            stop_game();
          }
        });

        $(document).bind('touchend', function (e) {
          e.preventDefault();
          stop_game();
        });

        $('#player_board')[0].play();

        window.interval_set = setInterval(function () { print_set(get_random_set(true)); }, 850);
        window.interval_space = setInterval(function () { move_active_space(); }, 500);
      }

      function stop_game () {
        clearInterval(interval_set);
        clearInterval(interval_space);

        $(document).unbind();

        $(document).keydown(function (e) {
          if ((e.keyCode == 32) || (e.keyCode == 33) || (e.keyCode == 34) || (e.keyCode == 66)) {
            e.preventDefault();
            load_game_board();
          }
        });

        $(document).bind('touchend', function (e) {
          e.preventDefault();
          load_game_board();
        });

        var winning_cell = 'div#item' + window.active_space;

        $('#player_board')[0].pause();
        $('#player_board')[0].currentTime = 0;

        if ($(winning_cell + ' img').attr('data-type') == 'whammy') {
          $('#player_whammy')[0].play();
        } else {
          $('#player_buzz')[0].play();
        }

        $(winning_cell).removeClass('active').delay(100).queue(function (next) {
          $(this).addClass('active').delay(100).queue(function (next) {
            $(winning_cell).removeClass('active').delay(100).queue(function (next) {
              $(this).addClass('active').delay(100).queue(function (next) {
                $(winning_cell).removeClass('active').delay(100).queue(function (next) {
                  $('img#image_middle').attr('src', './images/space.gif');
                  $('img#image_middle').addClass('prize');

                  $(this).addClass('active').delay(100).queue(function (next) {
                    $(winning_cell).removeClass('active').delay(100).queue(function (next) {
                      $('img#image_middle').attr('src', $(winning_cell + ' img').attr('data-large'));

                      $(this).addClass('active').delay(100).queue(function (next) {
                        $(winning_cell).removeClass('active').delay(100).queue(function (next) {
                          $(this).addClass('active').delay(100).queue(function (next) {
                            next();
                          });
                          next();
                        });
                        next();
                      });
                      next();
                    });
                    next();
                  });
                  next();
                });
                next();
              });
              next();
            });
            next();
          });
          next();
        });
      }

      function array_shuffle (orig_array) {
        var shuffled_array = orig_array.slice();
        var len = shuffled_array.length;
        var i = len;
        while (i--) {
          var p = parseInt(Math.random()*len);
          var t = shuffled_array[i];
          shuffled_array[i] = shuffled_array[p];
          shuffled_array[p] = t;
        }
        return shuffled_array; 
      };
    //]]>
    </script>
  </body>
</html>

Get the basic stuff out of the way… We import our CSS. We import jQuery UI. We lay out the game board and we set up some audio elements for the game sounds (which I pulled from some site that had all sorts of game show sounds archived, I can’t remember where it was).

$(document).ready(function () {
  var images = define_board_images();
  window.game_data = build_game_boards(images);
  load_game_board();
});

The first thing we do is initialize some stuff. Define our board images, build our possible game boards, throw a board onto the screen. Now let’s see how we do that.

function define_board_images () {
  var images = new Array();

  $.ajax({
    url: 'config.xml',
    dataType: 'xml',
    async: false,
    success: function (data) {
      $($(data)).find('image').each(function () {
        images.push({'thumb': $(this).find('thumb').text(), 'large': $(this).find('large').text(), 'type': $(this).find('type').text()});
      });
    }
  });

  return images;
}

We’re loading that config file, then looping through each “image” element to find the “thumb”, “large”, and “type” definitions we discussed earlier. Then we’re dropping those into an array.

When I wrote this I was shocked that there wasn’t an easier way to do this using XML. If it were similarly-structured JSON, it’d just parse automatically. Instead I have to do it manually. Considering what the X in AJAX stands for, I expected more out-of-the-box support for XML. Maybe I’m just missing something.

function build_game_boards (images) {
  var boards = new Array();

  do {
    var set = new Array();

    do {
      images = array_shuffle(images);
      var n = 0;

      do {
        set.push(images[n]);
        n++;
      } while ((set.length < 18) && (n < images.length));
    } while (set.length < 18);

    boards.push(set);
  } while (boards.length < 50);

  return boards;
}

With our available images defined, we cache a set of fifty possible game boards. We do this by shuffling the array of images (using a function I just grabbed from somewhere else) and adding them in order to a new set until there are 18 in that set. If we run out before we get to 18, we shuffle again and keep going. This means we can have as many or as few (as long as there’s at least one) images configured.

function load_game_board () {
  $('div#board div.active').removeClass('active');
  $('img#image_middle').removeClass('prize');
  $('img#image_middle').attr('src', './images/logo.jpg');

  window.active_set = -1;
  window.active_space = -1;

  print_set(get_random_set(false));

  $(document).keydown(function (e) {
    if ((e.keyCode == 32) || (e.keyCode == 33) || (e.keyCode == 34) || (e.keyCode == 66)) {
      e.preventDefault();
      start_game();
    }
  });

  $(document).bind('touchend', function (e) {
    e.preventDefault();
    start_game();
  });
}

Finally we load the game board. We make sure no tiles are active, we set the middle image back to our placeholder, we get a randomly-selected one of our cached tile sets and display it on the board. Then we define some key events that allow the game to be controlled from the keyboard or from a presentation mouse, so that any event will trigger the start of the game. We bind the same action on touchend so that the person who commissioned this can play on her phone.

function get_random_set (no_repeat) {
  var r = -1;

  if (no_repeat) {
    do {
      r = Math.floor(Math.random() * window.game_data.length);
    } while (r == window.active_set);
    window.active_set = r;
  } else {
    r = Math.floor(Math.random() * window.game_data.length);
  }
  return window.game_data[r];
}

Our function for getting a random set is simple enough. Get a random number from 0 to the size of the set (should always be 50). If we don’t want to allow the same set to be picked twice in a row, compare that number to the current one and do it again until we get something different. Return the set of images with that number as the key.

function print_set (data) {
  $('div#board div img').each(function (index) {
    if ($(this).attr('id') != 'image_middle') {
      $(this).attr('src', data[index].thumb)
      $(this).attr('data-type', data[index].type)
      $(this).attr('data-large', data[index].large)
    }
  });
}

To print out the board, we loop through each image on the board that isn’t the one in the middle. We use the index of the image and pull from the array we set in get_random_set() to reset said image’s attributes.

function start_game () {
  window.active_set = -1;
  window.active_space = -1;

  $(document).unbind();

  $(document).keydown(function (e) {
    if ((e.keyCode == 32) || (e.keyCode == 33) || (e.keyCode == 34) || (e.keyCode == 66)) {
      e.preventDefault();
      stop_game();
    }
  });

  $(document).bind('touchend', function (e) {
    e.preventDefault();
    stop_game();
  });

  $('#player_board')[0].play();

  window.interval_set = setInterval(function () { print_set(get_random_set(true)); }, 850);
  window.interval_space = setInterval(function () { move_active_space(); }, 500);
}

Ahh, yes, now we start the actual gameplay. We wipe out all of the events we set earlier and set new ones on the same triggers, this time for stopping the game. We start playing our in-game music. Then we set an interval to reload the game board every 850 milliseconds (allowing for the same board to be played twice in a row this time) and for the active tile to shift every half-second. I got those numbers from watching way too much Press Your Luck.

function move_active_space () {
  var r = -1;

  do {
    r = Math.floor(Math.random() * 18);
  } while (r == window.active_space);

  window.active_space = r;
  var new_selector = 'div#item' + r;

  $('div.active').removeClass('active');
  $(new_selector).addClass('active');
}

How do we switch the active tile? Well we know there are 18 tiles so we randomly select a number 0 to 17 until that number is not the same as the one we’ve already got. Then we remove the active class from whatever tile is active and add it to the one that corresponds to our randomly-selected number.

Our last step is to stop the game and it’s made up of a bunch of little things.

clearInterval(interval_set);
clearInterval(interval_space);

$(document).unbind();

$(document).keydown(function (e) {
  if ((e.keyCode == 32) || (e.keyCode == 33) || (e.keyCode == 34) || (e.keyCode == 66)) {
    e.preventDefault();
    load_game_board();
  }
});

$(document).bind('touchend', function (e) {
  e.preventDefault();
  load_game_board();
});

First we clear our intervals so the game won’t continue, then we wipe out our event bindings and set up new ones for the same triggers. These new ones will reset the game board and get us in a position to start a new game.

var winning_cell = 'div#item' + window.active_space;

$('#player_board')[0].pause();
$('#player_board')[0].currentTime = 0;

if ($(winning_cell + ' img').attr('data-type') == 'whammy') {
  $('#player_whammy')[0].play();
} else {
  $('#player_buzz')[0].play();
}

We get the winning tile and stop the in-game music. Based on what type of image that winning tile is, we play either the “buzz-in” sound or the “whammy” sound.

$(winning_cell).removeClass('active').delay(100).queue(function (next) {
  $(this).addClass('active').delay(100).queue(function (next) {
    $(winning_cell).removeClass('active').delay(100).queue(function (next) {
      $(this).addClass('active').delay(100).queue(function (next) {
        $(winning_cell).removeClass('active').delay(100).queue(function (next) {
          $('img#image_middle').attr('src', './images/space.gif');
          $('img#image_middle').addClass('prize');

          $(this).addClass('active').delay(100).queue(function (next) {
            $(winning_cell).removeClass('active').delay(100).queue(function (next) {
              $('img#image_middle').attr('src', $(winning_cell + ' img').attr('data-large'));

              $(this).addClass('active').delay(100).queue(function (next) {
                $(winning_cell).removeClass('active').delay(100).queue(function (next) {
                  $(this).addClass('active').delay(100).queue(function (next) {
                    next();
                  });
                  next();
                });
                next();
              });
              next();
            });
            next();
          });
          next();
        });
        next();
      });
      next();
    });
    next();
  });
  next();
});

This is how we make the lights around the winning tile flash and it’s ugly. We add and remove the “active” class from that tile in 100 millisecond intervals. Partway through that, we change the center image on the game board to match that of the winning tile. Again, those times were selected from watching way too much Press Your Luck.

And that’s really all there is to it.  There may be a better way by now (I hope there is for that flashing bit) but this is what I knew at the time.  It was a lot of fun to write and it was a lot of fun to see people play.

Griffins Jersey Contest: Sour Grapes

I was going to do a write-up of the winners of the Grand Rapids Griffins’ jersey contest over at DetroitHockey.Net – where I’d already done a review and posted my predictions – but I realized doing so after my design didn’t win would come across as sour grapes. Instead, I decided to own that; accept the label and go forward anyway, posting my issues with the winning designs here instead of at DH.N.

And since I’m complaining, I should mention my write-up of my original submission.  Obviously I designed that for a reason, it’s my style.  Apparently it wasn’t the style of the Griffins but we have no word from them on what criteria were used to make their decision so I stand by it.

Let’s look at the winners in the order they were announced…

This is the one that annoys me most.  It’s a fantastic jersey.  I said right off that bat that I knew the Griffins would wear it.  It’s not an original design, though.

That’s a Grand Rapids Rockets jersey updated to say “Griffins” instead.  It looks awesome and it’s a well-done update but Roberts did not design the jersey.  I feel like in a competition with a prize on the line, it’s akin to plagiarism.  How can you win a jersey design contest without designing a jersey?

As I wrote in my review, I like a bunch of the individual elements on this jersey.  Overall, though, it comes across as weak.  The blue crest gets lost on the blue jersey.  The lack of red loses part of the Griffins’ identity.  The “Tuebor” on the collar works but “Si Quaeris Peninsulam Amaenam Circumspice” on the back just seems slapped on there (I’d suggested it go on the cuff). Especially with at least six stronger jerseys in contention, I don’t get why this one was picked.  But there is a conspiracy theory out there…

Sure is easy to reproduce a jersey that already follows an existing template.

Like I said off the top (and in the title): chalk it up as sour grapes if you want.  I’m disappointed I didn’t win.  But I don’t think that means my thoughts on the winners are invalid.

Grand Rapids Griffins Jersey Concept Revisited

As of a couple hours ago, the Grand Rapids Griffins’ alternate jersey contest is closed.  I wrote about my submission early in the contest but with it done I feel a little more comfortable iterating on it a bit.

After staring at my work for nearly a month, wondering if it would hold up to the other incoming designs (I’m not sure it does), I kept finding little things I wish I’d taken the time to think about.

The shoulder logo, for example, comes across as a little flat to me.  That’s going to be the case with the “vintage” colors as they’re more muted, but I think making the text blue with a white outline and adding an outline to the interlocking GR (as the version I inadvertently recreated had) helps add some complexity.

The updated shoulder logo for my Griffins concept jersey.
The updated shoulder logo for my Griffins concept jersey.

For the primary logo, I neglected to take into account that every logo the Griffins have ever selected has featured the team’s name in some form.  As such, I added a “banner” across the bottom of the shield with “Griffins” in it and re-positioned the griffin inside the shield to make room for the banner.

The updated crest logo for my Griffins concept jersey.
The updated crest logo for my Griffins concept jersey.

I didn’t change the design of the jerseys at all but with these updates I think the red version is the best one.

My updated Griffins concept jersey, red with blue shoulders.
My updated Griffins concept jersey, red with blue shoulders.

I actually don’t know if I like this better than my original (aside from having come to prefer the red version) but I wanted to at least document it.


There is one thing about the contest that, as I’ve followed it, has bothered me a little.  I’ve seen reference to this as a “fan” design competition but pretty clearly there are submissions from people who are not Griffins fans.  If that’s the case, aren’t the Griffins just asking for spec design work?  And one of their last winners was a professional designer.

I don’t know.  I entered knowing what it was and I’m sure everyone else did, too.  Just feels wrong.