There's no such thing as a “half empty glass”

You probably know the rhetorical question of whether a glass is half full or half empty. I'm getting a little tired of the phrase, so I decided that it's time to eliminate this confusion about glass fullness or emptiness programmatically.

Your task is to write a program that takes an ASCII art representation of an ugly glass and outputs an ASCII art of a corresponding nice glass. It also has to decide whether the glass is full, mostly full, mostly empty or empty and output this as well (any 4 constant, distinct output values do).

TL;DR

Input is an ASCII art of a glass (# characters) and liquid (a-z) distributed randomly inside and outside of the glass. Liquid within the glass falls down and accumulates at its bottom, liquid outside of it gets discarded. Output an ASCII art of the glass after the liquid has settled at the bottom. Determine how full the glass is and output that as well.

Ugly and nice glasses

A glass in general is a container made out of # characters with a bottom, two side walls and no top.

• Valid glasses do not have holes in them. (All of the # characters have to be connected.)
• There will either be at least two # characters in each line of the input ASCII art, or none. There won't be a line with exactly one #.
• The top line of the input ASCII art will always have exactly two #.
• Valid glasses have exactly one local minimum in their delimiting wall of # characters. This means that liquid can't get trapped somewhere.
• The delimiting wall of a glass will not have local maxima.
• There won't be any # below the bottom of the glass.
• The interior of the glass will always be a connected space.
• There may be leading/trailing whitespace and newlines in the input.

Examples of valid and invalid glasses:

VALID (possible input to your program):

#  #
#  #
####

#        #
#      #
#    #
#    #
#    #
#  #
##

#      #
#      #
###   #
#  #
####

#       #
#      #
#     #
#      #
#       #
########

#   #
#   #
#   ###
#   ###
#   ###
#####

INVALID (you won't get one of those as input to your program):

#  #
#  Has a hole.
####

#      #
#  #  This is also considered a hole.
##

#   #
# #  Less than two # on a line.
#

## #
# #  More than two # on the first line.
###

#
# #  Less than two # on the first line.
###

#               #
#     #       #  More than one local minimum.
#   # #     #   Liquid might get trapped.
###   #   #
###

#  #
#  #
####  Interior is not a connected space.
#  #
#  #
####

#   #
#   #######
#   ###   #
#   ##   #  Has a local maximum.
#   #   #
#      #
#     #
######

#    #
#    #
#     #
#####
#  #    <--- # below the bottom of the glass.

#     #
#  #  #  This is also a glass with a hole. The #'s aren't all connected.
#  #  #
#     #
#######


An ugly glass is a glass with liquid just floating around in its interior.

• Liquid is represented by the lowercase letters a-z.
• There will be no liquid above the first line of # characters. This means that it's not required to allow for liquid to fall into the glass.
• There may be liquid outside of the glass. This liquid will get discarded when converting the ugly glass into a nice glass.

Examples of ugly glasses:

        # y    b #      i
x   v#p  q   l#   l
a     # a   zj # p   g
g  #ppcg   c#
u    #  r   n #   r
##########

<-- There will never be liquid above the glass
#  tz  g#
#y abc # d
av z#ox s #  l
c#y abth# b
#vg y rm#   a
########
e   a  b c  d     <-- Discard this as well (not within interior)


A nice glass is a glass where all liquid has accumulated at the bottom.

• From the bottom up, the interior of a nice glass consists of a number of lines that are completely filled with letters, followed by at most one line that's not completely filled with letters, and then a number of lines that are empty.
• There may not be any liquid outside of the interior of a nice glass.

Conversion of an ugly glass into a nice glass

• The liquid inside the glass falls down and accumulates at the bottom.
• Liquid outside of the glass gets discarded.
• When converting an ugly glass into a nice glass, the exact letters in it have to be preserved. For example, if the ugly glass has three a's in it, the nice glass has to have three a's as well. (Soda doesn't suddenly turn into water.)
• The letters within the nice glass do not have to be ordered.
• The shape of the glass has to be preserved. No # characters may be added or removed.
• Any amount of leading/trailing whitespace and newlines is allowed.

Determining glass fullness

• A glass is full if its entire interior space is filled with letters.
• It is mostly full if 50% or more of the interior space is filled.
• It's mostly empty if less than 50% of the interior space is filled.
• It's empty if there are no letters in the glass.
• There may be any number of additional newlines and spaces between the ASCII art glass and the fullness output.
• The program may output any distinct (but constant!) values for the 4 levels of glass fullness, it doesn't have to print the exact strings above. Please specify which value represents which fullness level.

I/O examples

Example 1 input:

# y    b #      i
x   v#p  q   l#   l
a     # a   zj # p   g
g  #ppcg   c#
u    #  r   n #   r
##########

Example 1 output:

#        #
#        #
#        #
#ppcglqb #
#yprazjnc#
##########
mostly empty

Example 2 input:

#  tz  g#
#y abc # d
av z#ox s #  l
c#y abth# b
#vg y rm#   a
########
e   a  b c  d

Example 2 output:

#       #
#   bc #
#oxysa#
#ygabth#
#vgtyzrm#
########
mostly full

Example 3 input:

#      #
#  g   # f
###ih #  d
a c #  # e
b  ####

Example 3 output:

#      #
#      #
###  g#
#hi#
####
mostly empty

Example 4 input:

#ab#
#cd#
####

Example 4 output:

#cb#
#da#
####
full

Example 5 input:

#        # h
#      #
a #    # g
b#    #  f
c  #    #
#  #  e
d  ##

Example 5 output:

#        #
#      #
#    #
#    #
#    #
#  #
##
empty

Example 6 input:

# b  az#
#y s ###
###### t
l  u

Example 6 output:

#  z   #
#ybsa###
######
mostly full

Example 7 input:

#   # g
# b #f
#  c###
#da ### i
#  e###
##### h

Example 7 output:

#   #
#   #
#   ###
#de ###
#abc###
#####
mostly empty


Misc

• This is code golf so the shortest answer wins.
• If possible, please provide a link to an online interpreter that can be used to run your program on the provided example inputs, for example tio.run
• Are these valid cups? paste.ubuntu.com/26097168 – l4m2 Dec 2 '17 at 18:14
• May i suggest: "A glass is mostly full if more than 50% of the interior space is filled." - If you then consider exactly 50% as invalid input (without requiring the solutions to handle this case) theres really no such thing as a "half empty glass" (or a "half full glass") any more, matching the title even better. Without invalidating any solutions that actually handle this case. – Anedar Dec 2 '17 at 21:46
• @l4m2 Updated the challenge and restricted the input even further. First one of your examples is invalid, second one is valid, third one invalid. – Jonathan S. Dec 3 '17 at 0:16
• @Anedar While it might make the challenge match the title better, this would take away too much from the challenge in my opinion and it already has enough invalid inputs anyway. I'll leave the 50% case in there. – Jonathan S. Dec 3 '17 at 0:18

Retina, 56 bytes

T% l!^.*?#|[^#]+$O |\w *! T#!¶ *M \w + \w +  Try it online! The output encoding is 0\n0 for full, 0\n1 for empty, 1\n0 for mostly full and 1\n1 for mostly empty (in other words, the first bit indicates "mostly" and the second bit indicates "empty"). Explanation T% l!^.*?#|[^#]+$


We start by turning all spaces and letters outside the glass into !. This is done by matching either a line-beginning up to the first # or by matching a line-ending that doesn't contain a # and transliterating all spaces and letters in those matches.

O |\w


Sort all spaces and letters. Since letters have higher code points than spaces, this sorts all the letters to the end, which means to the bottom of the glass. This also happens to sort the letters among themselves, but the order of the letters in the result is irrelevant.

*!



Dry run: print the result of replacing all ! with spaces, but don't actually apply this change to the working string. This prints the nice glass.

T#!¶


Discard all #, ! and linefeeds, so that we're only left with the spaces and letters inside the glass (still sorted).

*M \w


Dry run: print the number of matches of a space followed by a letter. This will find at most one match, and that only if the there were both spaces and letters inside the glass, i.e. the glass is mostly (full/empty).

+ \w



Repeatedly remove a space followed by a letter. This "cancels" letters and spaces, so that we end up with only that type of character which appears more often inside the glass.

 +


Count the number of matches of this regex, which gives 1 if there are any spaces left (i.e. the glass was [mostly] empty) and 0 if there are non left (i.e. the glass was at exactly 50% or more and therefore [mostly] full).

C, 190 bytes

Thanks to @l4m2 for saving 17 bytes!

i,k,t,s;f(char*g){char*p=g,l[strlen(g)];for(s=t=0;*p;*p>35&&(t?l[i++]=*p:1)?*p=32:0,~*p++&t&&++s)t^=*p==35;for(k=i;i;t&*p==32?*p=l[--i]:0)t^=*--p==35;printf("%s\n%d",g,k?k-s?k*2<s?1:2:3:0);}


Outputs 0 for empty glass, 1 for mostly empty, 2 for mostly full, and 3 for full.

First loops through the input string counting the space inside the glass, marking down letters that are inside the glass, and changing all letters to spaces. Then loops through the string backwards placing all the letters that were in the glass at the bottom of the glass.

Try it online!

Unrolled:

i,k,t,s;
f(char*g)
{
char l[strlen(g)], *p=g;
for (s=t=0; *p; *p>35&&(t?l[i++]=*p:1)?*p=32:0, ~*p++&t&&++s)
t ^= *p==35;
for (k=i; i; t&*p==32?*p=l[--i]:0)
t ^= *--p==35;
printf("%s\n%d", g, k?k-s?k*2<s?1:2:3:0);
}

• global variables are initally 0, so no need to reinit – l4m2 Dec 2 '17 at 18:16
• @l4m2 Thanks, but functions need to be reusable, so I need to initialize the variables inside the function. Except i it seems, as the function always leaves its value at 0 in the end. – Steadybox Dec 2 '17 at 18:27
• ·char*malloc(strlen(g))· can be char l[strlen(g)] if C99 allowed, as it's shorter and don't make memory leak – l4m2 Dec 2 '17 at 20:19
• t = *p-35 ? t : !t -> t ^= *p==35 if t is always 0 or 1 – l4m2 Dec 2 '17 at 20:21
• &&(*p=32) -> ?*p=32:0 char l[strlen(g)],*p=g -> char*p=g,l[strlen(g)] – l4m2 Dec 2 '17 at 23:12

Python 2, 342 bytes

import re
def f(g):
g=[l for l in g if'#'in l];s,w,l,W=zip(*[re.findall(r'([^#]*)(#+)'*2,l)[0] for l in g[:-1]]);a=sorted(''.join(l));R=len(a);r=a.count(' ');L=[]
for x in l:L+=[''.join(a[:len(x)])];a=a[len(x):]
for l in zip([' '*len(x)for x in s],w,L,W)+[re.sub('[^#]',' ',g[-1]),'mostly '*(0<r<R)+['full','empty'][r>R/2]]:print''.join(l)


Try it online!

Perl 5, 197 bytes

map{/#([^#]+)#/;$l.=$1;y/#/ /c}@a=grep/#/,<>;$f=length$l;$_=$l=~y/ //d/$f;$a[--$i]=~s/#( +)#/'#'.(substr$l,0,($q=length$1),"").$"x($q-$p).'#'/e while$p=length$l;say for@a;say'm'x($_!=int),\$_>.5?e:f


Try it online!

Outputs:

 e  empty
me  mostly empty
mf  mostly full
f  full