28
\$\begingroup\$

The boardgame Terra Mystica has some very interesting mechanics for one of the primary resources, power. Instead of gaining and spending units of power from a bank, each player starts the game with exactly 12 units of power which are distributed over three "bowls", which are labelled I, II and III. Gaining and spending power then simply shifts power between these bowls:

  • To spend a unit of power, move it from bowl III to bowl I (provided you have a unit in bowl III).
  • When you gain a unit of power, if there is a unit in bowl I, move it to bowl II. If there are no units in bowl I, but there is a unit in bowl II, move it to bowl III. If all units are already in bowl III, nothing happens.
  • When you gain or spend multiple units at once, they are processed one unit at a time.

Here is an example. Say, a player starts with the following power distribution (given in order I | II | III):

5 | 7 | 0

Their power changes as follows if they gain and spend power a few times:

               5 |  7 |  0
Gain  3  ==>   2 | 10 |  0
Gain  6  ==>   0 |  8 |  4   (move 2 power from I to II, 
                              then the remaining 4 from II to III)
Gain  7  ==>   0 |  1 | 11
Spend 4  ==>   4 |  1 |  7
Gain  1  ==>   3 |  2 |  7
Spend 7  ==>  10 |  2 |  0
Gain 12  ==>   0 | 10 |  2   (move 10 power from I to II,
                              then the remaining 2 from II to III)
Gain 12  ==>   0 |  0 | 12   (the two excess units go to waste)

Your task is to compute the result of one such gaining or spending event.

The Challenge

You are given four integers as input. The first three, I, II, III, represent the amount of power in each of the three bowls. They will be non-negative, and they will sum to 12. The fourth number, P, is the amount of power gained or spent, and will be in the inclusive range [-III, 24] (so you may assume that the player will never try to spend more power than they currently can, but they might be gaining more power than they need to move all power into bowl III).

You may take these numbers in any consistent order, as separate arguments, as a list of integers, or as a string containing these integers. You can also take P as one argument, as I, II, III as a separate list argument.

You should output three integers I', II', III' which represent the amount of power in each bowl after P units were gained or spent, following the rules explained above.

You may write a program or a function and use any of the our standard methods of receiving input and providing output.

You may use any programming language, but note that these loopholes are forbidden by default.

This is , so the shortest valid answer – measured in bytes – wins.

Test Cases

I II III P => I' II' III'
5 7 0 3    => 2 10 0
2 10 0 6   => 0 8 4
0 8 4 7    => 0 1 11
0 1 11 -4  => 4 1 7
4 1 7 0    => 4 1 7
4 1 7 1    => 3 2 7
3 2 7 -7   => 10 2 0
10 2 0 12  => 0 10 2
0 10 2 12  => 0 0 12
\$\endgroup\$
  • 1
    \$\begingroup\$ I recommend removing gender-specific pronouns and replacing them with gender-neutral ones (or restructuring sentences): gamers do not have to be male. \$\endgroup\$ – Greg Martin Mar 6 '17 at 22:13
  • 1
    \$\begingroup\$ @GregMartin Of course. Did I catch them all? \$\endgroup\$ – Martin Ender Mar 6 '17 at 22:14
  • 2
    \$\begingroup\$ Look like it; thank you for thinking about it! Also, is Terra Mystica as awesome as I've heard? \$\endgroup\$ – Greg Martin Mar 6 '17 at 22:16
  • 4
    \$\begingroup\$ @GregMartin yes. :) \$\endgroup\$ – Martin Ender Mar 6 '17 at 22:16
  • 5
    \$\begingroup\$ No power burns from bowl 2? This just feels so incomplete. \$\endgroup\$ – moreON Mar 7 '17 at 4:24

12 Answers 12

6
\$\begingroup\$

Mathematica, 52 bytes

{x=#-#4~Min~#,y=Max[#2+#-Abs[#4~Max~0-#],0],12-x-y}&

This is an unnamed function that takes a list {I, II, III, P} as input and returns a list {I', II', III'}.

A closed-form solution. It doesn't really feel optimal yet...

\$\endgroup\$
  • \$\begingroup\$ Thought I could shorten, but {##,12-+##}&[#-#4~Min~#,Max[#2+#-Abs[#4~Max~0-#],0]]& is a byte longer. I like the 12-+## though. \$\endgroup\$ – Greg Martin Mar 6 '17 at 22:30
  • 1
    \$\begingroup\$ @GregMartin I tried the same thing :) \$\endgroup\$ – Martin Ender Mar 6 '17 at 22:35
6
\$\begingroup\$

C, 97 94 bytes

f(i,j,k,n){for(;n;n-=n/abs(n))n<0?k?++i+--k:0:i?++j+--i:j?++k+--j:0;printf("%d %d %d",i,j,k);}

In ungolfed form:

f(i, j, k, n) {
    while (n) {
        if (n < 0) {
            if (k) {
                ++i; --k;
            }
            ++n;
        } else {
            if (i) {
                ++j; --i;
            }
            else if (j) {
                ++k; --j;
            }
            --n;
        }
    }
    printf("%d %d %d", i, j, k);
}
\$\endgroup\$
5
\$\begingroup\$

Python 2, 104 bytes

def f(i,d,t,g):
 x=min(i,g);i-=x;q=g>0;g-=x
 if q:d+=x;x=min(d,g);g-=x;d-=x;t+=x
 else:t+=x
 print i,d,t

Try it online

Ungolfed:

def f(i,d,t,g):
 if g>0:
    x=min(i,g)
    g-=x
    i-=x
    d+=x    
    x=min(d,g)
    g-=x
    d-=x
    t+=x
 else:
    x=min(i,g)
    g-=x
    i-=x
    t+=x
 print(i,d,t)
\$\endgroup\$
5
\$\begingroup\$

Haskell, 58 bytes

f(a,b,c)d|m<-min a d,z<-min(c+d-max 0 m)12=(a-m,b+c+m-z,z)

The intermediate value m denotes the amount of power going from (or to, if negative) the first bowl, z denotes the amount of power in the third bowl after the action. A last minute one byte optimization changed the old expression for the second bowl from 12-a+m-z using the identity a+b+c=12.

The natural result type is a triple for the bowls, so the input also takes the bowls as a triple and the power change as a second argument. This allows to handle all test cases with one application of scanl:

*Main> scanl f (5,7,0) [3,6,7,-4,0,1,-7,12,12]
[(5,7,0),(2,10,0),(0,8,4),(0,1,11),(4,1,7),(4,1,7),(3,2,7),(10,2,0),(0,10,2),(0,0,12)]
\$\endgroup\$
5
\$\begingroup\$

Röda, 100 94 bytes

f a,b,c,p{{c+=p;a-=p}if[p<0]else{{a--;b++;p--}while[p*a>0];{b--;c++;p--}while[p*b>0]};[a,b,c]}

Ungolfed:

f a,b,c,p {
    if [ p < 0 ] do
        c += p
        a -= p
    else
        { a-=1; b+=1; p-=1 } while [ p > 0 and a > 0 ]
        { b-=1; c+=1; p-=1 } while [ p > 0 and b > 0 ]
    done
    return a, b, c
}
\$\endgroup\$
  • \$\begingroup\$ Doesn't Röda have the ++ and -- operators? \$\endgroup\$ – Cows quack Mar 7 '17 at 5:23
  • \$\begingroup\$ @KritixiLithos Thanks! Yes it does. \$\endgroup\$ – fergusq Mar 7 '17 at 7:15
4
\$\begingroup\$

JavaScript, 61 59 bytes

(a,b,c,d)=>[r=a-d>0?a-d:0,s=r?b:b-d+2*a>0?b-d+2*a:0,12-r-s]

Try it online!

\$\endgroup\$
3
\$\begingroup\$

GNU sed, 66 bytes

Includes +1 for -r

/-/!{:
s/1,(.* )1/,1\1/
t}
s/(.*)(1+) -\2/\2\1/
s/(,,1{12}).*/\1/

Uses unary (see this consensus).

Try it online!

/-/!{                  # If there is not a '-'
  :                    # start loop
  s/1,(.* )1/,1\1/     # move a 1 from before a ',' to after the ',' for every 1 after the space
                       # sed reads left to right, so this takes everything from the first bowl before starting on the second
  t                    # loop if something changed
}                      # end if
s/(.*)(1+) -\2/\2\1/   # take all of the 1s from after a '-' and move them to the begining.
                       # at the same time, remove that many 1s from the 3rd bowl
s/(,,1{12}).*/\1/      # remove everything after 12 1s in the third bowl
\$\endgroup\$
3
\$\begingroup\$

Retina,  46  41 39 38 bytes

Thanks to Martin Ender for multiple helpful suggestions!

+`1,(.*¶)1
,1$1
(.*)(1+)¶-\2$
$2$1
G`,

Takes input in unary. The first line contains the amounts of power in the three bowls, comma separated, the second line the amount of power to cycle.

Test suite - Takes all inputs on a single line and converts from decimal to unary and vice-versa for convenience of use.

Explanation

+`1,(.*¶)1
,1$1

Positive case: we repeatedly remove the leading 1 from the second line, and move a 1 from the first non-empty bowl to the following one, as long as this operation is possible (i.e. the number of power to cycle is non-zero and not all power is in the third bowl). The s modifier means single-line, allowing . to match also the newline.

(.*)(1+)¶-\2$
$2$1

Negative case: done all in one step, moving the amount of power indicated by the last input from the third to the first bowl. This will also remove the line containing the negative amount of power to move.

G`,

Keep (grep) only lines containing a comma. This will get rid of the eventual remains of the first line.

\$\endgroup\$
3
\$\begingroup\$

Python 2, 91 bytes

Based on this answer

def f(i,d,t,g):
 x=min(i,g);i-=x
 if g>0:y=min(d+x,g-x);d+=x-y;t+=y
 else:t+=x
 print i,d,t

Try it online

Ungolfed:

def f(i,d,t,g):
 if g>0:
    x=min(i,g)
    y=min(d+x,g-x)
    i-=x
    d+=x-y
    t+=y
 else:
    x=min(i,g)
    i-=x
    t+=x
 print(i,d,t)
\$\endgroup\$
  • \$\begingroup\$ Welcome to the site! \$\endgroup\$ – DJMcMayhem Mar 8 '17 at 17:50
2
\$\begingroup\$

Batch, 87 bytes

@set/a"i=%4-%1,j=%4*(-%4>>5)-%2-2*i*(-i>>5),i*=i>>5,j*=j>>5,k=12-i-j
@echo %i% %j% %k%

Use the following formulae:

I' = min(I - P, 0)
II' = min(II + min(P, 0) - 2 * min(P - I, 0), 0)
III' = 12 - I' - II'

Since Batch doesn't have a less than operator, I calculate i = min(-i, 0) using i*=i>>5.

\$\endgroup\$
2
\$\begingroup\$

Perl 6, 99 bytes

->\a,\b,\c,\d{d>0??[»+»] (a,b,c),|(|((-1,1,0)xx a),|((0,-1,1)xx a+b),|(0 xx*))[^d]!!(a- d,b,c+d)}

Let a, b, and c be the number of starting tokens in bowls I, II, and III respectively. Then, for the adding power case, a list is created that contains a copies of the triplet (-1, 1, 0), followed by a + b copies of the triplet (0, -1, 1), followed by infinite copies of 0. The first d elements of this list, d being the amount of power to add, are added elementwise to the starting power distribution.

For subtracting power (negative d), a simple closed form is used: (a - d, b, c + d).

\$\endgroup\$
2
\$\begingroup\$

tinylisp, 134 bytes

(d f(q((x y z p)(i p(i(l p 0)(f(s x p)y(a z p)0)(i x(f(s x 1)(a y 1)z(s p 1))(i y(f x(s y 1)(a z 1)(s p 1))(f x y z 0))))(c x(c y(c z(

Defines a function f that takes four arguments, the three bowls (x y z) and the amount of power transacted (p), and returns a list of the three bowls after the transaction. Here's a properly spaced version with all test cases: Try it online!

(d f                         Define f to be
 (q(                          a quoted two-item list (which acts as a function):
  (x y z p)                    Arglist: the three bowls x y z and power p
  (i p                         If p is nonzero
   (i (l p 0)                   then if p is negative (spending power)
    (f(s x p)y(a z p)0)          then take -p from z, add -p to x, and recurse with p=0
    (i x                         else (gaining power), if x is nonzero
     (f(s x 1)(a y 1)z(s p 1))    then take 1 from x, add to y, decrement p and recurse
     (i y                         else if y is nonzero
      (f x(s y 1)(a z 1)(s p 1))   then take 1 from y, add to z, decrement p and recurse
      (f x y z 0))))               else no moves possible; recurse with p=0
   (c x(c y(c z())))))))        else (p=0), cons x y z into a list and return it
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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