2
\$\begingroup\$

In the old DOS operating system, a number of characters were provided to draw boxes. Here is a selection of these characters and their code points:

B3 B4 BF C0 C1 C2 C3 C4 C5 D9 DA
│  ┤  ┐  └  ┴  ┬  ├  ─  ┼  ┘  ┌

You can use these characters to draw boxes like this:

┌─────────┐
│         │
└─────────┘

Boxes can be attached to each other:

        ┌───┐
        │   │
┌───┬───┤   │
│   │   │   │
│   ├───┴───┘
│   │
└───┘

or intersect each other:

┌─────┐
│     │
│  ┌──┼──┐
│  │  │  │
└──┼──┘  │
   │     │
   └─────┘

or overlap each other partially or completely:

┌─────┐
├──┐  │
│  │  │
├──┘  │
└─────┘

Challenge description

Write a program that receives a series of positive decimal integers separated by whitespace from standard input. The number of integers you receive is a multiple of four, each set of four integers x1 x2 y1 y2 is to be interpreted as the coordinates of two points x1 y1 and x2 y2 forming opposing corners of a box. You may assume that for each set of coordinates, x1x2 and y1y2.

The coordinate system originates in the top left corner with x coordinates progressing to the right and y coordinates progressing downwards. You can assume that no x coordinate larger than 80 and no y coordinate larger than 25 appears.

Your program shall output in either UTF-8, UTF-16, or Codepage 437 a series of whitespace, carriage returns, line feeds, and box drawing characters such that the output shows the boxes described in the input. There may be extraneous whitespace at the end of lines. You may terminate lines either with CR/LF or just LF but stick to one, stray CR characters are not allowed. You may output any number of lines filled with an arbitrary amount of whitespace at the end of the output.

Examples

The following input draws the first diagram from the question:

1 11  1 3

The second diagram is offset a little bit, the leading whitespace and empty lines must be reproduced correctly:

17 13   3  7
 9  5   5  9
 9 13   5  7

The third diagram also tests that input is parsed correctly:

1 7 1
5 4 10
3 7

The fourth diagram:

11 5 2 6
5 8 3 5

As a final test case, try this fifth diagram:

    ┌─────┬─────┐
┌───┼───┬─┼─┬───┴───┐
│   │   │ │ │       │
└───┼───┴─┼─┴───┬───┘
    └─────┴─────┘

Which looks like this:

9 21 2 4 1
13 4 2 11 5
1 5 17 11 1
2 11 17 4 5

Winning condition

This is code golf. The shortest solution in octets (bytes) wins. Standard loopholes apply, (worthless) bonus points for writing a solution that works on DOS.

\$\endgroup\$
15
  • \$\begingroup\$ Any particular reason for the restrictive input format? \$\endgroup\$ Apr 11 '17 at 15:41
  • \$\begingroup\$ @AdmBorkBork I want people to not worry about array sizes and stuff like that if they chose to write an implementation for DOS. For typical code golf DSLs, this probably doesn't matter. \$\endgroup\$
    – FUZxxl
    Apr 11 '17 at 15:42
  • 2
    \$\begingroup\$ @FUZxxl "Flexible input format" doesn't mean "you have to parse every possible way this data could be formatted" but "you may choose an input format that is convenient for you so that you can focus on the actual task at hand instead of processing the input". So languages where your format is the simplest could still use that format, but others could use whatever's more natural for their language. \$\endgroup\$ Apr 12 '17 at 9:43
  • 1
    \$\begingroup\$ @JörgHülsermann All of these questions are answered in the question. Coordinates must be positive, x coordinates are between 1 and 80 (inclusive), y coordinates are between 1 and 25 (inclusive). \$\endgroup\$
    – FUZxxl
    Apr 12 '17 at 13:24
  • 4
    \$\begingroup\$ -1 Because of the strict input format. \$\endgroup\$
    – mbomb007
    Apr 12 '17 at 14:10
4
\$\begingroup\$

PHP>=7.1, 820 Bytes

<?preg_match_all("#(\d+(\s+\d+){3})#s",$_GET[0],$t);$r=($w="array_fill")(1,25,$w(1,80," "));function u($n,$y,$x){global$r;$u=[$z=[" ","│",$c="┤","┐","└",$d="┴",$e="┬",$b="├","─","┘","┌",$a="┼"],["│",$c,$b,$a],[$c,$a],["┐",$c,$e,$a],["└",$d,$b,$a],[$d,$a],[$e,$a],[$b,$a],["─",$d,$e,$a],["┘",$c,$d,$a],["┌",$e,$b,$a],[$a]];$o=$r[$y][$x];$s=array_intersect($u[($k="array_search")($n,$z)],$u[$k($o,$z)]);$r[$y][$x]=reset($s);}foreach($t[1]as$e){[$a,$b,$c,$d]=explode(" ",preg_replace("#\s+#"," ",$e));u("┌",$h=min($c,$d),$f=min($a,$b));u("┐",$h,$g=max($a,$b));u("└",$i=max($c,$d),$f);u("┘",$i,$g);foreach(($l="array_slice")(range($f,$g),1,-1)as$x){u("─",$h,$x);u("─",$i,$x);}foreach($l(range($h,$i),1,-1)as$y){u("│",$y,$f);u("│",$y,$g);}}foreach($r as$v)echo join($v)."\n";

-6 Bytes for use "array_fill","array_search", "array_slice" without "

+19 Bytes echo ltrim(rtrim(join($v))."\n","\n"); instead of echo join($v)."\n"; to print only the necessary Chars

Online Version

Expanded

preg_match_all("#(\d+(\s+\d+){3})#s",$_GET[0],$t); # find all rects
$r=($w="array_fill")(1,25,$w(1,80," ")); # fill a empty 2 D array with spaces 
function u($n,$y,$x){ # Char , Y Coordinate, X Coordinate as parameter
global$r; # result array must be global to make changes
# The following array based on Set Theory
$u=[
$z=[" ","│",$c="┤","┐","└",$d="┴",$e="┬",$b="├","─","┘","┌",$a="┼"],
["│",$c,$b,$a],
[$c,$a],
["┐",$c,$e,$a],
["└",$d,$b,$a],
[$d,$a],
[$e,$a],
[$b,$a],
["─",$d,$e,$a],
["┘",$c,$d,$a],
["┌",$e,$b,$a]
,[$a]];
$o=$r[$y][$x]; # old value for YX
$s=array_intersect($u[($k="array_search")($n,$z)],$u[$k($o,$z)]); # make the Cut quantity
$r[$y][$x]=reset($s); # Take the first value Cut quantity and set it as new value
}
foreach($t[1]as$e){ # for each rect
[$a,$b,$c,$d]=explode(" ",preg_replace("#\s+#"," ",$e)); #split the four coordinates
# next 4 rows make edges and set minimum and maximum for X an Y values
u("┌",$h=min($c,$d),$f=min($a,$b)); 
u("┐",$h,$g=max($a,$b));
u("└",$i=max($c,$d),$f);
u("┘",$i,$g);
foreach(($l="array_slice")(range($f,$g),1,-1)as$x){u("─",$h,$x);u("─",$i,$x);} # make the X lines
foreach($l(range($h,$i),1,-1)as$y){u("│",$y,$f);u("│",$y,$g);} # make the  Y lines
}
foreach($r as$v)echo join($v)."\n"; # Output

Order of the array Set Theory

foreach($u as $k0=>$v0)
foreach($u as $k1=>$v1)
echo "\n\n'".$z[$k0]."' + '".$z[$k1]."' = '". join("','",array_intersect($v0,$v1))."'";

Examples Set Theory all possible values

The array without the use of variables to short it

$u=[
$z=[" ","│","┤","┐","└","┴","┬","├","─","┘","┌","┼"],
["│","┤","├","┼"],
["┤","┼",],
["┐","┤","┬","┼"],
["└","┴","├","┼"],
["┴","┼"],
["┬","┼"],
["├","┼"],
["─","┴","┬","┼"],
["┘","┤","┴","┼"],
["┌","┬","├","┼"]
,["┼"]];');
\$\endgroup\$
8
  • \$\begingroup\$ If you use UNIX line endings, this is just 1234 bytes. \$\endgroup\$
    – FUZxxl
    Apr 12 '17 at 1:16
  • \$\begingroup\$ I get a parser error when trying to run your code in the site you linked. \$\endgroup\$
    – FUZxxl
    Apr 12 '17 at 3:00
  • \$\begingroup\$ @FUZxxl Yes I use the new list syntax that is only available with PHP 7.1. Please Change the PHP Version before execute the Code \$\endgroup\$ Apr 12 '17 at 9:19
  • \$\begingroup\$ You have a bug in your code: You preprend one empty line and one empty column to the output. Note that the origin is supposed to be (1, 1) not (0, 0). \$\endgroup\$
    – FUZxxl
    Apr 12 '17 at 13:22
  • \$\begingroup\$ @FUZxxl Now it is 1 Indexing and the best I could save 15 % by hardcoded the start value of the result array \$\endgroup\$ Apr 12 '17 at 13:48
3
\$\begingroup\$

CJam, 129

0a80*a25*[q~]4/{:(2/:$_::-+e_534915808 6b3/\ff=C2b.+{):A;)z{_A)+\[A!A].+_A)4*+\}*;}%{~:A;_3$=@_2$=A|tt}/}/"   ┌ ─┐┬ └│├┘┴┤┼"ff=N*

Try it online

Notes:

  • If the boxes don't look quite right, it's possible that your browser is using a fallback font for box drawing characters.
  • CJam doesn't specify an encoding for source files. In UTF-8, this code has 151 bytes, but you can save it using CP437 or CP850 instead (and run it with the same encoding), then it will use 1 byte/character.

Overview:

0a80*a25* creates a matrix of 80×25 zeros
[q~]4/ reads the input, converts to numbers and splits into quadruplets
:(2/:$_::-+e_ converts an [x1 x2 y1 y2] quadruplet to [xmin xmax ymin ymax -width -height] (negative values will be corrected later)
534915808 6b3/ generates [[1 2 5] [0 2 5] [0 3 4] [0 2 4]], to be used as indices in the previous array for extracting data about the 4 sides of the current box
\ff= extracts the data for each side, e.g. [1 2 5] -> [xmax ymin -height] for the right side
C2b.+ appends 1, 1, 0, 0 respectively to the arrays for the 4 sides (right, left, bottom, top); this number indicates the direction (0=horizontal, 1=vertical)
):A;)z{_A)+\[A!A].+_A)4*+\}*; generates line pieces for a side, as [x y bitmask] triplets; each piece is a line going from the center of the cell to one of 4 directions: 1=right, 2=bottom, 4=left, 8=top; e.g. [4 2 -3 0] (a horizontal line of length 3 starting at x=4, y=2) results in [4 2 1] [5 2 4] [5 2 1] [6 2 4] [6 2 1] [7 2 4]
{~:A;_3$=@_2$=A|tt}/ bitwise-OR's all these line pieces into the matrix, resulting in bitmasks from 0..15
" ┌ ─┐┬ └│├┘┴┤┼"ff= converts these bitmasks to the corresponding box-drawing characters
N* joins with newlines for display

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

Haskell, 399 340 398 bytes

-59 (!) bytes thanks to Ørjan Johansen

import Data.Bits
import Data.Array
(&)=(,)
z=maximum
r x=[minimum x..z x-1]
l=map(+1).r
e(a:b:c:d:q)=[x&y&n|(n,(f,g))<-zip[0..][id&l,id&r,l&id,r&id],x<-f[a,b],y<-g[c,d]]++e q;e[]=[]
f a|m<-z a=unlines[[" UD│L┘┐┤R└┌├─┴┬┼"!!(accumArray setBit 0(1&1,m&m)(e a)!(x,y))|x<-[1..m]]|y<-[1..m]]
main=interact$f.map read.words

Try it online!

Ungolfed

import Data.Bits
import Data.Array
import Data.List.Split

(&) = (,) -- Alias for tuples

-- Given [a,b], returns a list of all x: a <= x < b 
halfOpenLeft x = [minimum x..maximum x-1::Int]

-- Same as above, but for all x: a < x <= b
halfOpenRight = map(+1).halfOpenLeft

up    = ((id, halfOpenRight), 0) -- Coordinates that should have a line upwards
down  = ((id, halfOpenLeft),  1) -- Coordinates that should have a line downwards
left  = ((halfOpenRight, id), 2) -- Coordinates that should have a line leftwards
right = ((halfOpenLeft, id),  3) -- Coordinates that should have a line rightwards

-- Create a map between coordinates and line-segments. Each coordinate can have multiple line-segments.
lineSegments :: [[Int]] -> [((Int,Int),Int)]
lineSegments rects = [ ((x,y),n) | [x1,x2,y1,y2] <- rects, ((f,g),n) <- [up,down,left,right], x <- f [x1,x2], y <- g [y1,y2]]

-- Convert an array of bit-fields into a list of lines. 
printBitfieldArray :: Int -> Array (Int,Int) Int -> [String]
printBitfieldArray size a = [[" UD│L┘┐┤R└┌├─┴┬┼"!!(a!(x,y))|x<-[1..size]]|y<-[1..size]]

-- Merge all the line-segments for each coordinate into a bitfield
createBitFieldArray :: Int -> [((Int,Int),Int)] -> Array (Int,Int) Int
createBitFieldArray m = accumArray setBit 0 ((1,1),(m,m))

-- First split the input for each rectangle,
-- then create a list of pairs of coordinates and directions,
-- then merge all directions for each coordinate into a bitfield
-- and finally use the bitfield as an index to find out which shape to draw.
drawRectangles :: Int -> [Int] -> [String]
drawRectangles m=printBitFieldArray m.createBitFieldArray m.lineSegments.chunksOf 4

-- Parse the input, solve the problem and format the output. Use the largest number as both width and height.
main=interact$unlines.(drawRectangles=<<maximum).map read.words

Explanation

  1. First, lineSegments (or d in the golfed version) will loop over all rectangles, all directions and all coordinates that should have a line-segment in that direction from that rectangle and create a list of coordinates paired with directions.
  2. Then createBitFieldArry merges all entries with identical coordinates into an array of bitfields. Here, the 0th bit means up, 1st means down, 2nd means left and the 3rd means right. In other words, 5 is rendered as "┘", 0 is " " and 15 is "┼".
  3. Lastly, printBitFieldArray (or p) converts the bitfields into box-drawing characters by using them as indices.
\$\endgroup\$
7
  • 2
    \$\begingroup\$ (1) You can use & in more places, x&y&n and (1&1,m&m). (2) You don't need the ::Int. (3) You don't need Data.List.Split, it's cheaper to pattern match like e(a:b:c:d:q)=...++e q;e[]=[]. (Renaming d due to shadowing.) \$\endgroup\$ Nov 29 '17 at 4:19
  • 1
    \$\begingroup\$ Try it online! \$\endgroup\$ Nov 29 '17 at 4:19
  • 2
    \$\begingroup\$ A bit more by inlining p into f and shuffling some things from main to f: Try it online! \$\endgroup\$ Nov 29 '17 at 4:43
  • \$\begingroup\$ @ØrjanJohansen Awsome, Thanks! \$\endgroup\$
    – Hjulle
    Nov 29 '17 at 23:01
  • \$\begingroup\$ You're welcome! According to TIO, the lengths should be <s>388</s> <s>340</s> 338 (so I saved 48 bytes). \$\endgroup\$ Nov 30 '17 at 0:42
1
\$\begingroup\$

Python 3, 632 bytes

i=input()
j=''
while i:j+=i+' ';i=input()
c=[int(k)-1for k in j.split()]
c=[c[k:k+4]for k in range(0,len(c),4)]
for q in c:
    for v,w in[(0,1),(2,3)]:
        if q[w]<q[v]:t=q[w];q[w]=q[v];q[v]=t
d=list(zip(*c))
w=max(d[1])+1
h=max(d[3])+1
g=[0]*w*h
def i(a,b,f):
    for k in b:
        for l in a:g[k*w+l]|=f[0];f=f[1:]
def p(k,l,m,n,o,j=0):
    for i in range(m,n,o):l[i]|=k
    if j:p(k,l,m+j,n+j,o)
for q in c:i(q[:2],q[2:],[6,12,3,9]);p(10,g,q[0]+1+q[2]*w,q[1]+q[2]*w,1,(q[3]-q[2])*w);p(5,g,q[0]+q[2]*w+w,q[0]+q[3]*w,w,q[1]-q[0])
print('\n'.join(map(lambda k: ''.join(map(lambda x: '   └ │┌├ ┘─┴┐┤┬┼'[x], k)),[g[x:x+w]for x in range(0,len(g),w)])))
\$\endgroup\$
1
\$\begingroup\$

Node, 536 bytes

s=''
with(process.stdin){setEncoding('utf8')
on('readable',_=>s+=read()||'')
on('end',_=>{a=s.match(/(\d+\s+){3}\d+/g).map(s=>s.match(/\d+/g).map(m=>+m)).map(([m,n,o,p])=>[m>n?n:m,m>n?m:n,o>p?p:o,o>p?o:p])
m=i=>[...Array(Math.max(...a.map(a=>a[i])))]
console.log(m(3).map((_,y)=>m(1).map((_,x)=>' ??┘?│┐┤?└─┴┌├┬┼'[x++,a.some(([m,n,o,p])=>x==m|x==n&&y>o&y<=p)+2*a.some(([m,n,o,p])=>y==o|y==p&&x>m&x<=n)+4*a.some(([m,n,o,p])=>x==m|x==n&&y>=o&y<p)+8*a.some(([m,n,o,p])=>y==o|y==p&&x>=m&x<n)],y++).join``).join`\n`)})}

Directly calculates the correct character at each coordinate. Input is annoying to perform in Node...

\$\endgroup\$

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.