Acc!!, 152 148 99 bytes
N-46
Count l while l-5 {
Count c while c-14+2*0^(l%4) {
Write 32+0^((c+3)/2%7/_*(l%4))
}
Write 10
}
Uses exclamation points. Try it online!
Explanation
This was fun to golf. The verbose syntax of Acc!! drove me to combine as many Count while
and Write
statements as possible, which required some interesting expressions to encapsulate the logic. (2024 update: looks like I didn't combine nearly "as many as possible" the first time around.)
N-46
Read a character from stdin and store its codepoint minus 46 in the accumulator. If the input number is L
, the accumulator now holds the value L+2
.
Count l while l-5 {
Do the following (output one line of the battery) five times. (The first and fifth iterations will have l
be 0
and 4
, so to distinguish them from the others, we can check if l%4
is zero.)
Count c while c-14+2*0^(l%4) {
Do the following (output a character in the middle part of the battery) fourteen times; but when l%4
is zero, do it twelve times. (0^x
is a convenient way to turn x=0
into 1
and x>0
into 0
.)
Write 32+0^((c+3)/2%7/_*(l%4))
Write either a space or an exclamation point, based on the loop index c
, the accumulator value _
, and the line number l
.
For the inside of the battery, observe that we want to output some number of exclamation points, then some number of spaces, then three more exclamation points. This looks like a slice into a periodic function. Since we want to add exclamation points in pairs, let's aim for something like this:
0 1
01234567890123456789
!!!!..........!!!!..
where !
means "definitely maps to !
" and .
means "might map to either !
or
depending on the accumulator value."
We want to run our loop from 3 to 16 in this function; since loop variables in Acc!! always start at zero, we'll just add 3 to the loop variable c
.
Next, we combine the pairs of characters that will always be the same by int-dividing by 2:
0123456789
!!.....!!.
We can get the periodic behavior if we take this input mod 7:
0123456
!!.....
Now it's just a matter of adjusting the cutoff depending on the input value. Conceptually, we want exclamation points if this number is less than L+2
, and spaces if it is greater than or equal to L+2
. Conveniently, L+2
is the value in the accumulator. Inconveniently, Acc!! doesn't have comparison operators, so we'll have to abuse some arithmetic:
With integer division, and assuming b
is always positive, a/b
is 0
if a<b
and some positive number otherwise. To turn 0 vs positive into 1 vs 0, we can take 0 to that power. End result: 0^(a/b)
is 1
if a<b
and 0
otherwise.
To handle the first and last lines, which should be entirely exclamation points, we just need to multiply the exponent by l%4
. The product a/b*(l%4)
is 0
if a<b
or if l
is 0
or 4
, and some positive number otherwise. Taking 0 to that power gives the desired 1
or 0
result.
In our case, we have a = (c+3)/2%7
and b = _
(the accumulator). Substituting those into the above expression and adding to 32
gives exclamation point (ASCII 33) or space (ASCII 32) exactly where we want them.
}
Write 10
}
Close the c
loop, write a newline, and close the l
loop.
#
to a space. \$\endgroup\$