9
\$\begingroup\$

A while ago I purchased a new wallet which is able to hold 8 cards (4 on both side). However, I seem to have way more cards than that and I need to make choices on which ones I want to carry with me. Some cards I use more often than others, but the cards I prefer to carry with me are not necessarily the ones I use most.

The challenge

Given a stack of cards, return the layout of my wallet in the best way possible w.r.t. my preferences and restrictions. The layout should be as follows:

__ __ (row 1)
__ __ (row 2)
__ __ (row 3)
__ __ (row 4)

Currently I posess the following cards - stacks will always consist of a selection from these:

  • 1 identity card (ID)
  • 1 driver's license (DL)
  • 2 credit cards (CC)
  • 5 debit cards (DC)
  • 1 public transport card (PC)
  • 1 gym access card (GC)
  • 9 membership cards from random stores and warehouses (MC)

I have some preferences and restrictions:

  • Cards sorted by priority: ID, DL, CC, DC, PC, GC, MC
  • Cards sorted by usage frequency: CC, DC, PC, GC, MC, ID, DL
  • For safety reasons, the total number of debit cards and credit cards in my wallet can be at most 1 more than the sum of all other cards that will go in my wallet ( NDC+NCCNID+NDL+NPC+NGC+NMC+1 ).
  • If present, my identity card and driver's licence should always go in row 1. This does not mean that other cards may not occupy spots in row 1.
  • The most frequently used cards from the stack should always go in row 4.

Rules

  • No 2 cards can occupy the same spot.
  • Higher priority cards are always prefered over lower priority ones, unless the DC/CC restriction kicks in.
  • ID/DL at row 1 overrules the frequency rule: if only ID is provided, it will go in row 1 and row 4 will be empty!
  • Input formatting can be done in any way you like, as long as the order of the input stack is retained. e.g. ID,CC,PC,MC,MC,MC,DL may also be supplied as e.g. 1ID 1CC 1PC 3MC 1DL 0DC 0GC or ID CC PC MC MC MC DL.
  • Output formatting does have a few restrictions: rows must all start at a new line, columns must be delimited in some way. Empty spots may be presented any way you like, as long as it does not mess up the 4x2 layout.

  • There can be more than one solution/order, it's up to you which one you provide as output.

  • You may assume that cards of the same type will always be grouped at input.
  • Apart from the above, standard rules and loopholes apply.

Bonus

You are allowed to remove 15% of your bytecount if you also return any cards that did not go in the wallet. Print "It fits!" in case of no remaining cards. This additional output should be clearly separated from the returend layout.

Examples

Input:

ID, DL, CC, GC, MC

2 possible outputs:

ID DL      DL ID
__ __  or  __ MC
MC __      __ __
CC GC      GC CC

optional: It fits!

Input:

ID, CC, DC, PC, GC, MC, MC, MC, MC, MC

2 possible outputs:

ID MC      GC ID
MC MC  or  MC PC
PC GC      MC MC
CC DC      DC CC

optional: e.g. (MC, MC)  or  (2MC)

Input:

DC, DC, CC, CC, GC, DL

2 possible outputs:

DL __      GC DL
__ __  or  DC __
GC DC      __ __
CC CC      CC CC

optional: e.g. (DC)  or  (1DC)

Input:

CC, DC, DC, DC

2 possible outputs:

__ __      __ __
__ __  or  __ __
__ __      __ __
CC __      __ CC

optional: e.g. (DC, DC, DC)  or  (3DC)

Input:

CC, CC, MC, MC, MC, MC, MC, MC, PC, DC, DC, DC, DC, DC, GC

2 possible outputs:

MC MC      MC DC
PC GC  or  DC GC
DC DC      PC MC
CC CC      CC CC

optional: e.g. (DC, DC, DC, MC, MC, MC, MC)  or  (3DC, 4MC)

Input:

MC, MC, MC, MC, MC, MC, MC

2 possible outputs:

__ MC      MC MC
MC MC  or  MC MC
MC MC      MC __
MC MC      MC MC

optional: It fits!

Input:

ID, CC

2 possible outputs:

ID __      __ ID
__ __  or  __ __
__ __      __ __
CC __      CC __

optional: It fits!

This is , so the shortest code (in bytes) wins.

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

Java 10, 385 384 382 bytes

C->{String[]R=new String[8],F={"CC","DC","PC","GC","MC"};int c=C.size(),i=1,s=0;c=c>8?8:c;for(var q:C)if("DCC".contains(q))s++;for(;s>c- --s;c=(c=C.size())>8?8:c)i=C.remove(F[i])?i:0;for(c=0,i=8;i>0&c<5;c++)for(;i>0&C.remove(F[c]);)R[--i]=F[c];if(C.remove("ID"))R[c=0]="ID";if(C.remove("DL"))R[c<1?1:0]="DL";for(i=0;i<8;)System.out.print((R[i]!=null?R[i]:"__")+(i++%2>0?"\n":" "));}

Although it wasn't too difficult, I can see why it was unanswered. Especially that rule regarding "NDC+NCC ≤ NID+NDL+NPC+NGC+NMC+1" costs quite a lot of bytes at the moment..
And since it's been about 2.5 year since this challenge has been posted, OP might have got another wallet by now anyway.. ;p

-1 byte thanks to @Jakob.

Try it online.

Explanation:

C->{                       // Method with String-List parameter and String return-type
  String[]R=new String[8], //  String-array of size 8
          F={"CC","DC","PC","GC","MC"};
                           //  Frequency-order String-array
  int c=C.size(),          //  Size of the input-List
      i=1,                 //  Index integer, starting at 1
      s=0;                 //  Count-integer, starting at 0
  c=c>8?8:c;               //  If the size is larger than 8, set it to 8
  for(var q:C)             //  Loop over the cards of the input-List
    if("DCC".contains(q))  //   If the card is a DC or CC:
      s++;                 //    Increase the counter by 1
  for(;s>                  //  Loop as long as the amount of DC/CC is larger 
         c- --s;           //  than the other amount of cards + 1
      c=(c=C.size())>8?8:c)//    Recalculate the size after every iteration
    i=C.remove(F[i])?i:0;  //   If the List still contains a DC, remove it
                           //   Else: remove a CC instead
  for(c=0,                 //  Reset `c` to 0
      i=8;i>0              //  Loop as long as there is still room in the wallet,
      &c<5;                //  and we still have cards left
      c++)                 //    Go to the next card-type after every iteration
    for(;i>0               //   Inner loop as long as there is still room in the wallet,
        &C.remove(F[c]);)  //   and we still have a card of the current type left
      R[i--]=F[c];         //    Put a card of the current type in the wallet
  if(C.remove("ID"))R[c=0]="ID";
                           //  Add the 'ID' card to the first row if present
  if(C.remove("DL"))R[c<1?1:0]="DL";
                           //  Add the 'DL' card to the first row if present
  for(i=0;i<8;)            //  Loop over the wallet
    System.out.print(      //   Print:
      (R[i]!=null?         //    If the current slot contains a card:
        R[i]               //     Append this card
       :                   //    Else:
        "__")              //     Append an empty slot ("__")
      +(i++%2>0?"\n":" "));//    Append the correct delimiter (space or new-line)
  return r;}               //  And finally return the result

Java 10, 390.15 (459 bytes - 15% bonus)

C->{String r="",R[]=new String[8],F[]={"CC","DC","PC","GC","MC"},t=r;int c=C.size(),i=1,s=0;for(var q:C)if("DCC".contains(q))s++;for(;s>(c>8?8:c)- --s;c=C.size())if(C.remove(F[i]))t+=F[i]+",";else i=0;for(c=0,i=8;i>0&c<5;c++)for(;i>0&&C.remove(F[c]);)R[--i]=F[c];if(C.remove("ID")){t+=R[0]+",";R[c=0]="ID";};if(C.remove("DL")){t+=R[c=c<1?1:0]+",";R[c]="DL";}for(i=0;i<8;)r+=(R[i]!=null?R[i]:"__")+(i++%2>0?"\n":" ");return r+"\n"+(C.size()>0?t+C:"It fits!");}

Try it online.

\$\endgroup\$
  • 1
    \$\begingroup\$ You can save one byte by initializing F with {"CC","DC","PC","GC","MC"}. \$\endgroup\$ – Jakob Jul 2 '18 at 20:06
  • \$\begingroup\$ @Jakob Ah, didn't realize that was shorter. Thanks! \$\endgroup\$ – Kevin Cruijssen Jul 3 '18 at 6:46

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.