Power Grid Resource Costs


In the board game Power Grid, an integral part of the game is the act of buying resources to fuel your power stations. There are four types of resource used in the game (five, if you include renewable energy, but obviously you can't buy resources for that). These are Coal, Oil, Garbage, and Uranium. The resource market looks like this:

An illustration of the resource market, taken from the rulebook

1---- | 2---- | 3---- | 4---- | 5---- | 6---- | 7---- | 8---- | 10  | 12
CCC   | CCC   | CCC   | CCC   | CCC   | CCC   | CCC   | CCC   |  U  |  U
OOO U | OOO U | OOO U | OOO U | OOO U | OOO U | OOO U | OOO U | 14  | 16
GGG   | GGG   | GGG   | GGG   | GGG   | GGG   | GGG   | GGG   |  U  |  U

When resources are bought, they are taken from the left of the market, where they are cheapest. When new resources are added to the market, they are added from the right. As can be seen, this produces a supply and demand balance - if more resources of a particular type are being bought than are being replenished each round, the price of that resource will rise. Similarly, if it's less, the cost will reduce.

Coal, Oil, and Garbage all scale up in cost at the same rate, while Uranium scales much faster. A single unit of a non-Uranium resource costs 8 - floor((<units available> - 1) / 3). A single unit of Uranium costs 13 - <units available> if there are 5 or more units available, and 18 - (2 * <units available>) otherwise.

For example, at the start of the game there are all 24 units of Coal available to buy. If the first player would like to buy 4 units of Coal, the first three units would cost 1 each, and the 4th would cost 2, making the total cost 5. This leaves 20 units available. If the second player would also like to buy 4 units of Coal, the cost would be (2*2 + 2*3) = 10.

The Challenge

Your challenge is to write a program or function which calculates the cost of buying a given amount of a particular resource, assuming that there is a given amount of that resource present in the market.

Your submission should accept, in any reasonable input format and in any order, the following parameters:

  • The resource type - guaranteed to be one of [C, O, G, U].
  • The amount of that resource that is present in the market - guaranteed to be a non-negative integer. If the resource type is not U, it will not be greater than 24. If the resource type is U, it will not be greater than 12.
  • The amount of that resource that a player would like to buy - guaranteed to be a non-negative integer which is less than or equal to the amount already present in the market.

The output should be the cost of the requested resources.

Additional Rules

  • The formulae given are for illustrative purposes only, feel free to use any method of calculating the costs you like.
  • The letter codes for the different resource types (C, O, G, U) may be replaced by their lower case equivalents. You may also replace the letters for numbers, either in the form C=0, O=1, G=2, U=3 or C=1, O=2, G=3, U=4.
  • This is , so the smallest submission in bytes will be the winner.
  • If your submission requires the input in a different order or format than specified in the previous section, you should provide details.

Test cases

Test case format:

resource type, amount in market, amount to buy
> result

Test cases:

C, 24, 4
> 5

C, 20, 4
> 10

O, 3, 3
> 24

U, 1, 1
> 16

C, 1, 1
> 8

G, 0, 0
> 0

O, 10, 7
> 44

U, 12, 4
> 10

G, 11, 4
> 22
  • \$\begingroup\$ Do we assume the inputs are legal, or de we have to handle things like f("O",1,5)? \$\endgroup\$
    – Katenkyo
    Jul 12, 2016 at 12:03
  • \$\begingroup\$ @Katenkyo No, as detailed in the spec the inputs are guaranteed to be valid. IMO error checking in code golf is tedious so I've done away with it :o) \$\endgroup\$
    – Sok
    Jul 12, 2016 at 12:13
  • \$\begingroup\$ Perfect, might have missed that in the specs. Gonna do the post for my submission :) \$\endgroup\$
    – Katenkyo
    Jul 12, 2016 at 12:21
  • \$\begingroup\$ I think there are some errors in the test cases. I double checked and got f(G, 11, 4) = 22 and f(O, 10, 7) = 44. \$\endgroup\$ Jul 12, 2016 at 12:23
  • \$\begingroup\$ @Pietu1998 You're absolutely right, not sure how I got that wrong :\ editing now \$\endgroup\$
    – Sok
    Jul 12, 2016 at 12:46

6 Answers 6


Python 3, 71 69 bytes

Thanks to @xnor for -2 bytes

f=lambda r,a,b:b and[8-int(~-a/3),max(18-2*a,13-a)][r>2]+f(r,a-1,b-1)

A function that takes input via argument of the zero-indexed resource type r, the amount available a and the amount to buy b, and returns the cost.

This makes use of the fact that True and False equate to 1 and 0 in Python, allowing the use of Boolean expressions to index into lists.

How it works

f=lambda r,a,b           Function with input resource type r, amount available a and amount
                         to buy b
b and...                 Base case: return 0 if b=0
[8-int(~-a/3),...][r>2]  If not uranium, yield the unit cost 8-floor((a-1)/3)...
max(18-2*a,13-a)         ..else yield the current uranium unit cost
...f(r,a-1,b-1)          Decrement a and b, then pass to function
...+...                  Add the cost of each unit to give the total cost
:...                     Return the above

Try it on Ideone

  • 1
    \$\begingroup\$ You can do max(18-2*a,13-a) in place of [18-2*a,13-a][a>4]. \$\endgroup\$
    – xnor
    Jul 13, 2016 at 1:44

Javascript (ES6), 71 59 bytes


Takes type, market_amount and buy_amount as arguments. type is an integer between 0 and 3.


function run() { var tests = document.getElementById("tests").value.split("\n"), test, C = 0, O = 1, G = 2, U = 3; console.clear(); for (var test of tests) { console.log(test); console.log("> " + eval(test)); } }
<textarea rows="10" id="tests">f(C, 24, 4)&#10;f(C, 20, 4)&#10;f(O, 3, 3)&#10;f(U, 1, 1)&#10;f(C, 1, 1)&#10;f(G, 0, 0)&#10;f(O, 10, 7)&#10;f(U, 12, 4)&#10;f(G, 11, 4)</textarea><br/><button onclick="run()">Run</button>


Befunge, 142 bytes

v:&&<   ^-1\ -1p15+g15 <
v>#<v       <
! v5<
# 1:
| 81
\ 4\
: *-
4 -1
` .p
# @^15+g15<
>:49+\-   ^
>:2*92*\- ^

Try it here! Takes input as 3 integers, where the resource type is 0,1,2,3. Output is an integer.

No idea if this can be golfed any better. There's not that much whitespace, but the newlines probably hurt.


Python 2.7, 85 bytes:

F,G,H=input();B=0;exec"B+=[[18-(2*G),13-G][G>5],8-((G-1)/3)][F!='U'];G-=1;"*H;print B

Based on R. Kap's answer, but you can shave a byte down to 85 by removing the extra / for floor division. Because these are all integers it's automatically floored down to a whole number.

  • 1
    \$\begingroup\$ Welcome to PPCG! \$\endgroup\$ Jan 17, 2018 at 21:10

Python 2.7, 86 bytes:

F,G,H=input();B=0;exec"B+=[[18-(2*G),13-G][G>5],8-((G-1)//3)][F!='U'];G-=1;"*H;print B

Takes input by an array in the format [resource type, units available, units to purchase]. Output is an integer. Will try to golf more over time.

Try It Online! (Ideone)


Lua, 107 101 Bytes

Recursive function that have to be called with f(resource,stock,buy). resource have to be a number between 0 and 3. The output is done via the returned value.

Thanks LeakyNun for saving me 6 bytes : (25-y+(y-1)%3)/3 is shorter than 8-math.floor((y-1)/3) by 5 bytes and allow me to gain one more byte due to its placement.

function f(x,y,z)return z<1 and 0or(x<3 and(25-y+(y-1)%3)/3or(y<5 and 18-y*2or 13-y))+f(x,y-1,z-1)end


function f(x,y,z)                      -- define a function f with 3 parameters
  return z<1                           -- if we don't buy anything else
           and 0                       --   return 0
         or(                           -- else
           x<3                         --   if we're not buying Uranium
             and (25-y+(y-1)%3)/3      --     return 8-floor((stock-1)/3)                       
           or(y<5                      --   elseif there's less than 5 Uranium left
                and 18-y*2             --     return 18-stock*2
              or 13-y))                --   else return 13-stock
         +f(x,y-1,z-1)                 -- if we bought this iteration
                                       -- add f(resource,stock-1,toBuy-1) 
                                       -- to the returned value

You can test this code online by copy-pasting the following snippet.

function f(x,y,z)return z<1 and 0or(x<3 and(25-y+(y-1)%3)/3or(y<5 and 18-y*2or 13-y))+f(x,y-1,z-1)end
  • \$\begingroup\$ @Pietu1998 it's Lua 5.3. I don't know about 5.2, but in 5.3 it won't complain about these when they aren't forming hexadecimal values. for instance, 6and won't work because 6a is an hex value, but 6an isn't. \$\endgroup\$
    – Katenkyo
    Jul 12, 2016 at 12:40
  • \$\begingroup\$ 8-math.floor((y-1)/3) is really (25-y+(y-1)%3)/3 \$\endgroup\$
    – Leaky Nun
    Jul 18, 2016 at 8:09

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.