Acc!!, 152 bytes
N-46
Count i while i-2 {
Count l while (l-3)*i {
Count c while c-14 {
Write 32+1/((c+3)/2%7/_+1)
}
Write 10
}
Count h while h-12 {
Write 33
}
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.
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 i while i-2 {
...
}
Do the following twice:
Count l while (l-3)*i {
Do the following (output the middle part of the battery) three times, but only on the second iteration of the i
loop:
Count c while c-14 {
Do the following (output a character in the middle part of the battery) fourteen times:
Write 32+1/((c+3)/2%7/_+1)
Write either a space or an exclamation point, based on the loop index c
and the accumulator value _
.
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 add 1 (making it 1 vs greater than 1) and then int-divide 1 by that quantity. End result: 1/(a/b+1)
is 1
if a<b
and 0
otherwise.
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.
Count h while h-12 {
Write 33
}
Write 10
Output the top/bottom of the battery: Loop 12 times and write exclamation point; then write a newline.
#
to a space. \$\endgroup\$