Thursday, February 23, 2017

Programmatically Defining Power Grid's Marketplace

Power Grid is one of my all-time favorite games.  In fact, I just played it last weekend!  I won, of course.  But I had an unfair advantage: a deep understanding of the mechanical underpinnings of one of the most crucial systems in the game -- its unpredictable marketplace where players can acquire new power plants each round.

How have I acquired this understanding, you may ask?  It's simple, I was working on this blog post.  Let's get to it.

If we are to represent all of the core functionality of Power Grid's market in Swift, where should we start?  I chose the Resource Type of each of the power plants as my starting point, represented here by an enum:

Sick enum, bro.  Next let's make a struct representing our actual power plants:

Whoa, motherfudger.  That is a doozy.  Looking more closely, you can see that the only stored property of this struct is its number.  Both its resourceType and citiesCanPower properties are computed using switches on that  number.  This makes initializing these PowerPlantCards extremely simple!

Alrighty, we're also going to need some variables to store all these structs we're gonna be messing around with, so lets get those poppin:

Wonderful.  We got three arrays of type PowerPlantCard: one for the actual market where players can select new power plants to add to their collection, another for the future market where players can see power plants that will be available for auction soon, and a third representing a deck where future power plant cards can be drawn from.

We also have a variable to help us keep track of the total amount of cards drawn from this power plant deck.  Why?  Well, a game of Power Grid undergoes a major change on exactly the 35th draw from the power plant deck.  Playing the game, this is represented by an actual card physically placed at the bottom of the deck at the beginning of the game, but because we're in computer world this representative component is strictly unnecessary.

Aight, now lets set up our markets and deck so we can get this game going:

So here we're iterating over a range of numbers (3 and 50, to be exact) and generating powerPlantCards for each of them.  Cards 3-6 go into the actual market, cards 7-10 go into the future market.  The rest are shuffled into the power plant deck, except for card 13 which goes on top.  Any number that pertains to a card that doesn't actually exist in a game of Power Grid just gets iterated over using the continue keyword.

Great, let's take a look at our arrays and check out what they got going on:

Nice shuffling, now we just need some functions to be able to actually do some stuff with our cards.

This function is for removing a power plant from the actual market when a player takes one to add it to their collection.  It checks to make sure the actual market isn't empty, removes the card they select from the array, draws a new card from the deck, and arranges it into the market.  The markets in Power Grid must always be in ascending order with the lowest four ranks in the actual market and the  highest four ranks in the future market.  This makes rearrangement necessary every time a new card is drawn.  Let's take a look at both of these functions:

This function increments the totalDrawnCards variable, checks to see if this is exactly the 35th draw (and sets up the new market if it is), then draws a card and returns it.  The Card must be optional in case the draw deck is empty or the new market is generated on this turn.

This is a meaty function so I made it a bit bigger for your sorry eyes.  We got a new array, we got cards being moved all over the place, we got a sort method, we got functionality to check how the markets need to be arranged.  We got it all.  No questions, please.

At the end of every round of Power Grid the highest value card is put back on the bottom of the deck, so here's that:

Simple enough.  All that's left is our function that gets called by the 35th draw from the draw deck to set up the new market.

Not bad!  Once this function is called, the future market is obliterated for the rest of the game and moves all the power plants to the actual market, which now contains 6 cards instead of the usual 4.

You got all that?  You wanna see it in action?  No prob, here is the market after a round of play in which every player gets a new power plant:

The cards are moved around a bit, and there are four less cards in the deck than before.  But wait!  Haven't we removed five cards?  There are five draw card function calls happening here after all.  Don't forget that the removeHighestPowerPlantCard function also adds a card to the bottom of the deck.  You see that Power Plant Card #31?  If you scroll up and look at the starting state of the market  you can see that was actually the 4th card drawn and added to the market, but since it was the highest ranked card at the end of the round it got moved back to the bottom of the deck.

The last thing to check out is our market state after the 35th card draw.  In our case this happens in the 7th round of play.  Let's check it out:

There you have it.  The future market has tanked (don't gamble on futures, bro) and there are now six cards in the actual market.  The power plant deck now solely consists of cards that have been put at the bottom of the deck by our removeHighestPowerPlantCard function calls.  Just like the real game!

This was a fun challenge, but I happily emerged triumphant in the end!  You see, that's all it takes to be happy, kids.  Tenacity and obsessive fascinations with niche hobbies.  The golden path to any great future!  Thanks for reading!

No comments:

Post a Comment