g | x | w | all
Bytes Lang Time Link
242Lua250922T002856ZSomeone
154Ruby250901T113529ZNelson
046Jelly250831T134601ZJonathan
129JavaScript ES6250828T202423ZArnauld
179Python 3250828T031933ZTed
097x86_64 machine code250829T114015Zuser1290
6363Uiua250829T112339ZYufangIG
05905AB1E250828T082701ZKevin Cr
046Charcoal250827T235352ZNeil
7793Uiua250827T233446ZjanMakos
294R250827T222504ZM--

Lua, 280 252 249 246 242 bytes

I don't even want to…ugh…what is this…
Essentially, I use binary to index through a string of all 16 possible states (beginning, interior, middle edge, and end in terms of both row and column). However, because these are Unicode characters, I have to manually account for the box characters' 3-character girth. My original solution involved using Lua's native utf8.offset function. However, I ended up implementing it manually with a bit more jankomancy (and then doing a bunch more golfing after that), which is how I was able to slash almost thirty bytes. I'm looking for more trivial optimizations while writing this, but I'm sure there are also ways to spruce up the algorithm itself a bit. I certainly won't be trying it.

Addendum 1 (immediately after writing): found another trivial optimization! Now I've saved exactly 30 bytes!

Addendum 2: saved 2 more bytes by removing a useless addition…now the code is even less intuitive :(

Addendum 3: 2 more bytes from…okay, for some reason SOME numbers compress with SOME letters, but sometimes it just gives a "malformed number" error. I hate you.

Addendum 3.5: I found a bug, so time to retcon all the buggy old solutions and count their bytes.

Addendum 4: io moment

function(n)t=" ║║║═╔╗╦═╚╝╩═╠╣╬"for r=0,2*n+2 do s=""for c=0,4*n+4 do v=(c%4==0 and(c>0 and 6or 0)+(c>n*4 and 1or 4)or 1)+(r%2==0 and(r>0 and 24 or 0)+(r>2*n and 0 or 12)or 0)s=s..t:sub(math.max(v-2,1),v)end print(s)end end

Ruby, 154 Bytes

n=gets.to_i
(2*n+1).times{|i|puts i<1?"╔#{"═══╦"*~-n}═══╗":i>2*n-1?"╚#{"═══╩"*~-n}═══╝":i%2>0?"║#{"   ║"*n}":"╠#{"═══╬"*~-n}═══╣"}

Jelly, 46 bytes

(Or \$44\$, as a monadic Link yielding a list of lists of code-points - remove trailing ỌY)

3“¦ç®£££×øÞ¿ȷÇ‘sḢ,ṪW€ƲjẋṖ¥Zʋ⁺µj⁽ḅP)⁺»Ðo1+⁽"2ỌY

A monadic link that accepts a positive integer and yields a list of characters, or a full-program printing to stdout.

Try it online!

How?

3“...‘sḢ,ṪW€ƲjẋṖ¥Zʋ⁺µj⁽ḅP)⁺»Ðo1+⁽"2ỌY - Link: positive integer, N
 “...‘                                - a list of code-page indices
3     s                               - split into chunks of three
                                        -> [[5,23,8],[2,2,2],[17,29,20],[11,26,14]]
                                            ...9551 less than code-points of:
                                             ╔  ╦ ╗   ║ ║ ║    ╠  ╬  ╣    ╚  ╩  ╝
                   ⁺                  - do this twice:
                  ʋ                   -   last four links as a dyad:
            Ʋ                         -     last four links as a monad:
       Ḣ                              -       remove head and yield it
         Ṫ                            -       remove tail and yield it
        ,                             -       pair these together
          W€                          -       wrap each in a list
                ¥                     -     last two links as a dyad:
              ẋ                       -       repeat (remaining interior part(s)) {N} times
               Ṗ                      -       and remove the last one
             j                        -     join {[[head],[tail]]} with {repeated interior}
                 Z                    -     transpose
                          ⁺           - do this twice:
                    µ    )            -   for each:
                     j⁽ḅP             -     join with -9519 (these would all become spaces)
                            Ðo        - for odd indices (first, third, ...):
                           »  1       -   max with one (so those -9519 will now become ═)
                               +⁽"2   - add 9551
                                   Ọ  - cast from code-points to characters
                                    Y - join with newline characters

Note that for the first, third and fourth triples of initial code-points reduced by 9551 do have a nice pattern:
[L, 10x+y, R] = [5,23,8], [17,29,20], and [11,26,14]
...where x and y are the decimal digits of the central element, conform to \$L = x \times y - 1 = R - 3\$
But it does not help save any bytes, as we need to handle the [2,2,2] row by, for example, using x=y=0 and taking the maximum of the results and 2.

JavaScript (ES6), 129 bytes

f=(n,y=x=0)=>y>2*n?"":`╬╩╠╚╦║╔
╣╝═ ╗`[x>4*n?(x=-1,7):x%4?10+y%2:y%2?5:y/2/n|!x*2|!y*4|x/4/n<<3]+f(n,y+!++x)

Try it online!

Lookup string

The expression y/2/n|!x*2|!y*4|x/4/n<<3 can address 9 entries in the lookup string:

!x*2 (2 if left border) middle horizontal area (0) x/4/n<<3 (8 if right border)
!y*4 (4 if top border) 6 = 4 = 12 =
middle vertical area (0) 2 = 0 = 8 =
y/2/n (1 if bottom border) 3 = 1 = 9 =

The other entries are:

Python 3, 229 179 bytes

def f(n):[print("".join(" ║═╔║║╗╦═╚═╠╝╩╣╬"[sum((n*4!=c,2*(n*2!=r),c and 4,r and 8,c%4and 16,r%2*32))%21]for c in range(n*4+1)))for r in range(n*2+1)]

Try it online!

Explanation

In essence, we iterate through all the rows an columns (of the character grid, not of the box grid), denoted by r and c.

We define some checks for each cell:

sum((n*4!=c,2*(n*2!=r),c and 4,r and 8,c%4and 16,r%2*32))

These are designed to each be 1 bit worth of information, so when summed they don't affect eachother. Then we modulo the whole thing by 21, which gives us the smallest resulting domain of numbers without causing collisions between characters (found by exhaustive search).

Then we lookup the resulting value in the string " ║═╔║║╗╦═╚═╠╝╩╣╬"

Here's the code for finding the lookup string.

x86_64 machine code: 97 bytes

0:  57                      push   rdi
1:  48 b8 66 25 50 25 50    movabs rax,0x2550255025502566
8:  25 50 25
b:  89 f1                   mov    ecx,esi
d:  f3 48 ab                rep stos QWORD PTR es:[rdi],rax
10: b8 57 25 0a 00          mov    eax,0xa2557
15: ab                      stos   DWORD PTR es:[rdi],eax
16: 5a                      pop    rdx
17: 80 2a 12                sub    BYTE PTR [rdx],0x12
1a: 89 f1                   mov    ecx,esi
000000000000001c <writelines>:
1c: 51                      push   rcx
1d: 48 b8 51 25 20 00 20    movabs rax,0x20002000202551
24: 00 20 00
27: 89 f1                   mov    ecx,esi
29: f3 48 ab                rep stos QWORD PTR es:[rdi],rax
2c: b8 51 25 0a 00          mov    eax,0xa2551
31: ab                      stos   DWORD PTR es:[rdi],eax
32: 57                      push   rdi
33: 5a                      pop    rdx
34: 48 b8 6c 25 50 25 50    movabs rax,0x255025502550256c
3b: 25 50 25
3e: 89 f1                   mov    ecx,esi
40: f3 48 ab                rep stos QWORD PTR es:[rdi],rax
43: b8 63 25 0a 00          mov    eax,0xa2563
48: ab                      stos   DWORD PTR es:[rdi],eax
49: 80 2a 0c                sub    BYTE PTR [rdx],0xc
4c: 59                      pop    rcx
4d: e2 cd                   loop   1c <writelines>
4f: 80 2a 06                sub    BYTE PTR [rdx],0x6
0000000000000052 <correct_last>:
52: ff ce                   dec    esi
54: 74 06                   je     5c <end>
56: 80 2c f2 03             sub    BYTE PTR [rdx+rsi*8],0x3
5a: eb f6                   jmp    52 <correct_last>
000000000000005c <end>:
5c: 80 6f fc 06             sub    BYTE PTR [rdi-0x4],0x6
60: c3                      ret

Running on godbolt, but since godbolt doesn't seem to print the characters I've translated them to ascii characters that are kind of similar so there's something to display.

The algorithm is pretty simple, print the first line, then print alternating vertical and horizontal lines N times, correcting characters on the final line afterwards. Main size optimisation is using 1-byte subtracts to adjust the UTF-16 characters to the correct ones, since all the characters used are only distinguished by the low 8 bits.

Version with comments:

#save start position
    push rdi
#generate line - '╦═══'
    movabs rax, 0x2550255025502566
#write it N times
    mov ecx, esi
    rep stosq
#write ending - '╗\n'
    mov eax, 0x000a2557
    stosd
#restore first char
    pop rdx
#change starting char '╦' -> '╔'
    sub byte ptr[rdx], 18
#setup loop counter
    mov ecx, esi
writelines:
#save current loop counter
    push rcx
#generate line - '║   '
    movabs rax, 0x0020002000202551
#write it N times
    mov ecx, esi
    rep stosq
#write ending - '║\n'
    mov eax, 0x000A2551
    stosd
#save start position
    push rdi
    pop rdx
#generate line - '╬═══'
    movabs rax, 0x255025502550256c
#write it N times
    mov ecx, esi
    rep stosq
#write ending - '╣\n'
    mov eax, 0x000A2563
    stosd
#change starting char '╬' -> '╠'
    sub byte ptr[rdx], 12
#restore loop counter and iterate
    pop rcx
    loop writelines
#change starting char '╠' -> '╚'
    sub byte ptr[rdx], 6
#correct chars on last line '╬' -> '╩'
correct_last:
#decrement first so we don't immediately access out of bounds
    dec esi
    je end
    sub byte ptr[rdx + rcx*8], 3
    jmp correct_last
#change last char '╣' -> '╝'
end:
    sub byte ptr[rdi - 4], 6
    ret

Uiua, 63 characters (63 bytes SBCS)

˜⊏˜≡⌞⊏⍜⊢⍜⊢⋅@ +9520⬚@![""" -*9"" '$6"" 30<"]∩⌟/⊂₃⊸˙⊟₃0+×₂⊸⇌⊂0˜↯1

Try it!

05AB1E, 67 62 59 bytes

4*<U₄RX∍2ì3«©4DjRXÌ∍®āžm‡‚I·<∍`®ā56(‡»žm(•dÃи“·”.•29вŽb[+ç‡

Try it online or verify all test cases.

Explanation:

4*         # Multiply the (implicit) input-integer by 4
  <        # Decrease it by 1
   U       # Pop and store this input*4-1 in variable `X`
₄          # Push 1000
 R         # Reverse it: "0001"
  X∍       # Extend/shorten it to length `X`
    2ì3«   # Prepend a 2 and append a 3
        ©  # Store it in variable `®` (without popping)
4          # Push 4
 D         # Duplicate it
  j        # Pad leading spaces to make it that length: "   4"
   R       # Reverse it: "4   "
    XÌ∍    # Extend/shorten it to length `X+2` (aka input*4+1)
®          # Push string `®`
 ā         # Push a list in the range [1,length] (without popping): [1,2,3,...]
  žm       # Push constant 9876543210
    ‡      # Transliterate 1,2,3 to 9,8,7 in string `®`
           # (the other values that aren't present in `®` are no-ops)
‚          # Pair the top two strings together
 I·<∍      # Extend/shorten this list to length input*2-1
     `     # Pop and push all strings separately to the stack
®          # Push string `®` again
 ā         # Push a list in the range [1,length] (without popping) again
  56(      # Push -56
     ‡     # Transliterate 1,2,3 to "-",5,6 in string `®`
           # (the other integers that aren't present in `®` are no-ops again)
»          # Join all strings on the stack with newline delimiter
žm         # Push constant 9876543210
  (        # Negate it: -9876543210
•dÃи“·”.•  # Push compressed integer 10932220548783273
  29в      # Convert it to base-29 as list: [25,28,16,19,13,10,1,7,4,22,0]
     Žb[   # Push compressed integer 9552
        +  # Add it to each value in the list:
           #  [9577,9580,9568,9571,9565,9562,9553,9559,9556,9574,9552]
         ç # Convert each to a character with that unicode value:
           #  ["╩","╬","╠","╣","╝","╚","║","╗","╔","╦","═"]
‡          # Transliterate the "-","9","8",... to "╩","╬","╠",...
           # (after which the result is output implicitly)

See this 05AB1E tip of mine (sections How to compress large integers? and How to compress integer lists?) to understand why •dÃи“·”.• is 10932220548783273; •dÃи“·”.•29в is [25,28,16,19,13,10,1,7,4,22,0]; and Žb[ is 9552.

Charcoal, 46 bytes

NθUB⁺⁺╬×═³¶║P…⁺╦×ψ³⊕⊗θP↓…⁺╠ψ⊕θ╔‖B⌈

Attempt This Online! Link is to verbose version of code. Explanation:

Nθ

Input n.

UB⁺⁺╬×═³¶║

Set the background to a 2×4 section of the result. This handles all of the horizontal and vertical segments and the interior joins correctly, but the edges are still wrong.

P…⁺╦×ψ³⊕⊗θ

Output the top edges, but the top left corner is still wrong.

P↓…⁺╠ψ⊕θ

Output the left edges, but the top left corner is still wrong.

Output the top left corner.

‖B⌈

The previous code only drew the top left quarter of the result; this reflects it twice to complete the output.

Note that each of the box-drawing characters takes 3 bytes to represent in Charcoal.

Uiua, 77 characters (93 bytes SBCS)

≡⌞▽1_3⊂˜⊂/⊂₃¤⊃($"╠_╣"/⊂₃@╬|↯⟜(/⊂₃@ ˜↯@║+₁)⧻|⊓$"╔_╗"$"╚_╝"∩⌟/⊂₃@╦@╩)˜↯@═

Takes the integer as input, and outputs a matrix of characters.

Try it in the pad!

R, 294 bytes

p=paste;r=rep;q=\(...)cat(...,sep="");f=\(n)for(i in 0:(2*n))"if"(i%%2==0,"if"(i==0,q("╔",p(r("═══",n),collapse="╦"),"╗\n"),"if"(i==2*n,q("╚",p(r("═══",n),collapse="╩"),"╝\n"),q("╠",p(r("═══",n),collapse="╬"),"╣\n"))),q(p(r("║   ",n),collapse=""),"║\n"))

Attempt This Online!