# Pretty print a grid of polyominoes

Write a function that accepts a rectangular grid of ids in any reasonable format, for example a multi-line string:

IIILOO
ILLLOO

and a string or list of box drawing charcters such as:

' ═║╔╗╚╝╠╣╦╩╬'

or

[0x20, 0x2550, 0x2551, 0x2554, 0x2557, 0x255a, 0x255d, 0x2560, 0x2563, 0x2566, 0x2569, 0x256c']

(see below for more details) and returns a pretty printed version, as such:

╔═══════════╦═══╦═══════╗
║           ║   ║       ║
║   ╔═══════╝   ║       ║
║   ║           ║       ║
╚═══╩═══════════╩═══════╝

i.e. a rectangle made out of the box drawing characters, where each border indicates the border of each polyomino.

╔═══════════╦═══╦═══════╗
║ I   I   I ║ L ║ O   O ║
║   ╔═══════╝   ║       ║
║ I ║ L   L   L ║ O   O ║
╚═══╩═══════════╩═══════╝

# Rules

Shortest submission in bytes per language wins. Standard rules apply.

Note that to leave room for annotations and as the characters used are half width each unit square is 2x4 (3x5 but boundary fields are shared with neighbouring squares) characters in the output.

You may assume that input is clean rectangular and ids are unique to orthogonally connected regions.

To avoid any unicode related issues I have decided to provide the box drawing characters as an input. This may be any flat container holding the characters in any order as long as it contains

1. each character exactly once and
2. only these characters (no fillers)
3. access is sequential or per index only (no sophisticated lookup tables)

# More examples:

In:

XXXSS
RXSSL
ROOOL
RROLL

' ═║╔╗╚╝╠╣╦╩╬'

Out:

╔═══════════╦═══════╗
║           ║       ║
╠═══╗   ╔═══╝   ╔═══╣
║   ║   ║       ║   ║
║   ╠═══╩═══════╣   ║
║   ║           ║   ║
║   ╚═══╗   ╔═══╝   ║
║       ║   ║       ║
╚═══════╩═══╩═══════╝

In:

1220003
2240503
2444666

' ═║╔╗╚╝╠╣╦╩╬'

Out:

╔═══╦═══════╦═══════════╦═══╗
║   ║       ║           ║   ║
╠═══╝   ╔═══╣   ╔═══╗   ║   ║
║       ║   ║   ║   ║   ║   ║
║   ╔═══╝   ╚═══╬═══╩═══╩═══╣
║   ║           ║           ║
╚═══╩═══════════╩═══════════╝

# Charcoal, 1471298078767571 65 bytes

≔⁺ＳψθＷＳＦ²⊞υ⁺⭆ι×⁴λψ⊞υ⭆⌊υψＥυ⭆ＥιＥ⁴§§υ⁻κ÷ν²⁻μ∨⁼ν¹⁼ν²§θ﹪×⁷↨²Ｅλ⁼ν§λ⊕ξ⁹⁵

Try it online! Link is to verbose version of code. Expects the box-drawing characters first in the order ╬╦╠╔╩═╚╣╗║ ╝ followed by the newline-terminated grid of strings. Edit: Saved 6 bytes by stealing @Arnauld's cool mapping. Explanation:

≔⁺Ｓψθ

Append a null byte to the box drawing character string as this gives us a free modulo 13 due to Charcoal's cyclic indexing.

ＷＳＦ²⊞υ⁺⭆ι×⁴λψ⊞υ⭆⌊υψ

Read in the grid, duplicating it vertically and quadruplicating it horizontally, plus suffix a row and column of null bytes of padding.

Ｅυ⭆ＥιＥ⁴§§υ⁻κ÷ν²⁻μ∨⁼ν¹⁼ν²§θ﹪×⁷↨²Ｅλ⁼ν§λ⊕ξ⁹⁵

Map over the expanded grid, collecting a 2×2 square for each character, then comparing the characters in that square pairwise, treating the comparison results as base 2, and hashing into the box-drawing characters.

Previous 129 byte canvas-based version:

≔⁺ＳψθＷＳ⊞υιυＵＥ¹¦¹Ｆ⊕⊗ＬυＦ⊕⊗Ｌη«Ｊ⊖κ⊖ι≔ＫＭζ¿﹪ⅉ²¿﹪ⅈ²§θ﹪×⁷↨²Ｅ⁴⁼§ζ⊗λ§ζ⊗⊕λ⁹⁵Ｆ¬⁼§ζ¹§ζ⁵§θ⁵¿﹪ⅈ²Ｆ¬⁼§ζ³§ζ⁷§θ⁹»ＵＭＫＡ⎇№θιιψＵＥ¹Ｆ⊕ＬυＦ⊗Ｌη«Ｊ⊖⊗κ⊖⊗ι§ＫＶ⊕⊗κ

Try it online! Link is to verbose version of code. Expects the box-drawing characters first in the order ╬╩╣╝╦═╗╠╚║ ╔ followed by the newline-terminated grid of strings. Explanation:

≔⁺Ｓψθ

Append a null byte to the box drawing character string as this gives us a free modulo 13 due to Charcoal's cyclic indexing.

ＷＳ⊞υιυ

Read and print the grid.

ＵＥ¹¦¹

Double-space the grid horizontally and vertically.

Ｆ⊕⊗ＬυＦ⊕⊗Ｌη«Ｊ⊖κ⊖ι

Loop through each cell of the extended grid, including the border.

≔ＫＭζ

Get the contents of the adjacent cells.

¿﹪ⅉ²¿﹪ⅈ²§θ﹪×⁷↨²Ｅ⁴⁼§ζ⊗λ§ζ⊗⊕λ⁹⁵

If this is a cell e.g. between 4 ids then calculate which box-drawing character to draw.

Ｆ¬⁼§ζ¹§ζ⁵§θ⁵

If this is a row divider and the two ids differ then print a .

¿﹪ⅈ²Ｆ¬⁼§ζ³§ζ⁷§θ⁹

If this is a column divider and the ids differ then print a .

»ＵＭＫＡ⎇№θιιψ

Delete all non-box-drawing characters.

ＵＥ¹

Double-space the grid horizontally.

Ｆ⊕ＬυＦ⊗Ｌη«Ｊ⊖⊗κ⊖⊗ι§ＫＶ⊕⊗κ

Duplicate the row dividers.

• Should the length on this be indicated in characters rather than bytes? It seems like the language uses more than 256 characters (though I could be wrong), so given that it would not be possible even define a one-byte-per-char encoding. Nov 2, 2021 at 23:45
• @PaulFisher according to the Code page it seems to use fewer that 256; certainly I couldn't spot any missing characters.
– Neil
Nov 3, 2021 at 0:31
• You’re correct! Thank you; I had not seen that page. Nov 4, 2021 at 1:40

# PowerShell, 282243 219 bytes

Thanks @Arnauld for the idea that the values can be directly mapped to the output characters! I've made a code that finds such an expression by brute force. The *7%95%13 is single shortest expression.

-24 bytes thanks @Arnauld for "width 4w+1 and height 2h+1" before sliding over the input string.

param($t,$s)$n=($s=$s-replace'.',('$0'*4)|%{$_;$_})|% Le*|Sort -b 1
$s=$s+,''|% *ht($n+1)$s|%{-join(0..$n|%{$x=$_,--$_
$b,$a,$d,$c=$s[--$y,++$y]|%{$_[$x]}$t[(!($a-$b)+2*!($a-$c)+4*!($b-$d)+8*!($c-$d))*7%95%13]});$y++} Try it online! Less golfed: param($t,$s)$s=$s-replace'.',('$0'*4)|%{$_;$_}  # magnify to 4*width and 2*heght
$n=$s|% Length|sort -b 1            # find max length
$s=$s+,''|% PadRight($n+1) # add +1 to width and height$s|%{                               # (in addition, we got a rectangle)
-join(0..$n|%{$x=$_,--$_
$b,$a,$d,$c=$s[--$y,++$y]|%{$_[$x]} # I need to golf$i=!($a-$b)+2*!($a-$c)+4*!($b-$d)+8*!($c-$d)  # this 2 lines  cxxx|;:;:;:;:;:;:>

$t[7*$i%95%13]
})
\$y++
}

## Main idea

Let a square of 4 characters slide over the input string "magnified 4 times in width and twice in height".

..
..IILOO
ILLLOO

..
..ILOO
ILLLOO

..
I..LOO
ILLLOO

.~.~.~.~.~.

..IILOO
..LLLOO

..IILOO
..LLLOO

I..ILOO
I..LLOO

II..LOO
IL..LOO

.~.~.~.~.~.

IIILOO
..LLLOO
..

.~.~.~.~.~.

IIILOO
ILLLO..
..

Let's name the points in the square as a,b,c,d.

The expression (a=b)+2*(a=c)+4*(b=d)+8*(c=d) returns numbers in range 0..15, which correspond to the border chars:

0   1   2   3   4   5   6   7   8   9   10  11  12  13  14  15
----------------------------------------------------------------
ab  aa  ab  aa  ab  aa  ab  aa  ab  aa  ab  aa  ab  aa  aa  aa
cd  cd  ad  ad  cb  ca  ab  aa  cc  cc  aa  aa  bb  aa  aa  aa

0   1   2   3   4   5   6   15  8   9   10  15  12  15  15  15  - the expression results

╬   ╦   ╠   ╔   ╣   ╗   ║       ╩   ═   ╚       ╝               - related border chars. the space char for 15
0   7   1   8   2   9   3       4   11  5       6           10  - *7%95%13 - index in the "flat container"

012345678901
╬╠╣║╩╚╝╦╔╗ ═    - the flat container

The script draws the border char for each position of the sliding square. Plus draws middle row between input lines.

The script expectes the flat container with border chars in order ╬╠╣║╩╚╝╦╔╗ ═.

# JavaScript (ES6),  164 154  143 bytes

Expects (a)('╬╩╠╚╣╝║╦═╔ ╗'), where a is a list of strings.

a=>c=>[...a,...a,a[0]].map((r,y)=>[...r+r+r+r+0].map((v,x)=>c[g=d=>v==(v=(a[y-d%2>>1]||0)[x-~x-d>>3]),(g,8*g(1)|g(3)|4*g(2)|2*g)*7%95%13]))

Try it online!

### How?

Given a matrix $$\M\$$ of size $$\w\times h\$$, we build an expanded matrix $$\M'\$$ of width $$\4w+1\$$ and height $$\2h+1\$$ filled with the original values of $$\M\$$ magnified 4 times in width and twice in height. The extra column and extra row are filled with undefined values.

For instance:

AB  ==> AAAABBBB?
CD      AAAABBBB?
CCCCDDDD?
CCCCDDDD?
?????????

For each cell $$\(x,y)\$$ in $$\M'\$$, we extract $$\{M'}_{x,y}\$$ and 3 adjacent cells:

$$\begin{pmatrix}v_3={M'}_{x-1,y-1}&v_1={M'}_{x,y-1}\\ v_2={M'}_{x-1,y}&v_0={M'}_{x,y}\end{pmatrix}$$

Using these results, we build a 4-bit value as follows:

8 4 2 1
| | | |
| | | +--> set if v3 = v1
| | +----> set if v2 = v0
| +------> set if v3 = v2
+--------> set if v1 = v0

We may get all values from $$\0\$$ to $$\15\$$ except the ones with exactly 3 bits set: $$\7\$$, $$\11\$$, $$\13\$$ and $$\14\$$ (because if 3 equalities are satisfied, the 4th one must be satisfied as well).

The 12 remaining values can be directly mapped to the output characters. We use the expression $$\((n\times7)\bmod 95)\bmod 13\$$ to force all them into $$\[0\dots 11]\$$ (Try it online!).

• Hmm, should I save six bytes by stealing that cool mapping?...
– Neil
Nov 4, 2021 at 9:19
• @Neil Sure, no problem :) Nov 4, 2021 at 9:21
• yes, The *7%95%13 is single shortest expression to force the range [0..15]-[7,11,13,14] into [0…11]. thanks. Nov 4, 2021 at 12:20
• @mazzy Although interestingly all of our solutions still have the box drawing characters in a subtly different order.
– Neil
Nov 5, 2021 at 0:37
• Thanks. There is no the cyclic indexing in PowerShell. Nov 5, 2021 at 6:25