Stack the Christmas Gifts

21

1

Someone has been hastily stacking the Christmas presents, and it's quite a mess:

           ========================
           |                      |
           ========================
     =============
     |           |
     |           |
     |           |
     |           |
     |           |
     |           |
     =============
        =======
        |     |
        |     |
        |     |
        =======
  ===================
  |                 |
  |                 |
  |                 |
  ===================
=================
|               |
|               |
|               |
|               |
=================
   =======
   |     |
   |     |
   =======

Like, seriously, how does that top present even balance. It's probably a hammer. To prevent this tower of presents from crumbling, you're to reorder the presents so they stack nicely:

        =======
        |     |
        |     |
        =======
        =======
        |     |
        |     |
        |     |
        =======
     =============
     |           |
     |           |
     |           |
     |           |
     |           |
     |           |
     =============
   =================
   |               |
   |               |
   |               |
   |               |
   =================
  ===================
  |                 |
  |                 |
  |                 |
  ===================
========================
|                      |
========================

The Rules

  • Each present consists of a top and bottom of = characters, and one or more middle rows, consisting of two | separated by spaces. The width of the present is the same in all its rows.
  • There are no empty lines.
  • Consecutive presents will overlap in at least one column.
  • Presents are to be stacked in order of decreasing width. In the event of a tie, the taller present should go below the flatter present.
  • Presents should be centred on the present beneath. If the present cannot be placed exactly in the centre (because the difference in widths is odd), you may choose either position that is half a character off the centre.
  • You may or may not assume that the input has a single trailing newline, but please state your assumption.
  • Your solution does not have to work for an empty input, but must be able to handle a single present.
  • You may write a program or function, which takes input via STDIN or function argument and returns the result or prints it to STDOUT.
  • This is code golf, so the shortest answer (in bytes) wins.

Martin Ender

Posted 2014-12-24T20:56:03.443

Reputation: 184 808

Answers

15

CJam, 81 70 bytes

'"qN/{__Sm0=#>}%N*"=
="/"=\"\"="*'"++~]$_W='=/,f{1$'=/,m4/\N/\f{S*\N}}

So we have to stack the Christmas presents? This code does it like an actual person would do*.

First, we stack all the presents against a wall to easily move them up and down using this code:

'"qN/{__Sm0=#>}%N*

then, we identify each present as a separate item using this code:

"=
="/"=\"\"="*'"++~]

then, we sort the presents based on their heights and widths using this code:

$

Till now, all the gifts have been stacked against a wall in order to have perfect alignment with each other. But as this is Christmas, we want to place the gifts aligned centered like a Christmas tree! This code does that:

_W=Af{1$Am4/\N/\f{S*\N}}

Here is a step by step output of the code for example in the question:

"Step 1 - Stack the presents against a wall";
========================
|                      |
========================
=============
|           |
|           |
|           |
|           |
|           |
|           |
=============
=======
|     |
|     |
|     |
=======
===================
|                 |
|                 |
|                 |
===================
=================
|               |
|               |
|               |
|               |
=================
=======
|     |
|     |
=======

"Step 2 - Identify the presents as a collection of presents";
["========================
|                      |
========================" "=============
|           |
|           |
|           |
|           |
|           |
|           |
=============" "=======
|     |
|     |
|     |
=======" "===================
|                 |
|                 |
|                 |
===================" "=================
|               |
|               |
|               |
|               |
=================" "=======
|     |
|     |
======="]

"Step 3 - Sort on height & width, with presents stacked against a wall to help sort them";
=======
|     |
|     |
=======
=======
|     |
|     |
|     |
=======
=============
|           |
|           |
|           |
|           |
|           |
|           |
=============
=================
|               |
|               |
|               |
|               |
=================
===================
|                 |
|                 |
|                 |
===================
========================
|                      |
========================

"Final step - stack them like a Christmas Tree";
        =======
        |     |
        |     |
        =======
        =======
        |     |
        |     |
        |     |
        =======
     =============
     |           |
     |           |
     |           |
     |           |
     |           |
     |           |
     =============
   =================
   |               |
   |               |
   |               |
   |               |
   =================
  ===================
  |                 |
  |                 |
  |                 |
  ===================
========================
|                      |
========================

Try it online here

* Might differ from person to person though :P

Optimizer

Posted 2014-12-24T20:56:03.443

Reputation: 25 836

That's awesome that the standard lexicographic order happens to fulfill the sorting requirements! Nice catch. – wchargin – 2014-12-25T00:59:08.150

@WChargin yeah. Saved me a ton of bytes! – Optimizer – 2014-12-25T01:02:55.573

3

Japt, 18 bytes

mx óÈíY b'=²Ãn c û

Try it online!

I use a sufficiently different strategy from the other Japt answer that I thought it was worth its own answer. Takes input and output as an array of lines

Explanation:

mx                    #Trim leading whitespace from each line
   ó        Ã         #Split the array between lines where:
    ÈíY               # The lines interleaved (e.g. "abc","def" => "adbecf")
        b'=²          # starts with "=="
             n        #Default sorting for "array of arrays of strings"
               c      #Flatten to a single array of lines
                 û    #Pad each line so they are centered

I don't know precisely why "default sort" works like that, but I've tested that the taller box of the two with the same width is on the bottom regardless of which comes first in the input.

Kamil Drakari

Posted 2014-12-24T20:56:03.443

Reputation: 3 461

1Imagine that the shorter string gets right-padded with an imaginary character with code point -1 to the length of the longer one. – Erik the Outgolfer – 2018-12-18T17:12:50.007

1Replace "==" with '=² to save a byte. – Shaggy – 2018-12-18T17:22:43.087

2

Ruby, 164

Neat challenge! Couldn't get it down much further.

f=->x{y=x.scan(/\s+=+[\s|]+\s+=+/).sort_by{|p|-p.count(?|)}.sort_by{|p|p.count ?=}
y.map{|p|p.gsub(/^\s+/,'').each_line{|l|puts l.strip.center(y[-1].count(?=)/2)}}}

Explanation

The input String is chopped up into an Array where each present is an element. Then the array is sorted by the number of pipe characters and sorted again by the number of equal signs.

It then removes all leading whitespace and prints each line individually, centered by the width of the largest present.

It behaves the same with or without a trailing newline on the input.

Readable version

f = lambda do |x|
  y = x.scan(/\s+=+[\s|]+\s+=+/)
       .sort_by { |p| -p.count("|") }
       .sort_by { |p|  p.count("=") }

  y.map do |p|
    p.gsub(/^\s+/,'').each_line do |l|
      puts l.strip.center(y.last.count("=") / 2 )
    end
  end
end

britishtea

Posted 2014-12-24T20:56:03.443

Reputation: 1 189

1

05AB1E, 23 20 bytes

|ðδÛ»…=
=…=0=:0¡{».c

-3 bytes thanks to @ErikTheOutgolfer.

Try it online.

Explanation:

|         # Take the input split by newlines
 ðδÛ      # Remove leading spaces from each line
    »     # And join everything back together again with a newline delimiter
…=
=         # Push string "=\n="
 …=0=     # Push string "=0="
     :    # Replace all "=\n=" with "=0="
0¡        # Now split on "0"
          # (We now have our list of presents without any leading spaces)
  {       # Sort this list (with default string-wise sorting)
   »      # Join the list of presents by newlines
    .c    # Left-focused centralize the string (and output implicitly)

Notes:

  • Odd-width presents are left-focused centralized. This can be changed to right-focused by changing the trailing lowercase c to an uppercase C.
  • The leading | can be dropped if we are allowed to take the input as a list of string-lines.
  • Assumes the input contains no trailing spaces for any of the presents (similar as the input in the challenge description); trailing newlines are fine, since the | removes those anyway.

Kevin Cruijssen

Posted 2014-12-24T20:56:03.443

Reputation: 67 575

120 bytes. ðδÛ can be used instead of εðÛ} here, ¶'=.ø is the same as …=\n= (\n means newline), 0'=.ø is the same as …=0=. – Erik the Outgolfer – 2018-12-18T16:57:39.543

@EriktheOutgolfer Ah, I'm an idiot for using instead of the literal 3-char strings.. And thanks for ðδÛ. Actually never used δ before and had no idea it worked like that. – Kevin Cruijssen – 2018-12-18T17:18:55.247

1

Attache, 91 bytes

Join&lf@{Center&#(_@-1@0)@>_}@{SortBy[&{#_'#__},Strip@>Lines=>Split[_,/"(?<==)\\s+(?==)"]]}

Try it online!

Ungolfed

?? returns [length of first entry, number of entries]
revDim := &{#_'#__}

?? regex
SPLIT_ON_BARRIERS := /"(?<==)\\s+(?==)"

splitPresents[str] := (
    chopped .= Split[str, SPLIT_ON_BARRIERS];;
    normalized .= Strip @> Lines => chopped
)

orderPresents[presents] :=
    SortBy[revDim, presents]

fixPresents[ordered] := (
    ?? number of columns of bottom-most present
    pad_size .= Size[Last[ordered][0]];;
    ?? center each line of each present
    Center&pad_size @> _
)

joinNewlines := Join&lf

stackPresents := joinNewlines@fixPresents@orderPresents@splitPresents

Conor O'Brien

Posted 2014-12-24T20:56:03.443

Reputation: 36 228

0

Japt, 23 20 19 bytes

Similar approach to Kevin's solution. First byte can be removed if we can take input as an array of lines.

·mx ·r¥¬·È·Ãq, n ·û

Try it

·mx ·r¥¬·È·Ãq, n ·û     :Implicit input of string
·                       :Split on newlines
 m                      :Map
  x                     :  Trim
    ·                   :Join with newlines
     r                  :Global replace
      ¥                 :  Shortcut for the == operator. Passing an operator as the first argument of a method in Japt implicitly converts it to a string
       ¬                :  Split
        ·               :  Join with newlines, giving the string "=\n=" to be replaced
         È              :  Pass each match through a function
          ·             :    Split on newlines. As we're working within a string, the resulting array gets cast to a string (i.e., "=\n=" -> ["=","="] -> "=,="
           Ã            :End replace
            q,          :Split on ","
               n        :Sort
                 ·      :Join with newlines
                  û     :Centre pad each line with spaces to the length of the longest

Shaggy

Posted 2014-12-24T20:56:03.443

Reputation: 24 623

0

Python 2, 221 196 bytes

s,a,b,i=[c.strip()for c in input().split("\n")]+["="],[],[],0
exec"a+=[s[i].center(max(map(len,s)))]\nif s[i][0]==s[i+1][0]=='=':b+=[a];a=[]\ni+=1;"*(len(s)-1)
for c in sorted(b):print"\n".join(c)

Try it online!

Expects a quoted string without trailing newlines as input.

Not great, but it's the best I can do.

Triggernometry

Posted 2014-12-24T20:56:03.443

Reputation: 765

0

Perl 5 -n0, 123 bytes

sub k{pop=~y/=//}say s+^\s*+$"x((k($p[-1])- k$_)/4)+rmge for@p=sort{k($a)- k$b||$a=~y/|//-$b=~y/|//}/\s*(=+[| 
]+\s*\=+)/gs

Try it online!

Xcali

Posted 2014-12-24T20:56:03.443

Reputation: 7 671

0

Javascript 279 bytes 275 bytes

I am something of a novice to code-golf, and not anything like an expert in javascript but the challenge is interesting and fun. I would like to see what tricks a real expert in js would use.

Assumptions

  • Input and output are arrays of strings
  • No blank lines anywhere
  • Height of a box is <= 99 lines (does this disqualify me)?
  • Input and output variables are predefined, output being initially an empty array

Code

Input is in g[]. Output in m[].

a=[];s='';b=0;c=0;o=[];g.forEach((t,x)=>{t=t.trim(),c=Math.max(c,t.length);o.push(t);if(s==''){s=t;b=x}else{if(t==s){a.push({"K":s.length*100+x-b,"O":o});s='';o=[]}}});a.sort((p,q)=>{return p.K-q.K});a.forEach((t)=>{t.O.forEach((q)=>{m.push(" ".repeat((c-q.length)/2)+q)})});

The code works by

  1. building an array of objects, each object representing one box, with two members: K, a sort key being the (width x 100 + height) and O, an array of the (trimmed) strings making up the box. While building the array the code remembers the width of the widest box.

  2. The array of box objects is sorted into order by the key K. Where boxes have the same width the key ensures they are sorted by height.

  3. After sorting the boxes, the strings for each box are pushed into the output array with leading spaces added, which position the box centrally over the widest one.

Try it online!

JohnRC

Posted 2014-12-24T20:56:03.443

Reputation: 141