// for stats
var gamesPlayed = 0;
var totalMoves = 0;
// run this when the button is clicked
$('#run').click(function(){
var options = {
// see if the option to run until everyone wins is checked
doRunUntilEveryoneWins: $('#all-win').is(':checked')
};
// configure two players
var players = [
// ladies first
{ name: 'Thing 1', isLosingATurn: false,
position: -1, isWinner: false, moves: 0 },
{ name: 'Michael', isLosingATurn: false,
position: -1, isWinner: false, moves: 0 }
];
// call "DoGame" to play an entire game, passing
// in the players array
DoGame(options, players);
// update stats
gamesPlayed++;
totalMoves += players[0].moves + players[1].moves;
$('#average').text(Math.ceil(totalMoves/gamesPlayed));
// append another row to the table
$('#results').show()
.find('table').append(
$('<tr/>').append( $('<td/>').text(players[0].moves) )
.append( $('<td/>').text(players[1].moves) )
.append( $('<td/>').text(players[0].moves
+ players[1].moves) )
);
});
function DoGame(options, players){
// initialize the board
var board = MakeBoard();
// initialize the deck
var cards = MakeDeck();
// call "DoGameLoop" (pass in the board and deck)
// until it returns false, which indicates the game is over
while(DoGameLoop(options, players, board, cards));
}
// return false when the game is over
function DoGameLoop(options, players, board, cards){
// we will set this to true if someone is playing...
var IsSomeoneStillPlaying = false;
// for each player: DoPlayerLoop
for(var i = 0; i < players.length; ++i){
var player = players[i];
// skip this player if they've won already
// or made 10000 moves (that would be too many)
if(!player.isWinner && player.moves < 10000){
// do the actual move
DoPlayerLoop(options, player, board, cards);
// keep track if this player has won
IsSomeoneStillPlaying |= !player.isWinner;
// see if we should stop when just one player wins (configurable)
if(!options.doRunUntilEveryoneWins && player.isWinner){
return false;
}
}
}
// things went as planned, return true if some players are still playing
return IsSomeoneStillPlaying;
}
function DoPlayerLoop(options, player, board, cards){
// if we are losing a turn, turn off the "isLosingATurn"
// property and we're done (exit now)
if(player.isLosingATurn){
player.isLosingATurn = false;
return true;
}
// draw a card and increment the "moves" counter
var drawnCard = DrawACard(cards);
player.moves++;
// we'll can play either 1 or 2 moves because we have doubles
var currentSpace = DoMove(options, player, board, drawnCard);
// if the player drew a double card, they move again
if(drawnCard.isDouble && !player.isWinner){
DoMove(options, player, board, drawnCard ); // do it again!
}
if(player.isWinner){
// woohoo
return true;
}
// if the space we landed on is a bridge, follow the bridge
if(currentSpace.bridgeTo){
player.position = currentSpace.bridgeTo;
currentSpace = board[player.position];
}
// if we are now on a lose-a-turn space, turn on the
// "isLosingATurn" property so we know
// to skip our turn the next time around
if(currentSpace.loseTurn){
player.isLosingATurn = true;
}
}
function DoMove(options, player, board, card){
// we'll cycle through the board.
// if we have a regular color card (or double), we'll go
// until we hit the color, OR reach the end of the board
var currentSpace;
var iterations = 0;
do{
// advance one space
player.position++;
// if we hit the end of the board
// we start over if we have a character card
// or we win if we have a regular color card
if(player.position == board.length){
if(card.isCharacter){
// if we have a character card and we've reached the
// end of the board, wrap around
player.position = 0;
}
else{
// if your move takes you to the last square or beyond, you win
// set the "isWinner" property to true and exit
player.isWinner = true;
}
}
currentSpace = board[player.position];
// loop until we find the space we're looking for
// or we win
// or we iterate 10000 times (because something must be broken)
}while(currentSpace
&& currentSpace.color != card.color
&& !player.isWinner && ++iterations < 10000);
return currentSpace;
}
function DrawACard(cards){
// if there are no cards left in the deck they must have all been played so
// reshuffle them! Boom, now there are cards in the deck
if(cards.length == 0){
// note: we can't just overwrite cards as we did up above like this:
// cards = MakeDeck()
// because javascript doesn't support pass-by-reference. That means that
// when this function returns, cards still points to the same memory location,
// not the new location that MakeDeck points to. And so we need to merge the two arrays
// (even though one of them is empty), preserving the original memory address
// This is smelly and worthy of future attention
var newDeck = MakeDeck();
for(var i = 0; i< newDeck.length; ++i){
cards.push(newDeck[i]);
}
}
// remove a card and return it
return cards.pop();
}
function MakeDeck(){
// return a new, shuffled deck !
var unshuffled = [
{ color: 'Red' }, { color: 'Red' }, { color: 'Red' }, { color: 'Red' },
{ color: 'Red' }, { color: 'Red' }, { color: 'Red' }, { color: 'Red' },
{ color: 'Purple' }, { color: 'Purple' }, { color: 'Purple' }, { color: 'Purple' },
{ color: 'Purple' }, { color: 'Purple' }, { color: 'Purple' }, { color: 'Purple' },
{ color: 'Yellow' }, { color: 'Yellow' }, { color: 'Yellow' }, { color: 'Yellow' },
{ color: 'Yellow' }, { color: 'Yellow' }, { color: 'Yellow' }, { color: 'Yellow' },
{ color: 'Blue' }, { color: 'Blue' }, { color: 'Blue' }, { color: 'Blue' },
{ color: 'Blue' }, { color: 'Blue' }, { color: 'Blue' }, { color: 'Blue' },
{ color: 'Orange' }, { color: 'Orange' }, { color: 'Orange' }, { color: 'Orange' },
{ color: 'Orange' }, { color: 'Orange' }, { color: 'Orange' }, { color: 'Orange' },
{ color: 'Green' }, { color: 'Green' }, { color: 'Green' }, { color: 'Green' },
{ color: 'Green' }, { color: 'Green' }, { color: 'Green' }, { color: 'Green' },
{ color: 'Red', isDouble: true }, { color: 'Blue', isDouble: true },
{ color: 'Purple', isDouble: true }, { color: 'Orange', isDouble: true },
{ color: 'Yellow', isDouble: true }, { color: 'Green', isDouble: true },
{ color: 'Red', isDouble: true }, { color: 'Blue', isDouble: true },
{ color: 'Purple', isDouble: true }, { color: 'Orange', isDouble: true },
{ color: 'Yellow', isDouble: true }, { color: 'Green', isDouble: true },
{ color: 'Gingerbread Man', isCharacter: true }, { color: 'Candy Cane', isCharacter: true },
{ color: 'Gum Drop', isCharacter: true }, { color: 'Peanut', isCharacter: true },
{ color: 'Lolly Pop', isCharacter: true }, { color: 'Ice Cream Cone', isCharacter: true }
];
return shuffle(unshuffled);
}
function MakeBoard(){
// return a new board
return [
{ color: 'Red' },{ color: 'Purple' },{ color: 'Yellow' },
{ color: 'Blue' },{ color: 'Orange', bridgeTo: 59 },{ color: 'Green' },
{ color: 'Red' },{ color: 'Purple' },{ color: 'Gingerbread Man' },
{ color: 'Yellow' },{ color: 'Blue' },{ color: 'Orange' },{ color: 'Green' },
{ color: 'Red' },{ color: 'Purple' },{ color: 'Yellow' },{ color: 'Blue' },
{ color: 'Orange' },{ color: 'Green' },{ color: 'Candy Cane' },
{ color: 'Red' },{ color: 'Purple' },{ color: 'Yellow' },
{ color: 'Blue' },{ color: 'Orange' },{ color: 'Green' },
{ color: 'Red' },{ color: 'Purple' },{ color: 'Yellow' },
{ color: 'Blue' },{ color: 'Orange' },{ color: 'Green' },
{ color: 'Red' },{ color: 'Purple' },{ color: 'Yellow', bridgeTo: 45 },
{ color: 'Blue' },{ color: 'Orange' },{ color: 'Green' },
{ color: 'Red' },{ color: 'Purple' },{ color: 'Yellow' },{ color: 'Gum Drop' },
{ color: 'Blue' },{ color: 'Orange' },{ color: 'Green' },
{ color: 'Red', loseTurn: true },{ color: 'Purple' },{ color: 'Yellow' },
{ color: 'Blue' },{ color: 'Orange' },{ color: 'Green' },
{ color: 'Red' },{ color: 'Purple' },{ color: 'Yellow' },
{ color: 'Blue' },{ color: 'Orange' },{ color: 'Green' },
{ color: 'Red' },{ color: 'Purple' },{ color: 'Yellow' },
{ color: 'Blue' },{ color: 'Orange' },{ color: 'Green' },
{ color: 'Red' },{ color: 'Purple' },{ color: 'Yellow' },
{ color: 'Blue' },{ color: 'Orange' },{ color: 'Peanut' },{ color: 'Green' },
{ color: 'Red' },{ color: 'Purple' },{ color: 'Yellow' },
{ color: 'Blue' },{ color: 'Orange' },{ color: 'Green' },
{ color: 'Red' },{ color: 'Purple' },{ color: 'Yellow' },
{ color: 'Blue' },{ color: 'Orange' },{ color: 'Green' },
{ color: 'Red' },{ color: 'Purple' },{ color: 'Yellow' },
{ color: 'Blue', loseTurn: true },{ color: 'Orange' },{ color: 'Green' },
{ color: 'Red' },{ color: 'Purple' },{ color: 'Yellow' },{ color: 'Lolly Pop' },
{ color: 'Blue' },{ color: 'Orange' },{ color: 'Green' },
{ color: 'Red' },{ color: 'Purple' },{ color: 'Yellow' },
{ color: 'Blue' },{ color: 'Orange' },{ color: 'Green' },{ color: 'Ice Cream Cone' },
{ color: 'Red' },{ color: 'Purple' },{ color: 'Yellow' },
{ color: 'Blue' },{ color: 'Orange' },{ color: 'Green' },
{ color: 'Red' },{ color: 'Purple' },{ color: 'Yellow' },
{ color: 'Blue' },{ color: 'Orange' },{ color: 'Green' },
{ color: 'Red' },{ color: 'Purple' },{ color: 'Yellow', loseTurn: true },
{ color: 'Blue' },{ color: 'Orange' },{ color: 'Green' },
{ color: 'Red' },{ color: 'Purple' },{ color: 'Yellow' },
{ color: 'Blue' },{ color: 'Orange' },{ color: 'Green' },
{ color: 'Red' },{ color: 'Purple' },{ color: 'Yellow' },
{ color: 'Blue' },{ color: 'Orange' },{ color: 'Green' },
{ color: 'Red' }
];
}
//spiffy Fisher–Yates shuffle http://stackoverflow.com/q/962802/29
// O(n) woo hoo!
function shuffle(array) {
var tmp, current, top = array.length;
if(top) while(--top) {
current = Math.floor(Math.random() * (top + 1));
tmp = array[current];
array[current] = array[top];
array[top] = tmp;
}
return array;
}
<button id='run'>Run a game</button>
<label><input type='checkbox' id='all-win'/>Play out until everyone "wins"</label>
<div id='results'>
<hr/>
<h2>Results:</h2>
<p>Moves per game (average: <span id='average'>0</span>)</p>
<table>
<tr><th>Daddy</th><th>Thing 1</th><th>Total</th></tr>
</table>
</div>
#results { display:none; }