TransWikia.com

Connect the pixels

Code Golf Asked by TuxCrafting on October 27, 2021

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.

13 Answers

Charcoal, 45 bytes

WS⊞υιP⪫υ⸿F⪫υ⸿≡ι#§”y#─│┐──┌┬│┘│┤└┴├┼”↨EKV›κ ²ι

Try it online! Link is to verbose version of code. Takes input as a newline-terminated list of strings. Scored using 15 1-byte box drawing characters (normally 3 bytes each). Explanation:

WS⊞υι

Read in the text.

P⪫υ⸿

Print it without moving the cursor.

F⪫υ⸿

Loop over the text again.

≡ι#

If the current character is a #...

§”y#─│┐──┌┬│┘│┤└┴├┼”↨EKV›κ ²

... then look up the box drawing character based on the presence of neighbours.

ι

Otherwise just print the current character.

Answered by Neil on October 27, 2021

APL (Dyalog Extended), 9 bytes

'#'⌂draw⊢

Try it online!

A built-in that solves the challenge (except that it accepts the '*' symbol as the default, so '#' needs to be supplied as an explicit argument). ¯_(⍨)_/¯

Documentation is here.


A non-built-in solution, because a built-in is boring:

APL (Dyalog Extended), 43 bytes

' #│─┌─┐─┬││└├┘┤┴┼'⊇⍨⊢×0 0{1+⊥⊢/4 2⍴⍵}⌺3 3⊢

Try it online!

An anonymous function that takes the boolean matrix as its argument. Incidentally it wins over Jelly even without the built-in.

The approach is pretty similar to many other answers (base-2 conversion from the four neighbors and index into the target string).

One interesting thing to note is that the box-drawing characters are already part of the SBCS, so it is completely fine to use them in the code.

How it works

' #│─┌─┐─┬││└├┘┤┴┼'⊇⍨⊢×0 0{1+⊥⊢/4 2⍴⍵}⌺3 3⊢
  0 0{          }⌺3 3⊢  ⍝ Map over 3×3 submatrices, with zero padding...
           4 2⍴⍵        ⍝ Reshape 3×3 to 4×2:
                        ⍝ . A .   . A
                        ⍝ B . C → . B
                        ⍝ . D .   . C
                        ⍝         . D
         ⊢/  ⍝ Take the last column
        ⊥    ⍝ Convert base 2 to integer
      1+     ⍝ Offset 1 to distinguish between ' ' and '#'
⊢×  ⍝ Mask the places for blanks
' #│─┌─┐─┬││└├┘┤┴┼'⊇⍨  ⍝ Convert each value in the matrix to the char at that index

Answered by Bubbler on October 27, 2021

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 "─".

Answered by DanTheMan on October 27, 2021

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*/#$nG/s+8*/G#$n#/s,1%eg

Answered by Ton Hospel on October 27, 2021

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)

Answered by Sanchises on October 27, 2021

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")}
┌┬┬┐
├┼┼┤
├┼┼┤
└┴┴┘

Answered by rturnbull on October 27, 2021

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

Answered by miles on October 27, 2021

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'].

Answered by Lynn on October 27, 2021

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.

Answered by miles on October 27, 2021

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

Answered by Hactar on October 27, 2021

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.

Answered by Titus on October 27, 2021

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>

Answered by ETHproductions on October 27, 2021

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.

Answered by Arnauld on October 27, 2021

Add your own answers!

Ask a Question

Get help from others!

© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP