1P5: Nested boxes

53

11

This task is part of the First Periodic Premier Programming Puzzle Push.

You get a hierarchy of items in the following format:

2
Hat
1
Gloves

which need to be put in boxes, like so:

.------------.
| Hat        |
| .--------. |
| | Gloves | |
| '--------' |
'------------'

In the input format the numbers start a box with as many items as the number specifies. The first box has two items in it (the Hat and the box that contains the Gloves), the second one only contains a single item – the gloves.

As can be seen, boxes can live inside boxes, too. And they are always rounded ... sort of (pointy corners are a wound hazard and we wouldn't want that).

Below there are the nasty details for those that want to utilize every tiny bit of leeway the specification gives. Mind you, not reading the spec is no excuse for submitting wrong solutions. There is a test script and a few test cases at the very end.


Specification

  • Boxes are constructed from the following characters:

    • | (U+007C) is used to construct the vertical edges.
    • - (U+002D) is used to construct the horizontal edges.
    • ' (U+0027) are the round lower corners.
    • . (U+002E) are the round upper corners.

    A box therefore looks like this:

    .--.
    |  |
    '--'
    

    Note that while Unicode also has round corners and proper box-drawing characters, this task is in ASCII only. As much as I love Unicode I realize that there are languages and environments out there that didn't quite arrive in the second to last decade.

  • Boxes can contain a sequence of items that are either text or other items. Individual items in a box are rendered from top to bottom. The sequence A, B, C thus renders as follows:

    .---.
    | A |
    | B |
    | C |
    '---'
    

    This of course applies to nested boxes too, which are an item just like text. So the sequence A, B, Box(C, Box(D, E)), F would render as follows:

    .-----------.
    | A         |
    | B         |
    | .-------. |
    | | C     | |
    | | .---. | |
    | | | D | | |
    | | | E | | |
    | | '---' | |
    | '-------' |
    | F         |
    '-----------'
    
  • Boxes adjust their size to the content and nested boxes always extend to the size of their parent. There is always a space before and after the content, so that neither text nor nested boxes are too close to the outer box' edge. In short, the following is wrong:

    .---.
    |Box|
    '---'
    

    And the following is correct:

    .-----.
    | Box |
    '-----'
    

    Looks much nicer, too :-)

  • Text items (see Input below) have to be reproduced exactly.

  • There is always a single top-level box (cf. XML). However, one box can contain several other boxes.

Input

  • Input is given on standard input; for easier testing likely redirected from a file.

  • The input is given line-wise, with each line representing either a text item to put in the current box or opening a new box.

  • Every line is terminated by a line break.

  • Text items are marked by a line that does not consist of a number (see below). Text uses alphabetic characters, the space and punctuation (.,-'"?!()). Text will not start or end with a space and it will always have at least one character.

  • A box starts with a single line with a number in it. The number tells the size of the box, i.e. the number of following items that are put into it:

    2
    A
    B
    

    yields a box with two text items:

    .---.
    | A |
    | B |
    '---'
    

    A box will always contain at least one item.

  • The end of boxes is not explicitly marked with a line; instead boxes are implicitly closed after the specified number of items are put into them.

  • A box is always just a single item, regardless how many items are in it. E.g.

    3
    A
    4
    a
    b
    c
    d
    B
    

    will yield a box with three items, the second of which is another box with four items.

    Nesting also does not affect the fact that a box is just a single item.

Limits

  • The maximum nesting level is five. I.e. there are at most five boxes inside of each other. This includes the outermost one.

  • There is a maximum of ten items per box.

  • Text items have a maximum length of 100 characters.

Output

  • Output is the rendered box including all containing and nested items according to the rules outlined above.
  • Output should be given on standard output and it has to match exactly. No leading or trailing whitespace is allowed.
  • Each line must be terminated with a line break, including the last.

Winning condition

  • Shortest code wins (i.e. gets the accepted answer).

Sample input 1

3
This is some text!
Oh, more text?
Just text for now, as this is a trivial example.

Sample output 1

.--------------------------------------------------.
| This is some text!                               |
| Oh, more text?                                   |
| Just text for now, as this is a trivial example. |
'--------------------------------------------------'

Sample input 2

4
Extreme
nesting
3
of
boxes
4
might
lead
to
2
interesting
1
visuals.
Indeed!

Sample output 2

.--------------------------.
| Extreme                  |
| nesting                  |
| .----------------------. |
| | of                   | |
| | boxes                | |
| | .------------------. | |
| | | might            | | |
| | | lead             | | |
| | | to               | | |
| | | .--------------. | | |
| | | | interesting  | | | |
| | | | .----------. | | | |
| | | | | visuals. | | | | |
| | | | '----------' | | | |
| | | '--------------' | | |
| | '------------------' | |
| '----------------------' |
| Indeed!                  |
'--------------------------'

Sample input 3

1
1
1
1
1
Extreme nesting Part Two

Sample output 3

.------------------------------------------.
| .--------------------------------------. |
| | .----------------------------------. | |
| | | .------------------------------. | | |
| | | | .--------------------------. | | | |
| | | | | Extreme nesting Part Two | | | | |
| | | | '--------------------------' | | | |
| | | '------------------------------' | | |
| | '----------------------------------' | |
| '--------------------------------------' |
'------------------------------------------'

Sample input 4

3
Foo
2
Bar
Baz
2
Gak
1
Another foo?

Sample output 4

.----------------------.
| Foo                  |
| .------------------. |
| | Bar              | |
| | Baz              | |
| '------------------' |
| .------------------. |
| | Gak              | |
| | .--------------. | |
| | | Another foo? | | |
| | '--------------' | |
| '------------------' |
'----------------------'

Test Script

Since getting details right can be difficult at times we (Ventero and me) have prepared a test script you can run your solution with to check whether it's correct. It's available as both a PowerShell script and a bash script. Invocation is: <test-script> <program invocation>.

UPDATE: The test scripts have been updated; there were a number of test cases that did not honor the limits I defined. The PowerShell test script did not use case-sensitive comparison for checking the result. I hope everything is fine now. The number of test cases was reduced to 156, although the last one now is quite ... large.

UPDATE 2: I uploaded my test-case generator. Written in C#, targeting the .NET 2 runtime. It runs on Mono. It may help people to test their implementation. As a definitive worst-case given the limits in the task you can try:

nb.exe 1 10 10 5 100 100 | my invocation

which will generate only boxes up to the innermost level and utilize both the maximum number of items per box and the maximum length of text items. I didn't include this test case into the test script, though, since it's quite large and the output even larger.

UPDATE 3: I updated the PowerShell test script which was prone to throw errors depending on how the line endings were in the script and what line endings the solution printed. Now it should be agnostic to both. Sorry again for the confusion.

Joey

Posted 2011-04-27T20:17:37.383

Reputation: 12 260

You say boxes should adjust their size to their content. Yet in the last example, the first inner box adjusts it's size to the outer box. So, how do nested boxed get their size? – Juan – 2011-04-27T21:58:47.927

@Juan: Thanks for catching that. Amazing that slips like those still happen. Edited :-) – Joey – 2011-04-27T22:00:53.407

1@Joey An oldy but a goody. Hopefully it can inspire some of our newer users to write good, well-specified questions. :-) – Gareth – 2014-03-04T14:25:37.430

@Gareth, I should definitely try to find the time to write more of those again. But a well-specified question, test cases, reference implementation and stuff (things I consider essential for a competition, but that view isn't shared by many ;)) take time. It was much easier while in uni :D – Joey – 2014-03-04T14:37:33.430

Answers

2

GolfScript, 125 characters

n/):B;[(~{[B"."+"""-"]B"| "+:B;@@{(.10,`-{[B\" "]\}{~A}if}*B[2>:B"'"+"""-"]\}:A~;].{~;1$++,}%$-1=:§;{~§3$.+3$+,-*+1$-1%++}%n*

Using a similar approach as Keith's solution.

Howard

Posted 2011-04-27T20:17:37.383

Reputation: 23 109

26

Python, 204 chars

def P(n):x=raw_input();return eval('[(n+".","","-")]'+'+P(n+"| ")'*int(x))+[(n+"'",'','-')]if'0'<x<':'else[(n,x,' ')]
r=P('')
for q,t,f in r:print q+t+f*(max(len(2*x+y)for x,y,a in r)-len(2*q+t))+q[::-1]

P returns a list of triples, each of which is a line prefix/suffix (the suffix being the reverse of the prefix), some line text, and a line fill character. After calculating all the triples, they are printed using the right number of fill characters to make all the lines the same length.

Ungolfed version:

def get_lines(prefix):
  line=raw_input()
  result=[]
  if line.isdigit():
    result.append((prefix+'.', '', '-'))
    for i in xrange(int(line)):
      result += get_lines(prefix + '| ')
    result.append((prefix+"'", '', '-'))
  else:
    result.append((prefix, line, ' '))
  return result
lines=get_lines('')
width=max(2*len(prefix)+len(text) for prefix,text,fill in lines)
for prefix,text,fill in lines:
  print prefix+text+fill*(width-2*len(prefix)-len(text))+prefix[::-1]

Keith Randall

Posted 2011-04-27T20:17:37.383

Reputation: 19 865

Whoa, that was quick. And interesting idea with P there. – Joey – 2011-04-27T21:47:36.720

Wow, indeed. This is interesting, can you post the ungolfed version? I'd like to understand how the eval bit works. Heh, my ungolfed python solution is 1500+ chars :( Though I take a totally different (and inefficient) approach. – Casey – 2011-04-28T00:38:37.030

@Casey: eval is just a golfing shortcut for a loop, it isn't fundamental. I'll post an ungolfed version in a sec... – Keith Randall – 2011-04-28T05:20:10.683

13

Ruby 1.9, 174 characters

r=->l{$*<<(l*2+i=gets.chop).size;/\d/?eval('[l+?.,p=?-,p,'+'*r["| "+l],'*i.to_i+"l+?',p,p]"):[l,i,?\s]}
r[""].each_slice(3){|a,b,c|puts a+b+c*($*.max-(a*2+b).size)+a.reverse}

Somewhat similar to Keith's solution.

Ventero

Posted 2011-04-27T20:17:37.383

Reputation: 9 842

6

APL (78)

{∧/⎕D∊⍨I←⍞:{∆,('-'⍪⍵⍪'-'),∆←'.|'''/⍨1(⊃⍴⍵)1}⍕⍪/{⍵↑[2]⍨⌈/⊃∘⌽∘⍴¨∆}¨∆←∇¨⍳⍎I⋄⍉⍪I}⍬

marinus

Posted 2011-04-27T20:17:37.383

Reputation: 30 224

I can't get this to run on tio.run to test the solution. Otherwise I'd switch the accepted answer as well. – Joey – 2018-10-09T13:57:52.170

5whats this i dont even – Nowayz – 2014-03-04T13:32:10.273

5

Python - 355 314 259 chars

w=0
def p(n,l):
 global w;f=[(l-1,0)]
 for k in' '*n:
  i=raw_input()
  try:f+=p(int(i),l+1)
  except:f+=[(l,i)];w=max(w,4*l+len(i))
 return f+[(l-1,1)]
for l,s in p(input(),1):p=w-4*l-2;print'| '*l+(".'"[s]+'-'*p+".'"[s]if s<2 else s+' '*(p+2-len(s)))+' |'*l

Juan

Posted 2011-04-27T20:17:37.383

Reputation: 2 738

almost a 100 char reduction, good job. – Casey – 2011-04-29T02:51:32.040

5

C, 390 366 363 characters

#define F(n)for(int i=n;i--;)
#define H(n,s,a...)F(n)printf(s);printf(a);
#define I(s)H(v,"| ",s)H(l-2,"-",s)J
#define J H(v," |","\n")
S[1<<17][26],N[1<<17],P,a;E(p){int l=strlen(gets(S[p]));if(sscanf(S[p],"%d",N+p))F(N[p])l<(a=E(++P))?l=a:l;return l+4;}R(p,v,l){if(N[p]){I(".")F(N[p])R(++P,v+1,l-4);I("'")}else{H(v,"| ","%-*s",l,S[p])J}}main(){R(P=0,0,E(0)-4);}

Compile with gcc -std=gnu99 -w file.c

Not even close to Keith's version, but hey, it's good ol' C

esneider

Posted 2011-04-27T20:17:37.383

Reputation: 149

Passes only 159 of the 160 tests here. – Joey – 2011-04-28T09:01:38.123

Ouch. I think now it's OK. I was forgeting to allocate space for the \0 in the extreme case. – esneider – 2011-04-28T09:13:57.290

Looks still the same, Test #142 fails. By the way, the actual extreme case isn't even present as it has 10 MiB input and 78 MiB output. I didn't want the test script to be that large ;-) – Joey – 2011-04-28T09:19:13.967

weird, i'm getting 160/160 passed (I meant a string of 100 characters, which isn't present anyway) – esneider – 2011-04-28T09:33:44.100

Hm, weird. FreeBSD 8.2-RELEASE #5: Sun Feb 27 10:40:25 CET 2011 with gcc version 4.2.1 20070719 [FreeBSD] on x64 here. I'll take your word for the 160, then :-). And there should be a test case with 100 characters, actually (Tests 143–147). – Joey – 2011-04-28T09:47:25.933

Mh, this segfaults for me when trying the worst-case input (check Joey's 2nd update to the question). If you don't have/want Mono, I can either upload an example input for which your program fails or port the input generator to Python or something else. – Ventero – 2011-04-28T17:27:34.033

I'm sorry.. the arrays were too small for extremely large inputs. Now it's fixed (the maximum number of lines is 111111 which, for some reason, I failed to see at first) – esneider – 2011-04-28T22:56:56.640

5

Ruby 1.9, 229 228 226 223 222

g=->n{(1..n).map{g[Integer l=gets.chop]rescue l}}
w=->b{b.bytesize rescue b.map{|e|w[e]}.max+4}
p=->b,c{r=c-2
[?.+?-*r+?.,*b.map{|i|p[i,c-4]}.flatten.map{|k|"| #{k} |"},?'+?-*r+?']rescue[b.ljust(c)]}
puts p[b=g[1][0],w[b]]

Lowjacker

Posted 2011-04-27T20:17:37.383

Reputation: 4 466

4

Haskell, 297 characters

f§(a,b)=(f a,b)
h c=(c,'-',c)
b l=h".":map(\(p,f,q)->("| "++p,f,q++" |"))l++[h"'"]
y[]s z=([(s,' ',"")],z)
y[(n,_)]_ z=b§foldr(\_(l,w)->(l++)§x w)([],z)[1..n]
x(a:z)=y(reads a)a z
m(p,_,q)=length$p++q
n®a@(p,c,q)=p++replicate(n-m a)c++q++"\n"
o(l,_)=l>>=(maximum(map m l)®)
main=interact$o.x.lines

While golf'd, the method is pretty straight forward. Only limits are available memory.

MtnViewMark

Posted 2011-04-27T20:17:37.383

Reputation: 4 779

4

very functional python, 460 characters

r=range
s=lambda x:isinstance(x,str)
w=lambda x:reduce(max,[len(i)if s(i)else w(i)+4 for i in x])
z=lambda b,x:''.join(b for i in r(x))
def g(n=1):
 t=[]
 for i in r(n):
  x=raw_input('')
  try:t+=[g(int(x))]
  except:t+=[x]
 return t
o=list.append
def y(c,m):
 f='| ';h=' |';e=z('-',m+2);a='.'+e+'.';b="'"+e+"'";t=[a]
 for i in c:
  if s(i):o(t,f+i+z(' ',m-len(i))+h)
  else:[o(t,f+j+h)for j in y(i,m-4)]
 return t+[b]
x=g()[0];m=w(x);print '\n'.join(y(x,m))

eordano

Posted 2011-04-27T20:17:37.383

Reputation: 161

Hm, this doesn't seem to work for me the | characters aren't spaced correctly. It is very similar to my python solution – Casey – 2011-04-28T13:21:32.207

2Indeed, does not pass any of the test cases. eordano: We included those so that no one would submit answers that are plain wrong anymore. – Joey – 2011-04-28T16:41:47.553

1I guess I pasted an old version of the code. Should work now. Sorry about being unprofessional. – eordano – 2011-04-29T00:09:34.567

Works for me! Nice solution, I like the functional approach. – Casey – 2011-04-29T02:50:56.143

Indeed, works now. – Joey – 2011-04-29T15:42:05.070

4

C# -1005 859 852 782 characters

using c=System.Console;using System.Linq;class N{static void Main(){new N();}N(){var i=R();c.WriteLine(i.O(0,i.G().W));}I R(){var s=c.ReadLine();int l=0,i=0;if(int.TryParse(s,out l)){var b=new I(l);for(;i<l;){b.m[i++]=R();}return b;}else{return new I(0,s);}}class P{public int W;public int H;}class I{public I[]m;bool z;string t;public I(int l,string r=""){z=l!=0;m=new I[l];t=r;}public P G(){var s=new P();if(z){var d=m.Select(i=>i.G());s.W=d.Max(y=>y.W)+4;s.H=d.Sum(y=>y.H)+2;}else{s.W=t.Length;s.H=1;}return s;}public string O(int l,int w){if(z){string s=A(l,"."+"-".PadRight(w-2,'-')+"."),e=s.Replace(".","'");foreach(var i in m){s+="\n"+i.O(l+1,w-4);}s+="\n"+e;return s;}else{return A(l,t.PadRight(w));}}}static string A(int l,string o){while(l-->0){o= "| "+o+" |";}return o;}}

I need to take another look at this as I'm sure it can be improved, but this is my initial go third pass at it.

Ungolf'd:

using c=System.Console;
using System.Linq;

class NestedBoxes
{
    static void Main()
    {
        new NestedBoxes();
    }
    NestedBoxes()
    {
        var item = ReadItem();
        c.WriteLine(item.Print(0, item.GetSize().Width));
    }
    Item ReadItem()
    {
        var line = c.ReadLine();
        int count = 0, i = 0;
        if (int.TryParse(line, out count))
        {
            var box = new Item(count);
            for (; i < count;)
            {
                box.items[i++] = ReadItem();
            }
            return box;
        }
        else
        {

            return new Item(0,line);
        }
    }
    class Size
    {
        public int Width;
        public int Height;
    }
    class Item
    {
        public Item[] items;
        bool isBox;
        string text;
        public Item(int size,string word="")
        {
            isBox = size != 0; items = new Item[size]; text = word;
        }
        public Size GetSize()
        {
            var s = new Size();
            if (isBox)
            {
                var sizes = items.Select(i => i.GetSize());
                s.Width = sizes.Max(y => y.Width) + 4; s.Height = sizes.Sum(y => y.Height) + 2;
            }
            else
            {
                s.Width = text.Length;
                s.Height = 1;
            }
            return s;
        }
        public string Print(int level, int width)
        {
            if (isBox)
            {
                string output = AddLevels(level, "." + "-".PadRight(width - 2, '-') + "."),
                        bottomLine = output.Replace(".", "'");
                foreach (var item in items)
                {
                    output += "\n" + item.Print(level + 1, width - 4);
                }
                output += "\n" + bottomLine;
                return output;
            } else {return AddLevels(level, text.PadRight(width)); }
        }
    }
    static string AddLevels(int level, string output)
    {
        while(level-->0)
        {
            output = "| " + output + " |";
        }
        return output;
    }
}

Rebecca Chernoff

Posted 2011-04-27T20:17:37.383

Reputation: 397

@Joey, yeah, I definitely need to go through it all again. Need to play with the logic to try and cut it down also. – Rebecca Chernoff – 2011-04-29T15:54:30.073

I'm not familiar with C, but in JS, you can combine multiple var statements to one, like this: var a = 1, b = 2, c = 3;. Can't you do the same thing in C? – nyuszika7h – 2011-04-29T16:06:24.120

2@Nyuszika7H, this is C#, not C. You can't combine implicit var statements like that. You can only combine if they have an explicit type like Joey mentioned using string b="",e="". – Rebecca Chernoff – 2011-04-29T16:08:42.527

@RebeccaChernoff: I worked on the other guys answer, 689 now. – Nick Larsen – 2011-04-29T19:13:40.537

@NickLarsen, nice - but I'm not looking. q: I still need some time to go through mine. This was my initial go at logic, I'm sure there are places I can be smarter about the logic, just need time to give it attention. – Rebecca Chernoff – 2011-04-29T19:19:55.727

You still have an unnecessary space at the very end of your code in o= "| "+o+" |";. – Joey – 2011-05-06T10:18:09.077

4

PHP, 403 388 306 chars

<?b((int)fgets(STDIN),'');foreach($t as $r)echo$r[0].str_pad($r[2],$w-2*strlen($r[0]),$r[1]).strrev($r[0])."\n";function b($c,$p){global$t,$w;$t[]=array($p.".","-");while($c--){if(($d=trim(fgets(STDIN)))>0)b($d,"| ".$p);else$t[]=array("| ".$p," ",$d);$w=max($w,strlen($d.$p.$p)+4);}$t[]=array($p."'","-");}

Ungolfed:

box((int)fgets(STDIN), '');

foreach($table as $row) {
    $prefix = $row[0];
    $pad = $row[1];
    $data = $row[2];
    echo $prefix . str_pad($data, ($width - 2*strlen($prefix)), $pad) . strrev($prefix)."\n";
}

function box($count,$prefix) {
    global $table, $width;
    $table[] = array($prefix.".","-");
    while($count--) {
        if(($data = trim(fgets(STDIN))) > 0) {
            box($data, "| ".$prefix);
        } else {
            $table[] = array("| ".$prefix, " ", $data);
        }
        $width = max($width,strlen($data.$prefix.$prefix)+4);
    }
    $table[] = array($prefix."'","-");
}
?>

I borrowed the prefix-idea from Keith (is that allowed at all?), otherwise this is pretty much as the original. Still couldn't get below 300. Stuck with this. Onwards.

Samuli K

Posted 2011-04-27T20:17:37.383

Reputation: 51

2

Well, code is public here in every case, so borrowing ideas is allowed and perhaps even encouraged. I think that is also something that differentiates this site from other, similar ones. As gnibbler one noted we compete and collaborate at the same time.

– Joey – 2011-05-06T10:10:11.983

3

PHP, 806 769 721 653 619 chars

<?php function A($a,$b,$c,&$d){for($e=$b;$e>0;$e--){$f=fgets($a);if(false===$f){return;}$g=intval($f);if(0<$g){$h[]=A($a,$g,$c+1,$d);}else{$f=trim($f);$h[]=$f;$j=strlen($f)+4*$c;if($d<$j){$d=$j;}}}return $h;}$d=0;$h=A(STDIN,intval(fgets(STDIN)),1,&$d);function B($k,$c,$d){$f=str_pad('',$d-4*$c-2,'-',2);return C($k.$f.$k,$c,$d);}function C($f,$c,$d){$f=str_pad($f,$d-4*$c,' ');$f=str_pad($f,$d-2*$c,'| ',0);$f=str_pad($f,$d,' |');return $f;}function D($l,$c,$d){if(!is_array($l)){echo C($l,$c,$d)."\n";return;}echo B('.',$c,$d)."\n";foreach($l as $m){echo D($m,$c+1,$d);}echo B('\'',$c,$d)."\n";}D($h,0,$d);exit(0);?>

Ungolfed version:

<?php
function read_itemgroup($handle, $item_count, $depth, &$width) {

    //$items = array();

    for($i = $item_count; $i > 0; $i--) {
        $line = fgets( $handle );
        if(false === $line) {
            return;
        }

        $line_int = intval($line);
        if(0 < $line_int) {
            // nested group
            $items[] = read_itemgroup($handle, $line_int, $depth + 1, $width);
        }
        else {
            // standalone item
            $line = trim($line);
            $items[] = $line;

            // determine width of item at current depth
            $width_at_depth = strlen($line) + 4 * $depth;
            if($width < $width_at_depth) {
                $width = $width_at_depth;
            }
        }
    }

    return $items;
}
$width = 0;
$items = read_itemgroup(STDIN, intval(fgets( STDIN )), 1, &$width);

//var_dump($items, $width);

function render_line($corner, $depth, $width) {
    $line = str_pad('', $width - 4 * $depth - 2, '-', 2); // 2 = STR_PAD_BOTH
    return render_item($corner . $line . $corner, $depth, $width);
}

function render_item($line, $depth, $width) {
    $line = str_pad($line, $width - 4 * $depth, ' ');
    $line = str_pad($line, $width - 2 * $depth, '| ', 0); // 0 = STR_PAD_LEFT
    $line = str_pad($line, $width, ' |');
    return $line;
}

function render($item, $depth, $width) {
    if(!is_array($item)) {
        echo render_item($item, $depth, $width) . "\n";
        return;
    }
    echo render_line('.', $depth, $width) . "\n";
    foreach($item as $nested_item) {
        echo render($nested_item, $depth + 1, $width);
    }
    echo render_line('\'', $depth, $width) . "\n";
}

render($items, 0, $width);

exit(0);
?>

MicE

Posted 2011-04-27T20:17:37.383

Reputation: 131

Why're you using two-letter function names instead of one-letter ones? – Lowjacker – 2011-04-28T20:37:57.413

@Lowkacler: good catch, that's one thing that I still need to optimize. I didn't have any minifier at hand, so I did that manually. I also have several ideas on what to enhance (codewise, not minification), so I'll post a revised version later on. – MicE – 2011-04-28T20:49:01.690

1First of all, this is missing a <? in the beginning to even run. Then you're apparently using the maximum length of all text items in a test case as the width for the innermost box. This code only passes 118 of the test cases (tested on Linux and FreeBSD). I have no idea what you did to the PowerShell script that it wouldn't run, though :-(. Invoking it as powershell -noprofile -file test.ps1 php boxes.php should work, actually. But I have no PHP on my Windows machine to test. – Joey – 2011-04-28T22:03:59.290

Tested this on my box using the latest bash script, got 118/156. I put the output on a gist

– Juan – 2011-04-29T04:25:23.077

@Juan: thank you very much for that, kind sir! :-) – MicE – 2011-04-29T09:11:52.950

@Joey: thank you for pointing that out, I fixed the length detection and included the <? tags respectively. Your invocation example helped me to capture the output to a file, thank you for providing it. It seems that the tests fail due to different linebreaks. The test expects \r\n, but I cannot achieve that on Windows (CLI seems to override it). I uploaded output from the recent script to gist: https://gist.github.com/948083

– MicE – 2011-04-29T09:12:18.270

MicE: I updated the PowerShell test script; it expected whatever line endings it was saved with, which was the problem you (and others) encountered. It should be fixed now and work with whatever line endings you throw at it. – Joey – 2011-04-29T09:14:38.110

@Joey: thank you, that did the trick! 156/156 passed :-) – MicE – 2011-04-29T09:18:34.097

1Nice to hear :). That's what I get for writing a test script that was initially intended for single-line output ;-) – Joey – 2011-04-29T10:07:02.727

3

Java - 681 668 chars

import java.util.*;public class b{static int m,h,i;public static void main(String[]a)throws Throwable{for(Object o:z("")){a=(String[])o;String s=a[0]+a[1];i=a[0].length();for(h=0;h<m-i*2-a[1].length();h++){s+=a[2];}for(h=i;h>0;h--){s+=a[0].charAt(h-1);}System.out.println(s);}}static List z(String p)throws Throwable{String b="",d="";List l=new ArrayList();while((i=System.in.read())>-1){if(10==i){if(d!=""){String[]v={p+".",b,"-"},t={p+"'",b,"-"};l.add(v);for(int u=0;u<Integer.parseInt(d);u++){l.addAll(z(p+"| "));}l.add(t);}else{h=b.length()+p.length()*2;if(m<h)m=h;String[]v={p,b," "};l.add(v);}break;}else if(i>47&&i<58){d+=(char)i;}else {b+=(char)i;}}return l;}}

essentially the same method as Keith Randall's Python code

Ungolfed version:

import java.util.*;

public class b {
    static int m, h, i;

    public static void main(String[] a) throws Throwable {
        for (Object o : z("")) {
            a = (String[]) o;
            String s = a[0] + a[1];
            i = a[0].length();
            for (h = 0; h < m - i * 2 - a[1].length(); h++) {
                s += a[2];
            }
            for (h = i; h > 0; h--) {
                s += a[0].charAt(h - 1);
            }
            System.out.println(s);
        }
    }

    static List z(String p) throws Throwable {
        String b = "", d = "";
        List l = new ArrayList();
        while ((i = System.in.read()) > -1) {
            if (10 == i) {
                if (d != "") {
                    String[] v = { p + ".", b, "-" }, t = { p + "'", b, "-" };
                    l.add(v);
                    for (int u = 0; u < Integer.parseInt(d); u++) {
                        l.addAll(z(p + "| "));
                    }
                    l.add(t);
                } else {
                    h = b.length() + p.length() * 2;
                    if (m < h)
                        m = h;
                    String[] v = { p, b, " " };
                    l.add(v);
                }
                break;
            } else if (i > 47 && i < 58) {
                d += (char) i;
            } else {
                b += (char) i;
            }
        }
        return l;
    }
}

Greg Schueler

Posted 2011-04-27T20:17:37.383

Reputation: 151

I think you can get rid of one space each time there is a throws. – Joey – 2011-04-30T06:16:30.267

yes! also elminated a few more chars. (can assume each line is terminated with newline char, redundant break;) – Greg Schueler – 2011-04-30T18:31:10.183

probably could finesse the char comparisons by looking at ascii codes for longer...but i have to go get ready for vacation – Greg Schueler – 2011-04-30T18:33:43.037

3

Perl - 200 199 chars

Same algorithm as Keith Randall's Python (nice design, Keith), but a tiny bit more compact in this Perl take on it.

sub P{$_=<>;chop;$m>($l=length"$_@_@_")or$m=$l;/^\d/?(["@_.","","-"],(map{P("| @_")}1..$_),["@_'","","-"]):["@_",$_," "]}map{($q,$t,$f)=@$_;print"$q$t",($f x($m-length"$q$t$q")).reverse($q),"\n"}(P);

DCharness

Posted 2011-04-27T20:17:37.383

Reputation: 541

1$_@_@_ looks like someone chasing the dollar sign – ajax333221 – 2012-03-27T00:14:38.947

3

F# - 341 characters

let rec f(x,y)=[
 let l=stdin.ReadLine()
 let q,d=Core.int.TryParse l
 if q then
  yield x+".","",'-',"."+y
  for i=1 to d do yield!f(x+"| ",y+" |")
  yield x+"'","",'-',"'"+y
 else yield x,l,' ',y]
let l=f("","")
for w,x,y,z in l do printfn"%s"(w+x.PadRight(List.max(l|>List.map(fun(w,x,y,z)->2*w.Length+x.Length))-2*w.Length,y)+z)

An F# version of Keith's solution. Lists are immutable by default, so this version stuffs the entire recursive function into a list, returns the list, from which the items are extracted using the for..do loop and a yield!. I couldn't find a way to reverse the prefix concisely, so I just attached the suffix onto the triples.

FYI, the TryParse method returns a double (bool,int).

nharren

Posted 2011-04-27T20:17:37.383

Reputation: 383

2

Clojure - 480 chars

(use '[clojure.contrib.string :only (repeat)])(let [r ((fn p[%](repeatedly % #(let [x (read-line)](try(doall(p(Integer/parseInt x)))(catch Exception e x))))) 1)]((fn z[m,n,o] (let[b #( let[p(dec o)](println(str(repeat p "| ")%(repeat(- m(* 4 p)2)"-")%(repeat p " |"))))](b \.)(doseq[i n](if(seq? i)(z m i(inc o))(println(str(repeat o "| ")i(repeat(- m(count i)(* o 4))" ")(repeat o " |")))))(b \')))((fn w[x](reduce max(map(fn[%](if(seq? %)(+ (w %)4)(count %)))x)))r)(first r) 1))

This is my first Clojure program as well as my first Clojure golf attempt, so, needless to say, this shouldn't be taken as representative of Clojure solutions in general. I'm sure it could be shortened significantly, especially if Keith Randall's method of parsing and building the boxes at once was implemented.

Casey

Posted 2011-04-27T20:17:37.383

Reputation: 3 129

Man, half of this source must be whitespace. And mandatorily so :-). Interesting, though and I wonder whether one will see a Lisp variant win a code golf ;-) – Joey – 2011-04-30T06:17:57.177

I'm sure its possible... though like I said, I'm probably not going to be the one to do it. – Casey – 2011-04-30T18:44:10.680

2

Scala - 475 characters

object N2 extends App{type S=String;def b(x:List[S],t:Int,s:S,e:S):List[S]={var l=x;o=o:+(s+".-±-."+e+"-");for(i<-1 to t)if(l.head.matches("\\d+"))l=b(l.tail,l.head.toInt,s+"| ",e+" |")else{o=o:+(s+"| "+l.head+"±"+e+" | ");l=l.drop(1)};o=o:+(s+"'-±-'"+e+"-");return l};var o:List[S]=List();val l=io.Source.stdin.getLines.toList;b(l.tail,l.head.toInt,"","");(o map(x=>x.replaceAll("±",x.last.toString*((o sortBy((_:S).length)).last.length-x.length)).dropRight(1)))map println}

Gareth

Posted 2011-04-27T20:17:37.383

Reputation: 11 678

2

C# - 472 470 426 422 398 characters

using System.Linq;using y=System.Console;class W{static void Main(){var c=new int[5];var s=new string[0].ToList();int n=0,i;var l="";do{try{c[n]=int.Parse(l=y.ReadLine());l=".{1}.";n++;i=1;}catch{l+="{0}";i=0;}G:while(i++<n)l="| "+l+" |";s.Add(l);if(n>0&&--c[n-1]<0){n--;l="'{1}'";i=0;goto G;}}while(n>0);s.ForEach(z=>y.WriteLine(z,l="".PadLeft(s.Max(v=>v.Length)-z.Length),l.Replace(' ','-')));}}

nharren

Posted 2011-04-27T20:17:37.383

Reputation: 383

Nice. A goto! By the way, you can omit the parentheses around the lambda arguments z and v, bringing this down to 421. – Joey – 2011-05-23T19:17:47.417

1

C# 1198 1156 1142 689 671 634 Characters

using z=System.Console;using System.Collections.Generic;using System.Linq;
class T{bool U;List<T> a=new List<T>();string m;IEnumerable<string>R(int s){if(U){yield return ".".PadRight(s-1,'-')+".";foreach(var e in a.SelectMany(b=>b.R(s-4)))yield return ("| "+e).PadRight(s-e.Length)+" |";yield return "'".PadRight(s-1,'-')+"'";}else yield return m;}int L(){return U?a.Max(x=>x.L())+4:m.Length;}
static void Main(){var p=O(int.Parse(z.ReadLine()));z.WriteLine(string.Join("\r\n",p.R(p.L())));}
static T O(int n){var k=new T(){U=true};while(n-->0){var l=z.ReadLine();int c;k.a.Add(int.TryParse(l,out c)?O(c):new T{m=l});}return k;}}

Fatal

Posted 2011-04-27T20:17:37.383

Reputation: 119

1

Ungolfed version is up on github - https://github.com/paulduran/CodeGolf

– Fatal – 2011-04-29T06:30:24.947

Joining with \n appears to suffice in the end. – Joey – 2011-04-29T08:23:37.197

Getting rid of the interface freed up a lot of characters, the rest was mostly standard golfing. There is plenty more that can be done here, I would expect this could get below 600. – Nick Larsen – 2011-04-29T19:12:30.430

Nice work Nick. I suspected the interface was a bit of overkill, to be honest. a simple flag would have sufficed in this situation as u have shown. – Fatal – 2011-04-30T05:33:44.963

0

Java (1369 chars incl. EOLs)

Couldn't leave this without a Java implementation. Java is supposed to be more verbose that the slicks of Python and Ruby, so I went for an elegant, recursive solution.

The idea is a Tree (Graph) of objects (strings and boxes), containing one another starting from a "head" box. As you linearly parse the input file you add strings and boxes to the "current" box and while you are adding the maximum length of the container is adjusted. When a container reaches the amount of predefined items that it can hold you backtrack to the previous container. At the end of the input file, you have a "head" container that already has a "maxLength" calculated, so you simply call its print() method.

import java.io.*;import java.util.*;
public class N{private static String rPad(String s,int l){return s+str(l-s.length(),' ');}
private static String str(int l, char c){StringBuffer sb=new StringBuffer();while(l-->0){sb.append(c);}return sb.toString();}
private static class Box {Box prnt=null;String txt=null;int items;List<Box> c=new ArrayList<Box>();int maxLength=0;
public Box(Box p,int n){prnt=p;items=n;if(p!=null){p.c.add(this);}}
public Box(Box p,String s){prnt=p;txt=s;if(p!=null){p.c.add(this);p.notify(s.length());}}
public void print(String prefix,int l,String suffix){if (txt == null){System.out.println(prefix+"."+str(l-2,'-')+"."+suffix);for(Box b:c){b.print(prefix+"| ",l-4," |"+suffix);}System.out.println(prefix+"'"+str(l-2,'-')+"'"+suffix);}else{System.out.println(prefix+rPad(txt,l)+suffix);}}
protected void notify(int l){if (l+4>this.maxLength){this.maxLength=l + 4;if (this.prnt != null){this.prnt.notify(this.maxLength);}}}}
public static void main(String[] args)throws IOException{Box head=null;Box b=null;BufferedReader in=new BufferedReader(new InputStreamReader(System.in));String s;while ((s=in.readLine()) != null){try{int n=Integer.parseInt(s);b=new Box(b, n);}catch (NumberFormatException nfe){b=new Box(b, s);}if(head == null)head=b;while ((b != null) && (b.items == b.c.size())){b=b.prnt;}}head.print("",head.maxLength,"");}}

It is really an enjoyable solution to write. I liked the question a lot. As I mentioned earlier, I went for solution elegance not minimalist approach, sadly Java doesn't have Python's print "-"*4 to produce "----" :-)

Here's the ungolfed version:

import java.io.*;
import java.util.*;

public class NestedBoxes
{

    private static String rPad ( String s, int l )
    {
        return s + str(l - s.length(), ' ');
    }

    private static String str ( int l, char c )
    {
        StringBuffer sb = new StringBuffer();
        while (l-- > 0)
        {
            sb.append(c);
        }
        return sb.toString();
    }

    private static class Box
    {

        Box parent = null;
        String text = null;
        int items;
        List<Box> contents = new ArrayList<Box>();

        int maxLength = 0;

        public Box ( Box p, int n )
        {
            parent = p;
            items = n;
            if (p != null)
            {
                p.contents.add(this);
            }
        }

        public Box ( Box p, String s )
        {
            parent = p;
            text = s;
            if (p != null)
            {
                p.contents.add(this);
                p.notify(s.length());
            }
        }

        public void print ( String prefix, int l, String suffix )
        {
            if (text == null)
            {
                System.out.println(prefix + "." + str(l - 2, '-') + "." + suffix);
                for (Box b : contents)
                {
                    b.print(prefix + "| ", l - 4, " |" + suffix);
                }
                System.out.println(prefix + "'" + str(l - 2, '-') + "'" + suffix);
            }
            else
            {
                System.out.println(prefix + rPad(text, l) + suffix);
            }
        }

        protected void notify ( int l )
        {
            if (l + 4 > this.maxLength)
            {
                this.maxLength = l + 4;
                if (this.parent != null)
                {
                    this.parent.notify(this.maxLength);
                }
            }
        }
    }

    public static void main ( String[] args ) throws IOException
    {
        Box head = null;
        Box b = null;
        BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
        String s;
        while ((s = in.readLine()) != null)
        {
            try
            {
                int n = Integer.parseInt(s);
                b = new Box(b, n);
            }
            catch (NumberFormatException nfe)
            {
                b = new Box(b, s);
            }

            if (head == null)
            {
                head = b;
            }

            while ((b != null) && (b.items == b.contents.size()))
            {
                b = b.parent;
            }
        }
        head.print("", head.maxLength, "");
    }
}

ksymeon

Posted 2011-04-27T20:17:37.383

Reputation: 125

4You know, this is a code golf. You should at least try aiming for a small solution. Elegance is all nice and well, but not actually required nor wanted here. – Joey – 2011-04-29T20:57:29.387

0

Pip, 89 bytes (non-competing)

(The language is newer than the challenge. Also, I couldn't quite outgolf APL.)

Code is 87 bytes, +2 for -rn flags.

(z:{I+YPOi{Y{Vz}M,ym:MX#*Y$ALyY'|.s._.sX++m-#_.'|MyY".."J'-X++mALyAL"''"J'-Xm}yALl}i:g)

Try it online!

The function z processes the first item of the input list (g, copied into global variable i so as to be available inside function calls). If this is a number n, it calls itself recursively n times, pads the resulting list of lines to a full rectangle, wraps each line in "| " " |", and adds .---. and '---' lines before returning the new list. If it is a string, it simply converts it to a one-item list and returns it. The final result is printed newline-separated (-n flag). More detail available on request.

DLosc

Posted 2011-04-27T20:17:37.383

Reputation: 21 213

I usually don't have a problem with languages newer than the challenge, especially considering that the problem isn't so trivial that a newly-created language would have operations specifically for solving it :-) – Joey – 2016-09-21T08:39:35.440

This fails the fourth sample. – Joey – 2017-08-22T15:41:33.690