# Optimize ASCII test-tubes

You are given a bunch of ASCII test-tubes, your task is to reduce number of test-tubes used.

Each test tube looks like this:

|  |
|  |
|  |
|~~|
|  |
|  |
|  |
|  |
|__|


Obviously, ~~ is the water level. Test-tube can be also empty, in which case there are no ~~ characters inside. Single tube can contain up to 8 water level units.

You are given finite number of test tubes with different water levels inside. You have to pour the water in least amount of test-tubes possible, and output the result.

|  | |  | |  | |  |         |~~| |  |
|  | |  | |  | |  |         |  | |  |
|  | |~~| |  | |  |         |  | |  |
|~~| |  | |  | |  |         |  | |~~|
|  | |  | |  | |  | ------> |  | |  |
|  | |  | |  | |  |         |  | |  |
|  | |  | |~~| |  |         |  | |  |
|  | |  | |  | |  |         |  | |  |
|__| |__| |__| |__|         |__| |__|

05 + 06 + 02 + 00  ------>  08 + 05


As you can see, test-tubes are separated with single space. Empty tubes should not be shown in output. This is code golf, so code with least number of bytes wins.

Test cases: http://pastebin.com/BC0C0uii

Happy golfing!

• Can we also redistribute the water? E.g. would 7+6 be a valid output for your example? – Martin Ender Dec 10 '16 at 11:10
• @MartinEnder You should use least amount of tubes possible. I think that's acceptable in this case. – Jacajack Dec 10 '16 at 11:12
• @StewieGriffin I haven't seen anything similar here yet, so If that's a kinda duplicate I'm sorry – Jacajack Dec 10 '16 at 11:13
• Is trailing whitespace allowed? – PurkkaKoodari Dec 10 '16 at 11:36
• Better title - "Optimizer ASCII test tube babies" – Optimizer Dec 10 '16 at 11:38

# Pyth, 4845 44 bytes

jCssm+_B,*9\|_X+\_*8;ld\~*9;cUs*L/%2w\~_S8 8


Try it online.

Prints a single trailing space on every line.

## JavaScript (ES6), 159 148 bytes

s=>s.replace(/~~|\n/g,c=>1/c?i++:n+=7-i,n=i=-1)&&012345678.replace(/./g,i=>|${g(+i)}| .repeat(n>>3)+|${g(~n&7^i)}|
,g=i=>i?i>7?__:  :~~)


Outputs a trailing linefeed. Edit: Saved 11 bytes with some help from @Arnauld.

• s.replace(/~~/g,(_,i)=>n+=9-i/s.indexOf\n|0,n=0) should save 4 bytes. You may want to initialize n to -1 instead and use n>>3 and ~n&7^i to save one more byte. – Arnauld Dec 11 '16 at 22:37
• @Arnauld Thanks for the -1 idea but I was able to improve on the replace idea. – Neil Dec 12 '16 at 0:02
• Nice! I never realized 1/"\n" was truthy. – Arnauld Dec 12 '16 at 0:14
• @Arnauld Well, it was only an extra byte of icing on the cake... – Neil Dec 12 '16 at 1:20

## Perl, 150 bytes

149 bytes of code + -n flag.

$l+=9-$.for/~~/g}if($l){$%=($v=$l/8)+($r=$l!=8);say"|~~| "x$v.($@="|  | ")x$r;say$:=$@x$%for$l%8..6;say$@x$v."|~~|"x$r;say$:for 2..$l%8;say"|__| "x$%  I won't explain all the code, just a few things: $l+=9-$.for/~~/g counts how much water the is in the input. The second part of the code prints the output. The idea is to put as many fully filled tubes as possible, and a last one which contains the water that is left (if any). So the algorithm is in 4 parts: prints the first line of water (the top of tubes): say"|~~| "x$v.($@="| | ")x$r. Then, print empty parts of tubes until we reach the level of water of the last tube: say$:=$@x$%for$l%8..6. Then print the level where the last tube water is: say$@x$v."|~~|"x$r. Then, print all the remaining "empty" levels: say$:for 2..$l%8;. And finally, print the bottom line: say"|__| "x$%.
The variable names make it hard to read ($%, $@, $:) but allows for keywords like x and for to be written after the variable without a space. To run it: perl -nE '$l+=9-$.for/~~/g}if($l){$%=($v=$l/8)+($r=$l!=8);say"|~~| "x$v.($@="| | ")x$r;say$:=$@x$%for$l%8..6;say$@x$v."|~~|"x$r;say$:for 2..$l%8;say"|__| "x$%' <<< "|  | |  | |  | |  |
|  | |  | |  | |  |
|  | |~~| |  | |  |
|~~| |  | |  | |  |
|  | |  | |  | |  |
|  | |  | |  | |  |
|  | |  | |~~| |  |
|  | |  | |  | |  |
|__| |__| |__| |__| "


I'm not very satisfied with how long this answer is. I tried to make the best out of my algorithm, but a different approach could probably be shorter. I'll try to work on it soon.

• @JamesHolderness I tried all of the test cases (and re-tried now because you had me doubting) and it seems fine to me. "The last one" is the one with 3 tubes: 2 with water level at 4, and 1 with water at level 2, right? If so, then I tried it and it gives the same output as the one on pastbin – Dada Dec 11 '16 at 13:34
• @JamesHolderness Oh right, it explains a lot! Thanks :) – Dada Dec 11 '16 at 15:19

# Befunge, 144 138 bytes

9>1-00p>~$~2/2%00gv |:g00_^#%4~$~$~+*<$< v01!-g01+*8!!\*!\g00::-1</8+7:<p01-1<9p00+1%8-1:_@#:
_ ~>g!-1+3g:"|",,," |",,:>#^_$55+,10g:#^_@  Try it online! The first two lines process the input, basically ignoring everything except the first character in each tube that might be a level marker. We take the ASCII value of that character, divide by 2 and mod 2 (giving us 1 or 0 depending on whether we're on a level marker or not), multiply that by the row number (counting down from 8, thus giving us the level value for that tube), and add it to a running total. The output is handled on the second two lines, essentially starting on the far right of the third line. We first calculate the number of tubes by taking the total water level plus 7 divided by 8. Then when iterating over the rows of all the tubes, we calculate the character to display inside a specific tube (t, counting down to 0) for a given row (r, counting down from 8 to 0) as follows: last_level = (total_water - 1)%8 + 1 level = last_level*!t + 8*!!t char_type = !(level - r) - !r  The calculated char_type is -1 for the bottommost row (the base of the tube), 0 for a any other area that isn't a water level, and 1 for the water level. It can thus be used as simple table lookup for the appropriate character to output (you can see this table at the start of line 4). ## Haskell, 186 bytes import Data.Lists z=[8,7..0] f x|s<-sum[i*length j-i|(i,j)<-zip z$splitOn"~~"<$>lines x],s>0=unlines$(\i->(#i)=<<(min 8<$>[s,s-8..1]))<$>z|1<2=""
l#i|i==l="|~~| "|i<1="|__| "|1<2="|  | "


Usage example:

*Main> putStr $f "| | | | | | | |\n| | | | | | | |\n| | |~~| | | | |\n|~~| | | | | | |\n| | | | | | | |\n| | | | | | | |\n| | | | |~~| | |\n| | | | | | | |\n|__| |__| |__| |__|" |~~| | | | | | | | | | | | | |~~| | | | | | | | | | | | | | | | | |__| |__|  Puts a trailing space on every line. How it works:  lines x -- split the input string at newlines splitOn"~~"<$>       -- split every line on "~~"
zip z                  -- pair every line with its water level, i.e.
-- first line = 8, 2nd = 7 etc.
[i*length j-i|(i,j)   ] -- for each such pair take the number of "~~" found
-- times the level
s<-sum                    -- and let s be the sum, i.e. the total amount of water

s>0                      -- if there's any water at all

[s,s-8..1]       -- make a list water levels starting with s
-- down to 1 in steps of 8
min 8<$> -- and set each level to 8 if its greater than 8 -- now we have the list of water levels for the output \i->(#i)=<<( )<$>z      -- for each number i from 8,7..0 map (#i) to the
-- list of output water levels and join the results
unlines                    -- join output lines into a single string (with newlines)

l#i                        -- pick a piece of tube:
--  |__|  if l==0
--  |~~|  if l==i
--  |  |  else

|1<2=""                  -- if there's no water in the input, return the
-- empty string


The main pain was the lack of a function that counts how often a substring occurs in a string. There's count in Data.Text, but importing it leads to a bunch of name conflicts that are way too expensive to resolve.

# Python, 261 bytes

i=input().split('\n')
t=0
R=range(9)[::-1]
for n in R:t+=i[n].count('~')/2*(8-n)
m=t%8
e=t/8
o=t/8+1
T='|~~| '
b='|  | '
B='|__| '
n='\n'
if m:
print T*e+b
for n in R:
if n==m:print b*e+T
else:print b*o
print B*o
elif t<1:1
else:print T*e+(n+b*e)*7+(n+B)*e


I feel like there is something I am missing. Also, if a bunch of newlines are accptable for the blank output, I could lose some bytes. Takes input like '| | | | | |\n| | | | | |\n| | | | | |\n| | | | | |\n| | | | | |\n| | | | | |\n| | | | | |\n| | | | | |\n|__| |__| |__|'.

# Ruby, 139 bytes

(138 bytes of code plus one byte for the -n)

n||=0;b=gsub(/~~/){n=n+9-$.}[0,5];END{8.times{|i|puts (b*(n/8)).tr(?_,i>0??\ :?~)+(n%8>0?b.tr(?_,(8-i==n%8)??~:?\ ):"")};puts b*((n+7)/8)}  Try it online! A few explanations: This program requires the -n switch. n – water counter. b – Template for constructing tubes; equals "|__| " i – Current line index during tube construction. gsub(/~~/){} – This abuses gsub to simply count the water level. gsub actually expands to Kernel.gsub, which is equivalent to $_.gsub!. This unnecessarily manipulates the current line ($_); however, it allows for a more concise assignment of b=[0,5] instead of b=$_[0,5].

n=n+9-$. – To measure the water level, the expression uses the pre-defined variable $., which carries the current input line number. This allowed me to lose the explicit loop variable.

b=gsub(/~~/){}[0,5] – caches the bottom of the leftmost tube as a template. (Feels a little bit like the “Elephant in Cairo” pattern to me because the bottom line wins.)
Since the bottom of the tube never shows water, the gsub won’t replace anything when we’re there; therefore in the end, b always equals "|__| ".

END{} – Gets called after the whole input stream has been processed. I use this phase to construct the target tubes.

i>0??\ :?~ – is simply short-hand for i > 0 ? " " : "~".

Update 1: Added details on variables, the gsub trickery and the END{} phase.

Update 2: (±0 bytes overall)

• Use n||=0 instead of n=n||0 (-1 byte)
• Took malus for -n (+1 byte)

# Python 3, 404 bytes

This program creates the full intended output with the water levels both in ASCII and number formats.

w,x,y=[],[],[];a,b,s=" ------> ","~","";y=input().split("\n")
for i in [i for i in zip(*y) if "_" in i][::2]:w+=[8-i.index(b)] if b in i else [0]
u=sum(w)
while u:x+=[[8],[u]][u<8];u-=x[-1]
for i,k in enumerate(y):
s+=k+"%s"%[a," "*9][i!=4]
for j,l in enumerate(x):
c=["  ","__"][i==8];s+="|%s| "%(c,b*2)[l==8-i]
s+="\n"
s+="\n"
for i in w:s+=" %02d  "%i
s+="\b"+a
for i in x:s+=" %02d  "%i
print(s)