40
\$\begingroup\$

Given a text like this:

# #### ## #
## #  ##  #
   ####  ##

Output the same text but by connecting the pixels with the characters ─│┌┐└┘├┤┬┴┼. If a pixel doesn't have any neighbours, don't change it.

So the output of the last text is:

│ ─┬── ┌─ │
└─ │  ┌┘  │
   └──┘  ─┘
  • You can take input as a boolean array.
  • The input will always contain at least 1 pixel.
  • You can count box-drawing chars as 1 byte.
  • You can assume the input is padded with spaces.

Test cases

## #
=>
── #
###
 #
=>
─┬─
 │
##### ##
 # #  #
########
=>
─┬─┬─ ┌─
 │ │  │
─┴─┴──┴─
 # #
#####
 # #
=>
 │ │
─┼─┼─
 │ │
# # # # #
 # # # #
# # # # #
 # # # #
# # # # #
=>
# # # # #
 # # # #
# # # # #
 # # # #
# # # # #
#####
#####
#####
#####
#####
=>
┌┬┬┬┐
├┼┼┼┤
├┼┼┼┤
├┼┼┼┤
└┴┴┴┘

Since this is , the shortest code wins.

\$\endgroup\$
  • 2
    \$\begingroup\$ Must I use the hash character as a pixel? Can I receive input as a boolean array? \$\endgroup\$ – Rohan Jhunjhunwala Sep 19 '16 at 21:17
  • \$\begingroup\$ Can there be trailing space(s )or newline(s)? \$\endgroup\$ – Sanchises Sep 20 '16 at 20:25
  • \$\begingroup\$ btw: -|r7LJE3TW+ is a suitable 1-byte-char replacement for the block characters. \$\endgroup\$ – Titus Sep 21 '16 at 22:11

11 Answers 11

7
\$\begingroup\$

Jelly, 60 52 51 50 49 48 bytes

ṖḤ0;+Ḋ×
“µ³Q~E!G⁸ṗṫ\’ḃ61+9471Ọ⁾# j
ZÑ€4×Z++Ñ€ị¢Y

Saved a byte thanks to @Dennis.

The input is a boolean array of 1's and 0's. Iterates over each column and each row converting the head and tail of each infix of size 3 from a pair of binary digits to a decimal, and multiplies that with the center of each infix. Then it sums it with itself to find the index into '#───│┌┐┬│└┘┴│├┤┼ '.

Try it online! (case 2) (case 3) (case 4)

Explanation

This relies on the same idea as my answer in J but instead of processing on each 3x3 subarray, I process over each row and each column while still obtaining the same table of indices.

Over half of the bytes are spent generating the list of box characters '#───│┌┐┬│└┘┴│├┤┼ '. String literals start with in Jelly and have different meanings depending on their terminator. Here the terminator means that the string will be parsed as the code points of each character according to the Jelly code page, and convert from a list of base 250 digits to a decimal.

“µ³Q~E!G⁸ṗṫ\’ => 10041542192416299030874093
(bijective base 61) => [1, 1, 1, 3, 13, 17, 45, 3, 21, 25, 53, 3, 29, 37, 61]
(add 9471 and convert to char) => '───│┌┐┬│└┘┴│├┤┼'

Then convert that decimal to a list of digits in bijective base 61 and increment each by 9471 to move it into the range of the box characters and convert each using Python's chr. Then prepend it with a character literal ”# and append a space .

ṖḤ0;+Ḋ×  Helper link - Input: 1d list A
Ṗ        Get all of A except the last value
 Ḥ       Double each value in it
  0;     Prepend a 0
    +    Add elementwise with
     Ḋ     All of A except the first value
      ×  Multiply elementwise by A

“µ³Q~E!G⁸ṗṫ\’ḃ61+9471Ọ⁾# j  Nilad. Represents '#───│┌┐┬│└┘┴│├┤┼ '
“µ³Q~E!G⁸ṗṫ\’               Get the code points of each char in the string and
                            convert from a list of base 250 digits to decimal
             ḃ61            Convert that to a list of digits in bijective base 61
                +9471       Add 9400 to each
                     Ọ      Convert from ordinals to chars, gets '───│┌┐┬│└┘┴│├┤┼'
                      ⁾#    A pair of chars ['#', ' ']
                         j  Join the pair using the box characters

ZÑ€4×Z++Ñ€ị¢Y  Input: 2d list M
Z              Transpose
 р            Apply the helper link to each row of the transpose (each column of M)
   4×          Multiply each by 4
     Z         Transpose
      +        Add elementwise with M
       +       Add elementwise with
        р       The helper link applied to each row of M
          ị¢   Use each result as an index to select into the nilad
            Y  Join using newlines
               Return and print implicitly
\$\endgroup\$
15
\$\begingroup\$

J, 82 72 66 bytes

(ucp' #───│┌┐┬│└┘┴│├┤┼'){~]+]*3 3((2#.1 7 3 5{,);._3)0,.~0,.0,~0,]

The input is a boolean table of 1's and 0's. The rules state that the box characters each count as one byte, not three, and that has been applied here.

Usage

   f =: (ucp' #───│┌┐┬│└┘┴│├┤┼'){~]+]*3 3((2#.1 7 3 5{,);._3)0,.~0,.0,~0,]
   m =: 1 0 1 1 1 1 0 1 1 0 1 , 1 1 0 1 0 0 1 1 0 0 1 ,: 0 0 0 1 1 1 1 0 0 1 1
   m { ' #'
# #### ## #
## #  ##  #
   ####  ##
   f m
│ ─┬── ┌─ │
└─ │  ┌┘  │
   └──┘  ─┘
   ' #' {~ m =: 5 5 $ 1
   f m
┌┬┬┬┐
├┼┼┼┤
├┼┼┼┤
├┼┼┼┤
└┴┴┴┘
   ' #' {~ m =: 5 9 $ 1 0
# # # # #
 # # # # 
# # # # #
 # # # # 
# # # # #
   f m
# # # # #
 # # # # 
# # # # #
 # # # # 
# # # # #

Explanation

First the input is padded with 0's on all sides.

   ] m =: 1 0 1 1 1 1 0 1 1 0 1 , 1 1 0 1 0 0 1 1 0 0 1 ,: 0 0 0 1 1 1 1 0 0 1 1
1 0 1 1 1 1 0 1 1 0 1
1 1 0 1 0 0 1 1 0 0 1
0 0 0 1 1 1 1 0 0 1 1
   (0,.~0,.0,~0,]) m
0 0 0 0 0 0 0 0 0 0 0 0 0
0 1 0 1 1 1 1 0 1 1 0 1 0
0 1 1 0 1 0 0 1 1 0 0 1 0
0 0 0 0 1 1 1 1 0 0 1 1 0
0 0 0 0 0 0 0 0 0 0 0 0 0

Then each subarray of size 3 is selected

   3 3 <;._3 (0,.~0,.0,~0,]) m
┌─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┐
│0 0 0│0 0 0│0 0 0│0 0 0│0 0 0│0 0 0│0 0 0│0 0 0│0 0 0│0 0 0│0 0 0│
│0 1 0│1 0 1│0 1 1│1 1 1│1 1 1│1 1 0│1 0 1│0 1 1│1 1 0│1 0 1│0 1 0│
│0 1 1│1 1 0│1 0 1│0 1 0│1 0 0│0 0 1│0 1 1│1 1 0│1 0 0│0 0 1│0 1 0│
├─────┼─────┼─────┼─────┼─────┼─────┼─────┼─────┼─────┼─────┼─────┤
│0 1 0│1 0 1│0 1 1│1 1 1│1 1 1│1 1 0│1 0 1│0 1 1│1 1 0│1 0 1│0 1 0│
│0 1 1│1 1 0│1 0 1│0 1 0│1 0 0│0 0 1│0 1 1│1 1 0│1 0 0│0 0 1│0 1 0│
│0 0 0│0 0 0│0 0 1│0 1 1│1 1 1│1 1 1│1 1 0│1 0 0│0 0 1│0 1 1│1 1 0│
├─────┼─────┼─────┼─────┼─────┼─────┼─────┼─────┼─────┼─────┼─────┤
│0 1 1│1 1 0│1 0 1│0 1 0│1 0 0│0 0 1│0 1 1│1 1 0│1 0 0│0 0 1│0 1 0│
│0 0 0│0 0 0│0 0 1│0 1 1│1 1 1│1 1 1│1 1 0│1 0 0│0 0 1│0 1 1│1 1 0│
│0 0 0│0 0 0│0 0 0│0 0 0│0 0 0│0 0 0│0 0 0│0 0 0│0 0 0│0 0 0│0 0 0│
└─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┘

Then only 5 of the values in each subarray is considered

┌───┐
│xAx│
│CED│
│xBx│
└───┘

The values ABCD are selected by flattening each subarray and selecting at indices 1 7 3 5. Those values are multiplied by E which is at index 4. It is then converted from a list of binary digits to a decimal, and incremented by E. The x values are not needed.

   3 3 (4&{([+2#.*)1 7 3 5&{)@,;._3 (0,.~0,.0,~0,]) m
 5 0 2  8 4 3  0  6 3 0  5
10 3 0 13 0 0  6 11 0 0 13
 0 0 0 10 4 4 11  0 0 2 11

This is used as an index to select which character to draw according to the table below (reordered a bit for golfing). The last column matches the output value of each subarray to a box character.

 0  (space)  0
 1  #        1
 2  ┌        6
 3  ┬        8
 4  ┐        7
 5  ├        14
 6  ┼        16
 7  ┤        15
 8  └        10
 9  ┴        12
10  ┘        11
11  │        5, 9, 13
12  ─        2, 3, 4

Also, in J, the string ' #───│┌┐┬│└┘┴│├┤┼' uses 8-bit characters making it have a length of 47 (for each byte) for the 17 characters needed. The command ucp converts it to 16-bit characters which allows it to be length 17.

\$\endgroup\$
13
\$\begingroup\$

JavaScript (ES6), 155 121 103 102 characters

let f =
    
s=>s.replace(/#/g,(c,p)=>'#│─┘─└─┴││┐┤┌├┬┼'[t=x=>s[p+x]==c,8*t(w=s.search`
`+1)+4*t(1)+2*t(-1)+t(-w)])

console.log(f(
  '# #### ## #\n' +
  '## #  ##  #\n' +
  '   ####  ##'
));

Edit: saved 18 bytes with the help of ETHproductions
Edit: saved 1 byte by using the 1st parameter of replace() as '#'

How it works

We iterate on all # characters found in the input string. For each of them, we test whether its neighbors are also # characters using the t() function:

t = x => s[p + x] == c  // where c = '#'

The parameter x of the t() function is the offset of the neighbor with respect to the current position p. We use -1/+1 to test left/right neighbors and -w/+w for top/bottom neighbors (where w is the width of a row, i.e. the position of the first line-break + 1).

Each neighbor is assigned a different weight (1, 2, 4 or 8) according to the following compass:

  1
2 + 4
  8

Each weight combination leads to unique value in [ 0 .. 15 ]. For instance, if both the neighbor at the top and the neighbor on the right are set, the sum will be 1 + 4 = 5, which is translated into using this table:

00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15
#  │  ─  ┘  ─  └  ─  ┴  │  │  ┐  ┤  ┌  ├  ┬  ┼

Therefore, '#│─┘─└─┴││┐┤┌├┬┼'[weight_sum] leads to the expected character.

\$\endgroup\$
  • \$\begingroup\$ Ha, we had basically the same idea ;) \$\endgroup\$ – ETHproductions Sep 19 '16 at 22:32
  • \$\begingroup\$ @ETHproductions - Pretty much so. ^^ \$\endgroup\$ – Arnauld Sep 19 '16 at 22:48
  • \$\begingroup\$ Really, really nice technique there. You've solidly out-golfed me :) \$\endgroup\$ – ETHproductions Sep 20 '16 at 14:55
  • \$\begingroup\$ You can save 2 bytes like so: s=>(w=s[0].length+1,s=s.join`\n`).replace(/#/g,(_,p)=>'#│─┘─└─┴││┐┤┌├┬┼'[t=x=>s[p+x]>' ',t(-w)+2*t(-1)+4*t(1)+8*t(w)]) \$\endgroup\$ – ETHproductions Sep 20 '16 at 15:07
  • \$\begingroup\$ And it's actually far shorter to switch back to a multiline string: s=>s.replace(/#/g,(_,p)=>'#│─┘─└─┴││┐┤┌├┬┼'[t=x=>s[p+x]>' ',t(-w)+2*t(-1)+4*t(1)+8*t(w)],w=s.indexOf`\n`+1) \$\endgroup\$ – ETHproductions Sep 20 '16 at 15:09
8
\$\begingroup\$

Python 2.7, 318 315 bytes (270 267 chars)

I'm sure this can be golfed further (particularly I'd love to get rid of that annoying first-line comment) but here's my entry:

#encoding:utf-8
f=lambda t:(lambda l,s:'\n'.join(''.join((u'┼├┤│┬┌┐│┴└┘│───#'[(s==l[i][j-1])+2*(s==l[i][j+1])+4*(i<1 or s==l[i-1][j])+8*(i>len(l)-2 or s==l[i+1][j])],s)[s==l[i][j]]for j in range(len(l[i])-1))for i in range(len(l))))([l+' 'for l in t.split('\n')],' ')

Here's an explanation of how the whole thing works:

#encoding:utf-8 # Dammit, Python. This adds an extra 16 bytes!
f=lambda t:( # main lambda function
    lambda l,s: # inner lambda so we can pass it "local constants" (see last line)
        '\n'.join( # join each line
            ''.join( # join each char within the line
                (u'┼├┤│┬┌┐│┴└┘│───#'[ # string of all possible characters, indexed by binary 0-15 based on adjacent chars
                    (s==l[i][j-1])+ # left
                    2*(s==l[i][j+1])+ # right
                    4*(i<1 or s==l[i-1][j])+ # up ('i<1' just yields zero in case this is the first line, so that we don't get index problems)
                    8*(i>len(l)-2 or s==l[i+1][j])], # down ('i>len(l)-2' is same as above)
                s)[s==l[i][j]] # if original is space, choose space, else choose the previously chosen box-drawing char
                for j in range(len(l[i])-1)) # do this process for each char (excluding last, which is a space)
            for i in range(len(l))) # do this for each line
    )([l+' ' for l in t.split('\n')],' ') # call the inner lambda with two parameters: first, the text split into lines; second, a space char (actually makes code shorter)

EDIT: Removed some spaces before for ... in ...

\$\endgroup\$
  • 9
    \$\begingroup\$ Using Python 3 instead should solve your Unicode encoding problem, I think... \$\endgroup\$ – Byte Commander Sep 20 '16 at 0:49
6
\$\begingroup\$

JavaScript (ES6), 150 139 133 131 chars

a=>a.map((q,y)=>q.replace(/#/g,(c,x)=>"#│─┘─└─┴││┐┤┌├┬┼"[g=(X,Y=0)=>(a[Y+y]||[])[X+x]==c,g(0,1)*8+g(1)*4+g(-1)*2+g(0,-1)])).join`
`

Takes input as an array of strings, e.g. f(["###", " # "]).

Test snippet

f=a=>a.map((q,y)=>q.replace(/#/g,(c,x)=>"#│─┘─└─┴││┐┤┌├┬┼"[g=(X,Y=0)=>(a[Y+y]||[])[X+x]==c,g(0,1)*8+g(1)*4+g(-1)*2+g(0,-1)])).join`
`
<textarea id=I rows=6># #### ## #
## #  ##  #
   ####  ##</textarea>
<br>
<button onclick="O.innerHTML=f(I.value.split('\n')).replace(/\n/g,'<br>')">Run</button>
<pre id=O></pre>

\$\endgroup\$
5
\$\begingroup\$

ALPACA, 414 + 2 = 416 bytes

neighbourhoodV(^ v < >);states" ";statep"#"toA when4inV p,toB when3inV p andvs,toC when3inV p and^s,toD when3inV p and>s,toE when3inV p and<s,toF when2inV p and>s andvs,toG when2inV p andvs and<s,toH when2inV p and<s and^s,toI when2inV p and^s and>s,toJ when^p orvp,toK when<p or>p;stateA"┼";stateB"┴";stateC"┬";stateD"┤";stateE"├";stateF"┘";stateG"└";stateH"┌";stateI"┐";stateJ"│";stateK"─".

Requires the -fI flags.

This solution uses a very large number of bytes, but it is unique in that it uses a cellular automaton. ALPACA is usually used as a metalanguage, but here I'm using it as a programming language.

Ungolfed version:

neighbourhood V (^ v < >);
state s " ";
state p "#" to A when 4 in V p,
to B when 3 in V p and v s,
to C when 3 in V p and ^ s,
to D when 3 in V p and > s,
to E when 3 in V p and < s,
to F when 2 in V p and > s and v s,
to G when 2 in V p and v s and < s,
to H when 2 in V p and < s and ^ s,
to I when 2 in V p and ^ s and > s,
to J when ^ p or v p,
to K when < p or > p;
state A "┼";
state B "┴";
state C "┬";
state D "┤";
state E "├";
state F "┘";
state G "└";
state H "┌";
state I "┐";
state J "│";
state K "─".
\$\endgroup\$
4
\$\begingroup\$

PHP, 203 bytes

This can probably be done in a shorter way.

while($s=fgets(STDIN))$f[]=$s;foreach($f as$y=>&$s)for($x=strlen($s);$x--;)if($s[$x]>$b=" ")$s[$x]="#───│┘└┴│┐┌┬│┤├┼"[($s[$x-1]>$b)+2*($s[$x+1]>$b)+4*($f[$y-1][$x]>$b)+8*($f[$y+1][$x]>$b)];echo join($f);

reads input from STDIN. run with -r.

\$\endgroup\$
4
\$\begingroup\$

Python 3, 149 bytes

def f(s):S=' ';w=s.find('\n')+1;t=lambda i:(s+w*S)[i]>S;return[[c,'#│─┘─└─┴││┐┤┌├┬┼'[t(p-w)+2*t(p-1)+4*t(p+1)+8*t(p+w)]][c>S]for p,c in enumerate(s)]

Takes input like ##\n #\n and returns output like ['─', '┐', '\n', ' ', '│', '\n'].

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

R, 199 212 bytes

EDIT: It's now a function, rather than a code snippet.

The input is a matrix m of 1s and 0s. This is pretty ugly and hacky.

function(m){
v=strsplit(" #─│┘│┐│┤──└┴┌┬├┼","")[[1]]
d=dim(m)+1
n=array(0,dim=d+1)
n[2:d[1],2:d[2]]=m
for(i in 0:(d[1]-2)){for(j in 0:(d[2]-2))cat(v[1+(p<-n[2+i,2+j])*sum(2^(0:3)*n[1:3+i,1:3+j][1:4*2])+p]);cat("\n")}
}

A couple of tests:

> m = matrix(c(1, 1, 1, 0, 1, 0), nrow=2, byrow=TRUE)
> v=strsplit(" #─│┘│┐│┤──└┴┌┬├┼","")[[1]]
> d=dim(m)+1
> n=array(0,dim=d+1)
> n[2:d[1],2:d[2]]=m
> for(i in 0:(d[1]-2)){for(j in 0:(d[2]-2))cat(v[1+(p<-n[2+i,2+j])*sum(2^(0:3)*n[1:3+i,1:3+j][1:4*2])+p]);cat("\n")}
─┬─
 │ 
> m = matrix(rep(1, 16), ncol=4)
> v=strsplit(" #─│┘│┐│┤──└┴┌┬├┼","")[[1]]
> d=dim(m)+1
> n=array(0,dim=d+1)
> n[2:d[1],2:d[2]]=m
> for(i in 0:(d[1]-2)){for(j in 0:(d[2]-2))cat(v[1+(p<-n[2+i,2+j])*sum(2^(0:3)*n[1:3+i,1:3+j][1:4*2])+p]);cat("\n")}
┌┬┬┐
├┼┼┤
├┼┼┤
└┴┴┘
\$\endgroup\$
  • \$\begingroup\$ Submission must be either a full program or a function. Reading from a existing variable is not an allowed form of input. \$\endgroup\$ – TuxCrafting Sep 20 '16 at 16:17
  • \$\begingroup\$ How do you get some of the unicode characters to work nicely with R? x = "\U253C" works but x = "┼" does not. \$\endgroup\$ – Vlo Sep 20 '16 at 20:16
  • \$\begingroup\$ @TùxCräftîñg: Fixed now! \$\endgroup\$ – rturnbull Sep 20 '16 at 20:41
  • \$\begingroup\$ @Vlo utf-8 is my native OS encoding. x = "┼" works just fine for me. \$\endgroup\$ – rturnbull Sep 20 '16 at 20:42
3
\$\begingroup\$

Perl, 89 88 bytes

Includes +2 for -0p. The special characters are counted as 1 byte, but to make them actually display as single characters it's best to also add the -C option.

Give input on STDIN with the lines space padded so they all have the same length:

perl -C connect.pl
# #### ## #
## #  ##  #
   ####  ##
^D

connect.pl:

#!/usr/bin/perl -0p
/
/;$n=".{@-}";s%#%substr"#───│└┘┴│┌┐┬│├┤┼",/\G##/+2*/#\G/+4*/#$n\G/s+8*/\G#$n#/s,1%eg
\$\endgroup\$
1
\$\begingroup\$

MATL, 102 characters

I assign a neighbour a value (1, 2, 4 or 8); their sum will match a character in a string containing the drawing characters. I think there is still lots of room for improvements, but for a rough draft:

' #│││─┌└├─┐┘┤─┬┴┼' % String to be indexed
wt0J1+t4$(          % Get input, and append zeros at end of matrix (solely to avoid
                    %  indexing a non-existent second row/column for small inputs)
ttttt               % Get a whole load of duplicates to work on
3LXHY)              % Select rows 2:end from original matrix (one of the many dupes)
      w4LXIY)       % Select rows 1:end-1 from the 'destination' summing matrix)
             +HY(   % Add the neighbors below, store in 'destination' matrix
tIY)b3LY)2*+IY(     % +2* the neighbors above    +-------------------------------+
tHZ)b4LZ)4*+HZ(     % +4* the neighbors right    |(note: H and I contain 1:end-1 |
tIZ)b3LZ)8*+IZ(     % +8* the neighbors left     |  and 2:end respectively)      |
HH3$)               % Select the original matrix +-------------------------------+
*                % Make sure only cells that originally had a # get replaced
  1+)            % Add one because the original string is one-indexed. Index using ).

Improvements to be made:

  • Possibly replace the whole summing part with some kind of loop working on rotated copies of the matrix
  • Ditch the entire thing and make something based on a single loop working through the matrix
  • Use modular indexing to work on a flattened vector from the original array (?)

Try it Online! (may not have box drawing characters support)

\$\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.