Risk Battle: Going Down

16

2

Given two lists of dice rolls for a battle in Risk, your program or function must output how many troops each player loses.

Background

You do not have to read this, for it is merely background. Skip to "Task" subheading to continue unabated.

In the game of Risk, one player can attack another player (in fact, this is necessary to win). The outcome of a battle is determined by the roll of dice. Every battle occurs as a succession of sub-battles in which each player can lose up to 2 of their army pieces.

In a sub-battle, the defender and attacker each roll several dice whose number may vary based on circumstances irrelevant to this challenge. The highest-valued die of the attacker is compared to the highest-valued die of the defender. If the attacker's die is higher than the defender's die, the defender loses one piece. Otherwise, the attacker loses one piece.

Then, if both players have at least two dice, the second-highest valued dice of the two players are compared. Again, if the attacker's die is higher than the defender's die, the defender loses one piece. Otherwise, the attacker loses one piece.

(Defender wins ties. If both defender and attacker roll a 4, then the attacker loses a piece.)

Dice Comparison

In this sub-battle from the Wikipedia article, the attacker's dice are red and the defender's dice are white. The highest of the attacker's dice is 4 and the highest of the defender's is 3. Since the attacker was higher, the defender loses a piece. The second-highest are 3 for the attacker and 2 for the defender. Since the attacker was again higher, the defender loses another piece. Thus in this sub-battle, the attacker loses no pieces and the defender loses 2 pieces.

Note that the third-highest pieces are not compared. This is because the defender has no more than two dice on a single sub-battle, so there are no third-highest pieces to compare ever.

Task

Given the unsorted dice rolls (integers in the range 1 to 6 inclusive) of both the attacker and the defender of a sub-battle of Risk in any convenient form, output the number of army pieces each player loses. The output may be in any convenient form, as long as it has different outputs to indicate the five possibilities. You must indicate what those different outputs are in your question.

The output is determined as follows: Start with def=0 and atk=0. If the greatest value of the list of dice rolls of the attacker is greater than the greatest value of the list of dice rolls of the defender, then increment def. Otherwise, increment atk.

If both lists of dice rolls have length at least 2, then: if the second-greatest value of the list of dice rolls of the attacker is greater than the second-greatest value of the list, then increment def and otherwise increment atk.

Finally, the program or function must output a unique identifier for each of the following 5 output possibilities:

  ╔═══╦═══╗
  ║atk║def║
  ╠═══╬═══╣
  ║ 1 ║ 0 ║
  ║ 0 ║ 1 ║
  ║ 2 ║ 0 ║
  ║ 1 ║ 1 ║
  ║ 0 ║ 2 ║
  ╚═══╩═══╝

Example

Defender: [3, 2] Attacker: [2, 4, 1] Max of defender is 3 and max of attacker is 4. 4>3, so def=1 Second of defender is 2 and second of attacker is 2. Not(2>2), so atk=1. The output could then be [1,1].

Test Cases

Defender
Attacker
Output (as [def,atk])
-----
[1]
[1]
[0,1]
-----
[6,6]
[1,1,1]
[0,2]
-----
[1,2]
[5,2,3]
[2,0]
-----
[5]
[3,4]
[0,1]
-----
[4]
[4,5]
[1,0]
-----
[1,3]
[1,2,3]
[1,1]
-----
[4]
[4,5,6]
[1,0]
-----
[4,5]
[6,2]
[1,1]
-----
[5]
[6,1,3]
[1,0]
-----
[5,5]
[4,4,1]
[0,2]
-----
[2,5]
[2,2]
[0,2]
-----
[6,6]
[4,4,3]
[0,2]
-----
[2,1]
[4,3]
[2,0]
-----
[4]
[1,5]
[1,0]
-----
[1]
[5,2]
[1,0]
-----
[6,2]
[4]
[0,1]
-----
[4,2]
[2,5,5]
[2,0]
-----
[2]
[6,6,2]
[1,0]
-----
[6]
[2,6]
[0,1]
-----
[3,1]
[1]
[0,1]
-----
[6,2]
[3,5,2]
[1,1]
-----
[4,2]
[1,1]
[0,2]
-----
[4,3]
[5,4,1]
[2,0]
-----
[5,6]
[1,2]
[0,2]
-----
[3,2]
[4,4]
[2,0]
-----
[2]
[6,3,4]
[1,0]
-----
[1,4]
[6,2,4]
[2,0]
-----
[4,2]
[2,5,4]
[2,0]
-----
[5]
[6,2,1]
[1,0]
-----
[3]
[2,5,4]
[1,0]
-----
[5,4]
[2]
[0,1]
-----
[6,3]
[2,6,5]
[1,1]
-----
[3,1]
[4]
[1,0]
-----
[4]
[6,6,5]
[1,0]
-----
[6,3]
[4,2]
[0,2]
-----
[1,6]
[5,4]
[1,1]
-----
[3,6]
[4,4]
[1,1]
-----
[5,4]
[5,1,1]
[0,2]
-----
[6,3]
[5,4]
[1,1]
-----
[2,6]
[1,2]
[0,2]
-----
[4,2]
[3,5,5]
[2,0]
-----
[1]
[1,2,1]
[1,0]
-----
[4,5]
[1,6]
[1,1]
-----
[1]
[3,5,1]
[1,0]
-----
[6,2]
[6,2]
[0,2]

Sample implementation

Python 2 or 3

def risk(atk_rolls,def_rolls):
    # set the rolls in descending order, e.g. [5,3,2]
    atk_rolls = sorted(atk_rolls,reverse = True)
    def_rolls = sorted(def_rolls,reverse = True)
    # minimum length.
    minlen = min(len(atk_rolls),len(def_rolls))
    atk_lost = 0
    def_lost = 0
    # compare the highest-valued rolls
    if atk_rolls[0]>def_rolls[0]:
        def_lost += 1
    else:
        atk_lost += 1
    if minlen == 2:
        # compare the second-highest-valued rolls
        if atk_rolls[1] > def_rolls[1]:
            def_lost += 1
        else:
            atk_lost += 1
    return [def_lost, atk_lost]    

Specifications

  • The input may be taken as any form that clearly encodes only the defender's rolls and the attacker's rolls.
  • The output may be in any form that provides a unique output for each of the five possibilities listed above.
  • The defender's rolls are a list of 1 or 2 integers in the set [1,2,3,4,5,6]. The attacker's rolls are a list of 1 to 3 integers in the set [1,2,3,4,5,6].
  • Since this is , shortest code in each language wins! Do not let answers in golfing languages discourage you from posting answers in other languages.

fireflame241

Posted 2017-07-27T19:48:35.183

Reputation: 7 021

Saw this on the sandbox, nice question – Noah Cristino – 2017-07-27T19:51:03.347

https://codegolf.meta.stackexchange.com/a/13456/61877 – Noah Cristino – 2017-07-27T19:51:06.197

The attacker loses if its maximum roll is equal to the defender's highest roll, right? – Mr. Xcoder – 2017-07-27T20:02:32.950

1Yes @Mr.Xcoder, the defender wins ties. – fireflame241 – 2017-07-27T20:03:43.100

Hence I deleted the comment :) – Mr. Xcoder – 2017-07-27T20:43:50.240

Answers

8

NAND gates, 237

Created with Logisim

Inputs are 3-bit unsigned binary, entered on the left. Outputs (2 bits) are on the right.

It is too large to fit on the screen and Logisim can't zoom, so the image is black-and-white. Sorry :(

Works for all test cases.

There is likely a better way to do this using some memory circuit, allowing large sections to be reused.

Khuldraeseth na'Barya

Posted 2017-07-27T19:48:35.183

Reputation: 2 608

4

Jelly,  12  11 bytes

NṢ€>/Ṡḟ-o-S

A monadic link taking a list of Defender, Attacker rolls (each as lists), returning an integer between -2 and 2 inclusive (defender losses - attacker losses):

result : [def, atk]
    -2 : [  0,   2]
    -1 : [  0,   1]
     0 : [  1,   1]
     1 : [  1,   0]
     2 : [  2,   0]

Try it online! or see a test suite (which maps the results to the OP format).

How?

NṢ€>/Ṡḟ-o-S - Link: list [list Def, list Atk]
N           - negate all the rolls
 Ṣ€         - sort €ach of the lists of -1*rolls (max rolls are to the left now)
    /       - reduce by:
   >        -   is greater than?  (when len(Atk) > len(Def) leaves trailing negatives)
     Ṡ      - sign (maps all negatives to -1; zeros and ones of comparison unchanged)
       -    - literal -1
      ḟ     - filter discard (remove the -1s)
         -  - literal -1
        o   - logical or, vectorises (replaces the zeros with minus ones)
          S - sum

Jonathan Allan

Posted 2017-07-27T19:48:35.183

Reputation: 67 804

2

JavaScript (SpiderMonkey), 97 83 78 bytes

d=>a=>{for(u=v=0;d.sort()>[]&a.sort()>[];)a.pop()>d.pop()?u++:v++
return[u,v]}

Try it online!

-4 bytes and fixed thanks to @ovs and @Craig Ayre
-1 byte thanks to @Shaggy

WaffleCohn

Posted 2017-07-27T19:48:35.183

Reputation: 311

Doesn't work for one attacker against two defenders. – Neil – 2017-07-27T21:16:35.240

Oh hmm I didn't think about that. I'll fix it – WaffleCohn – 2017-07-27T22:29:19.930

2This might work. – ovs – 2017-07-28T10:14:15.207

@ovs's solution passes all test cases, you can save a few bytes too (tio link was too large): d=>a=>{for(u=v=0;d.sort()>[]&a.sort()>[];)a.pop()>d.pop()?u++:v++;return[u,v]} – Craig Ayre – 2017-07-28T11:19:22.020

The first line break is unnecessary and costing you a byte. – Shaggy – 2017-07-28T13:40:47.543

Ah yes, you're right – WaffleCohn – 2017-07-28T13:54:24.893

2

Retina, 82 bytes

%O^`.
((.)+).*(¶(?<-2>.)+)(?(2)(?!)).*
$1$3
O$`.
$.%`
\d
$*1D
(1+)D1*\1

1+D
A
O`.

Try it online! First line of input is attacker's dice, second is defender's dice. Returns (on separate lines) AA, AD, DD, A or D as appropriate.

Neil

Posted 2017-07-27T19:48:35.183

Reputation: 95 035

2

Python 2, 83 75 bytes

x=0
k=map(sorted,input())
while all(k):x+=cmp(*map(list.pop,k))or 1
print-x

Try it online!

Output is defender losses - attacker losses

ovs

Posted 2017-07-27T19:48:35.183

Reputation: 21 408

2

MATL, 23 bytes

oH2$S1&Y)Y&t1M>t~b,Y&sD

Try it online!

Not sure why defenders are allowed more dice than attackers, but maybe I am not that well versed in Risk. The core program is just >t~,sD, all the other bytes are there to allow for different input lengths, with a bit of sorting thrown in. Input is attacker followed by defender, output is attacker losses followed by defender losses.

o    % Convert input to numeric array, padding with zeroes 
H2$S % Sort row-wise (specified to prevent 1v1 sorting)
1&Y) % Split attacker/defender
Y&t  % Logical and to filter out excess dice. Duplicate for 'do twice' later.
1M>  % Get throws again, decide who won
t~   % And the inverse to decide who lost
b,   % Bubble filter to the top. Do twice:
  Y& % Apply filter
  sD % Sum of losses. Display.

Sanchises

Posted 2017-07-27T19:48:35.183

Reputation: 8 530

2

Husk, 10 bytes

M#eI¬¤z>Ö>

Try it online!

Input as two separate lists of rolls, output as in the op.

Explanation

¤z>Ö> sorts each list in descending order and then zips them comparing corresponding elements (and truncating the longer list).

M#eI¬ creates a 2-element list (e) with counts (#) of truthy values (through identity I) and falsy values (through logical negation ¬)

Leo

Posted 2017-07-27T19:48:35.183

Reputation: 8 482

1

Perl 5, 66 + 1 (-a) = 67 bytes

@A=sort split/ /,<>;$b+=@A?pop@A>$_?-1:1:0for reverse sort@F;say$b

Try it online!

Input:

Two lines. First line is defender (player 1), second is attacker (player 2). Individual rolls separated by spaces.

Output:

Indicates effective change in defender's strength vs. attacker.

Output Attacker Defender
   2      0        2        Defender wins both
   1      0        1        Defender wins the only roll
   0      1        1        Attacker wins first, defender wins second
  -1      1        0        Attacker wins the only roll
  -2      2        0        Attacker wins both rolls

Xcali

Posted 2017-07-27T19:48:35.183

Reputation: 7 671

"The program or function must output a unique identifier for each of the 5 output possibilities." Yours has two outputs for [1,1]. Please edit your answer to fix this ( just sort or sum) – fireflame241 – 2017-07-28T14:56:48.597

What's wrong with six? It's more specific. :)

I changed it at a cost of 6 bytes. – Xcali – 2017-07-28T15:18:03.017

0

Jelly, 30 bytes

0,0‘⁸Ṁ€</¤¦‘⁸ḟ"Ṁ€⁺</¤¦⁸L€Ṃ>1¤¡

Try it online!

Veeeeeeeery ungolfed! >_<

Outputs values exactly as in test cases.

Erik the Outgolfer

Posted 2017-07-27T19:48:35.183

Reputation: 38 134

Wow, this seems way too long even to me :P – Mr. Xcoder – 2017-07-27T20:23:39.703

0

R, 46 bytes

function(x,y)s(s(y,T)[1:2]>s(x,T)[1:2])
s=sort

Try it online!

All this does is three sorts and one comparison... plus extracting the first two elements in the middle.

Input is two vectors of dice rolls.

Output encoded as follows:

╔═══╦═══╗
║atk║def║
╠═══╬═══╣
║ 1 ║ 0 ║ TRUE
║ 0 ║ 1 ║ FALSE
║ 2 ║ 0 ║ TRUE  TRUE
║ 1 ║ 1 ║ FALSE TRUE
║ 0 ║ 2 ║ FALSE FALSE
╚═══╩═══╝

Works because extraction in R does not recycle its argument, but pads the result with NA to get to the requested length.

JayCe

Posted 2017-07-27T19:48:35.183

Reputation: 2 655