18
\$\begingroup\$

In this challenge, we render Ascii user interfaces.

+----------------------+
|+-----------++-------+|
||<- Previous||Next ->||
|+-----------++-------+|
|== The title ==       |
|                      |
|Lorem ipsum dolor     |
|sit amet...           |
|+--------------+      |
||Post a comment|      |
|+--------------+      |
|+-----------------+   |
||User X commented:|   |
||                 |   |
||This is amazing! |   |
|+-----------------+   |
|+-----------------+   |
||User Y commented:|   |
||                 |   |
||lol              |   |
|+-----------------+   |
+----------------------+

Each drawing like this is made of one element, which can contain subelements. The possible elements are listed below:

  1. Text element. Contains one or more lines of text.
  2. Box element. Contains one subelement that is surrounded with borders. The borders have +s at the corners and -s and | at the edges.
  3. Horizontal list. Contains one or more elements that are aligned horizontally.
  4. Vertical list. Contains one or more elements that are aligned over each other vertically and to left horizontally.

Every element is a rectangle.

Each element, in addition to its content, has a property called baseline. The baseline is used to align the elements vertically: every element of a horizontal list is aligned such that their baselines are on the same line. In the example below, the baseline contain characters aeg. The baselines of the three box elements are (0-indexed) 1, 3 and 2.

   +-+   
   |c|+-+
+-+|d||f|
|a||e||g|
|b|+-+|h|
+-+   +-+

The baselines are determined with the following rules:

  1. For text elements, the first line of text is the baseline, ie. 0.
  2. For box elements, the baseline is 1 + the baseline of the subelement.
  3. For horizontal lists, the baseline is the maximum baseline in the list (3 in the example above).
  4. For vertical lists, the baseline is the baseline of an element, which must be specified in the input.

Input

The input is a specification of an interface in some format (eg. lists, json). The example inputs have the following format:

  1. A string element is a string: "..."
  2. A box element is a list thats first element is "b": ["b", subelement]
  3. A horizontal list is a list thats first element is "h": ["h", items...]
  4. A vertical list is a list thats first element is "v" and the second element is the (0-indexed) number of the element thats baseline is used: ["v", n, items...]

Output

The output must contain the elements aligned using the rules I specified above. The output can be stdout, a list of strings or anything else meaningful.

Scoring

This is , the usual rules apply.

Test cases

1

["b", ["v", 0, ["h", ["b", "<- Previous"], ["b", "Next ->"]], "== The title ==\n\nLorem ipsum dolor\nsit amet...", ["b", "Post a comment"], ["b", "User X commented:\n\nThis is amazing!"], ["b", "User Y commented:\n\nlol"]]]

+----------------------+
|+-----------++-------+|
||<- Previous||Next ->||
|+-----------++-------+|
|== The title ==       |
|                      |
|Lorem ipsum dolor     |
|sit amet...           |
|+--------------+      |
||Post a comment|      |
|+--------------+      |
|+-----------------+   |
||User X commented:|   |
||                 |   |
||This is amazing! |   |
|+-----------------+   |
|+-----------------+   |
||User Y commented:|   |
||                 |   |
||lol              |   |
|+-----------------+   |
+----------------------+

2

["h", ["b", ["v", 0, "a", "b"]], ["b", ["v", 2, "c", "d", "e"]], ["b", ["v", 1, "f", "g", "h"]]]

   +-+   
   |c|+-+
+-+|d||f|
|a||e||g|
|b|+-+|h|
+-+   +-+

3

["h", ["b", ["v", 0, ["b", ["h", "a\nb", "c"]], "d", "e", ["h", ["h", "f"], ["b", ["h", "g"]], "h"]]], ["b", "ijk\nl\nmn\no"], ["v", 2, ["b", "pqrst"], ["b", "uv\nw"], ["b", "x"]], ["b", ["b", ["b", "yz"]]]]

            +-----+        
            |pqrst|        
            +-----+        
            +--+           
            |uv|           
            |w |   +------+
+-----+     +--+   |+----+|
|+--+ |+---++-+    ||+--+||
||ac| ||ijk||x|    |||yz|||
||b | ||l  |+-+    ||+--+||
|+--+ ||mn |       |+----+|
|d    ||o  |       +------+
|e    |+---+               
| +-+ |                    
|f|g|h|                    
| +-+ |                    
+-----+                    

4

["h", "a * b = ", ["v", 0, "a + a + ... + a", "\\_____________/", "    b times"]]

a * b = a + a + ... + a
        \_____________/
            b times    
\$\endgroup\$
  • 2
    \$\begingroup\$ Sweet jesus... It's geocities all over again. \$\endgroup\$ – Magic Octopus Urn Aug 3 '17 at 15:31
  • \$\begingroup\$ I'm not quite sure how the baseline works, could you explain it further? \$\endgroup\$ – Stan Strum Aug 9 '17 at 8:26
  • \$\begingroup\$ @StanStrum The baseline is used to align the items in a horizontal list vertically. The baselines of the items should match, ie. they should all be the same physical row. For example, in the swcond example the first box has been moved downwards so that the letter a is at the same line as e, as they are both at the baseline of their boxes. I'm not completely sure if "baseline" is the correct word for this, I know only that it is used in the field of typography for a similar purpose. \$\endgroup\$ – fergusq Aug 9 '17 at 10:35
  • \$\begingroup\$ @fergusq On the first clause: "For text elements, the first line of text is the baseline, ie. 0.", does this mean the other "baselines" are shifting the text up? \$\endgroup\$ – Stan Strum Aug 9 '17 at 20:29
  • \$\begingroup\$ @fergusq Baseline should be the correct word, IIRC it's used in the descriptions for CSS flexbox as well \$\endgroup\$ – ASCII-only Aug 10 '17 at 11:42
10
+200
\$\begingroup\$

Python 3, 721 694 693 671 661 bytes

Edit: Saved 27 bytes due to @Arnold Palmer and @Step Hen

Edit: Saved 1 byte

Edit: Saved 22 bytes thanks to @Arnold Palmer

Edit: Saved 10 bytes

This could probably be golfed quite a bit

L,M,R,e=len,max,range,lambda t:([list(r)+[" "]*(M(map(L,t.split("\n")))-L(r))for r in t.split("\n")],0)if str==type(t)else b(t)if"h">t[0]else h(t)if"v">t[0]else v(t);F=lambda t:"\n".join(map("".join,e(t)[0]))
def h(t):
	t=[e(r)for r in t[1:]];Z=M(s[1]for s in t);X=M(L(s[0])-s[1]for s in t)+Z;u=[[]for i in R(X)]
	for a,b in t:u=[u[j]+[Z-b<=j<L(a)+Z-b and a[j-Z+b][i]or" "for i in R(L(a[0]))]for j in R(X)]
	return u,Z
def b(t):t,b=e(t[1]);u=[["+",*"-"*L(t[0]),"+"]];return u+[["|"]+r+["|"]for r in t]+u,1+b
def v(t):w=[e(r)for r in t[2:]];return[a[i]+[" "]*(M(L(a[0])for a,d in w)-L(a[i]))for a,c in w for i in R(L(a))],sum(L(x[0])for x in w[:t[1]])+w[t[1]][1]

Try it online

\$\endgroup\$
  • \$\begingroup\$ You can also move all of the definitions and P,L,M,R,K=[" "],len,max,range,map to the top of the program (variables at the top), and outside of F. Doing that you can get it down to at least 711 bytes. (TIO link too large to post). \$\endgroup\$ – Arnold Palmer Aug 8 '17 at 15:14
  • \$\begingroup\$ @StepHen Not anymore, I've fixed the problem. :-) \$\endgroup\$ – Erik the Outgolfer Aug 9 '17 at 10:24
  • \$\begingroup\$ Down to 671. Had to use tinyurl since the TIO link was way too long. I reworked a lot of your maps since they could be replaced with normal list comprehension stuff. Because I was able to trim so many maps out, I also removed the K variable since it was costing 2 bytes. \$\endgroup\$ – Arnold Palmer Aug 9 '17 at 13:03
  • \$\begingroup\$ Good job. You win the bounty. \$\endgroup\$ – Oliver Ni Aug 14 '17 at 12:05

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.