Simulate a 'battle' in the playing card game 'Oorlog'

15

1

Let's build a simulation for an aspect in the card game, which I personally know by the Dutch name 'Oorlog' (translates to 'War').

How does 'Oorlog' work?

Two decks of cards (each including two Jokers) are equally divided between the amount of players playing. Each player shuffles their own stock, put it upside down in front of them, and all players open up the first card of the stock at the same time.
The winner of that 'battle' is determined by the values of the cards following these rules: Joker/Ace defeats King; King defeats Queen; Queen defeats Jack; Jack defeats 10; 10 defeats 9; .... In addition, both 2 and 3 defeat Ace/Joker. The last rule may lead to a cycle where 2 or 3 beats Ace or Joker, Ace or Joker beats some other card, which in turn beats the 2 or 3. In this case, the 2 or 3 wins the battle.
(Suit is irrelevant in this card game.)

When two or more players have the equal highest cards, they have a 'war'. This means they put one card upside down, and then each opens up a new card from their stock, again looking who has the highest card. This continues until a single player wins the entire battle.
(All cards of that battle go to the discard pile of the player that won the battle. Then everyone opens up a new card. When a player's stock is out of cards, they turn their discard pile upside down and continue with this new stock. This continues until a player is out of all his/her cards and then the player with the highest amount of cards wins.)

Example 'battles' with three players:

  1. 4, 8, Jack:
    Jack wins.
  2. 7, Ace, Queen:
    Ace wins.
  3. 10, 10, King:
    King wins.
  4. 3, Joker, 2:
    3 wins.
  5. Ace, Joker, 2:
    2 wins.
  6. 3, Queen, Ace:
    3 wins.
  7. Queen, Queen, 9:
    Queen & Queen are having a 'war', so it continues with two new cards: 4, 8;
    8 wins.
  8. 4, 4, 4:
    All are having a 'war', so it continues with three new cards: 8, Ace, 2;
    2 wins.
  9. Jack, 5, Jack:
    Jack & Jack are having a 'war', so it continues with two new cards: 5, 5;
    5 & 5 are also equal, so the 'war' continues again with two new cards: 10, King;
    King wins.
  10. Joker, Joker, Ace:
    All are having a 'war', so it continue with three new cards: 9, 7, 9;
    9 & 9 are also equal, so the 'war' continues with two new cards: Jack, 3;
    Jack wins.

So, onto the code challenge:

Input:

STDIN with an array, or a string simulating an array (your call - even if your language does support arrays). This array contains the cards of a battle in chronological order (see test cases for a clearer understanding of this).

Output:

STDOUT the index of the player that won the battle.
You can choose whether you want a zero-indexed (i.e. 0, 1, or 2) or one-indexed output (i.e. 1, 2, 3).

Challenge rules:

  • The input will be a single array / string representing an array. So you can't have an array of arrays to simplify it. You also can't have surrogate items for cards not participating in the war.
  • We use number notations for the face-cards instead of letter notation. So Ace/Joker = 1; Jack = 11; Queen = 12; and King = 13.
  • In this challenge we can assume we are always playing with 3 players.
  • The first three indicate the start of the 'battle'. When two or more players have a 'war' the continuing cards in the array indicate their battle (see test cases for a clearer understanding of this).

General rules:

  • This is tagged , so shortest answer in bytes wins.
    This doesn't mean non code-golfing languages shouldn't enter. Try to come up with an as short as possible code-golf answer for 'every' programming language.
  • Please mention which indexing (zero- or one-indexed) you've used for the output.

Test cases:

Test case 1:  [4, 8, 11]                 ->  2 (or 3)
Test case 2:  [7, 1, 12]                 ->  1 (or 2)
Test case 3:  [10, 10, 13]               ->  2 (or 3)
Test case 4:  [3, 1, 2]                  ->  0 (or 1)
Test case 5:  [1, 1, 2]                  ->  2 (or 3)
Test case 6:  [3, 12, 1]                 ->  0 (or 1)
Test case 7:  [12, 12, 9, 4, 8]          ->  1 (or 2)
Test case 8:  [4, 4, 4, 8, 1, 2]         ->  2 (or 3)
Test case 9:  [11, 5, 11, 5, 5, 10, 13]  ->  2 (or 3)
Test case 10: [1, 1, 1, 9, 7, 9, 11, 3]  ->  0 (or 1)
Test case 11: [13, 13, 4, 1, 3]          ->  1 (or 2)
Test case 12: [13, 4, 13, 2, 3]          ->  2 (or 3)

Kevin Cruijssen

Posted 2016-06-03T06:56:46.180

Reputation: 67 575

4

The English name is indeed War (minus the jokers and minus the 2-and-3-beat-ace rule).

– Martin Ender – 2016-06-03T07:11:32.803

@MartinEnder also, when there is a tie, both players flip 3 cards face down, and a 4th face up. The 4th decides the winner of the round, and the face down cards are the "spoils of war". Also, doesn't the game keep going until 1 player has all the cards? I dunno if this was a local rule or not, anyone else remember this? That's how I remember playing. – Magic Octopus Urn – 2017-10-06T19:55:05.513

1@MagicOctopusUrn I remember flipping over the discard piles to continue playing until one player had it all. – Kamil Drakari – 2017-10-06T20:03:35.867

1@KamilDrakari yeah! That's the way I played too. I was in Louisiana growing up playing this. – Magic Octopus Urn – 2017-10-06T20:27:14.647

@MagicOctopusUrn My experience is from Minnesota, and since we now have 2 data points I think it's safe to say all of America is the same. – Kamil Drakari – 2017-10-06T20:49:12.120

@MagicOctopusUrn The way I played it was with one card face down for each played who was at war. So when the opening is 10, 10, 4, the first two players laid down a face-down card AND a face-up card. And then the two face-up cards are compared again. And the winner of the war indeed took all, both face-up and face-down. Also, because it usually takes quite a while, we continued until one player had no cards left, and whomever had the most at that moment won. But you could continue until one player has all cards. – Kevin Cruijssen – 2017-10-07T08:56:05.550

Answers

4

q - 142 characters

{p:til 3;while[1<(#:)p;h:(n:(#:)p)#x;x:n _x;$[1~min h;p:$[max a:h in(2;3);$[1<(#:)(?:)h(&:)a;p(&:)h=3;p(&:)a];p(&:)h=1];p:p(&:)h=max h]];(*:)p}

Note: zero indexed.

There is no notion of reading stdin in q, so you should call it as a function: {p:til 3;while[1<(#:)p;h:(n:(#:)p)#x;x:n _x;$[1~min h;p:$[max a:h in(2;3);$[1<(#:)(?:)h(&:)a;p(&:)h=3;p(&:)a];p(&:)h=1];p:p(&:)h=max h]];(*:)p}[1,2,3]

Pretty long, actually, but there are plenty of corner cases. It keeps a roster of active players, and consumes the list of cards in a loop. The most problematic thing is to detect the correct winner in hands like [13, 2, 3], since 3 beats 2, as normal, but it had to be duplicated to be put into the corner case.

C. Quilley

Posted 2016-06-03T06:56:46.180

Reputation: 546

3

JavaScript (ES6), 146 bytes

f=a=>(b=a.splice(0,3)).filter(m=>m-n,n=Math.max(...b.includes(1)?b.filter(m=>m<4):b)).length>1?b.indexOf(n):f([...b.map(m=>m-n?0:a.shift()),...a])

Returns a zero-based index. 127 bytes if I'm allowed the initial deal as a separate array (this also works for an arbitrary number of hands of course):

f=(b,a)=>b.filter(m=>m==n,n=Math.max(...b.includes(1)?b.filter(m=>m<4):b)).length<2?b.indexOf(n):f(b.map(m=>m-n?0:a.shift()),a)

Neil

Posted 2016-06-03T06:56:46.180

Reputation: 95 035

0

Java 8, 257 bytes

int c(int[]a){int t=0,f=0,q,r=0,i=-1,l=a.length,x[];for(;++i<3;t=a[i]>t?a[r=i]:t)a[i]+=a[i]==1?f++*0+13:0;for(;i-->0;t=f>0&a[i]<4?t!=3?a[r=i]:t>a[i]?a[r=i]:t:t);for(f=-1,x=new int[q=l<7?3:l>7?l-3:5];l>3&++i<q;)x[i]=t==a[i]|i>2?a[++f+3]:0;return l>3?c(x):r;}

Ok, my challenge is harder than I thought it would be with everything in a single array like that. ;) But since it's been more than I year ago since I posted this challenge, I decided to give it a go myself. Took me quite a while with multiple work-arounds and quirks.. So it can definitely be golfed some more, but I'll look into that another time. This already took way longer than I expected..

Explanation:

Try it here.

int c(int[]a){      // Method with integer-array parameter and integer return-type
  int t=0,f=0,q,    //  Temp integers
      r=0,          //  Result-integer
      i=-1,         //  Index-integer
      l=a.length,   //  Length of the input-array
      x[];          //  Temp array we use when there is a war
  for(;++i<3;       //  Loop (1) from 0 to 3 (exclusive)
      t=            //    After every iteration, change `t` to:
        a[i]>t?     //     If the current item is larger than `t`:
         a[r=i]     //      Use the current item (and save its index in `r`)
        :           //     Else:
         t)         //      Leave `t` the same
    a[i]+=a[i]==1?  //   If the current item is a 1 (Ace/Joker):
         f++*0      //    Increase `f` by 1,
         +13        //    and change the 1 to 14 (by increasing with 13)
        :           //   Else:
         0;         //    Leave the current item the same (by increasing with 0)
                    //  End of loop (1) (implicit / single-line body)
  for(;i-->0;       //  Loop from 2 down to 0 (inclusive)
    t=f>0&a[i]<4?   //   If a Joker/Ace was present, and the current item is 2 or 3:
       t!=3?        //    And if it's not a 3 (so either 2 or 14)
        a[r=i]      //     Change `t` to the current item (and save its index in `r`)
       :t>a[i]?     //    Else-if the current item is 2, and `t` is a Joker/Ace:
        a[r=i]      //     Change `t` to the current item (and save its index in `r`
       :            //    Else:
        t           //     Leave `t` the same
      :             //   Else:
       t            //    Leave `t` the same
  );                //  End of loop (2)
  for(f=-1,         //  Reset `f` to -1
      x=new int[    //  Create the temp array `x` with length:
       q=l<7?       //   If the current length is 6 or lower:
          3         //    New length after war is 3
         :l>7?      //   Else-if the current length is 8 or higher:
          l-3       //    New length after war is current length - 3
         :          //   Else(-if current length is 7)
          5];       //    New length after war is 5
      l>3&          //  If there is a war:
          ++i<q;)   //   Loop (3) from 0 to the new length (exclusive)
    x[i]=t==a[i]    //    If the current item is at war,
         |i>2?      //    or the current item is after the first 3 items:
          a[++f+3]  //     Fill the new array with the correct next item
         :          //    Else:
          0;        //     Fill the item of the new array with 0
                    //     (since it won't participate in the next round)
                    //   End of loop (3)
  return l>3?       //  If there was a war:
          c(x)      //   Recursive-call with the new array
         :          //  Else:
          r;        //   Return the index of the card that won
}                   // End of method

Kevin Cruijssen

Posted 2016-06-03T06:56:46.180

Reputation: 67 575