# Are there More Hard Objects or Soft Objects

Tangentially inspired by the opening to the What-If book.

The input is a rectangle of spaces as a string, list of string, etc., with objects made of #'s inside:

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

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


The objects will always be non-intersecting, non-touching, rectangles. A soft object is defined as an object that isn't filled up with #'s in the middle and is only a border, a hard object is one that is filled up. An object with width or height <=2 is considered hard. All objects are either hard or soft.

If there are more hard objects in the input, output "Hard", if more soft, output "Soft", if they are equal, output "Equal".

This is , so shortest code in bytes wins!

# Test Cases

These cases aren't full inputs, but rather what each object should be characterized as. The actual input will be like the ascii-art at the top of the question.

## Hard

#

####

##
##

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


## Soft

###
# #
###

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

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


## Actual Test Cases

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

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

Hard

###
###
###

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

Equal

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

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

Soft

• Are the outputs strict, or can any 3 unambiguous outputs be used (such as H/S/E or -1/0/1)? – trichoplax Apr 18 '16 at 21:04
• @trichoplax they are strict – Maltysen Apr 18 '16 at 21:05
• Meta answer on cumbersome I/O formats (not to say you can't do what you choose, but just to give a place for people to express a more fine grained opinion if they wish). – trichoplax Apr 18 '16 at 21:09
• @DLosc sure that's fine, adding. – Maltysen Apr 18 '16 at 21:17
• @LuisMendo no, adding. – Maltysen Apr 18 '16 at 21:37

# MATL, 1051045850 49 bytes

Thanks to @Neil for a suggestion that allowed me to remove 46 bytes!

2\TTYaEq4:HeqgEqZ+K/Zot0>+ss'Soft Hard Equal'Ybw)


Input is a 2D char array, with rows separated by ;. The example in the challenge is

['########          ';'#      #          ';'########          ';'                  ';'   ###        ####';'   ###        ####';'   ###            ']


Here's another example:

['###                ';'###                ';'###                ';'                   ';'###################';'#                 #';'#                 #';'#                 #';'###################']


This corresponds to

###
###
###

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


and thus should give 'Equal'.

As a third example, corresponding to 'Soft',

['   ######    ';'   #    #    ';'   ######    ';'          ###';'   ##  #  # #';'          ###';'             ';'             ';' ########    ';' #      #    ';' ########    ']


that is,

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

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


Try it online!

### Explanation

This uses 2D convolution to detect shapes. The input is converted to a 2D array with 1 indicating # and -1 for space; and is padded with a frame of -1 values. This assures that shapes at the edge of the original field are also detected.

A soft object is detected by the mask

 1   1
1  -1


which corresponds to the object's upper-left corner with one empty interior point. Note that the convolution inverts the mask, so it is defined as [-1 1; 1 1] in the code. The number S of positions in which the convolution equals 4 is the total number of soft objects.

An object (soft or hard) is detected by the mask

-1  -1
-1   1


which correponds to the object's upper-left corner together with some empty exterior points. This mask is the negated version of the previous one, so the previous convolution result can be reused. Specifically, the number T of positions in which that result equals -4 is the total number of objects.

The number H of hard objects is TS. The output string is determined by the sign of SH = 2*ST.

2\                 % Input. Modulo 2: '#' gives 1, ' ' gives 0
TTYa               % Add a frame of zeros
Eq                 % Convert 0 to -1
4:HeqgEq           % Generate mask [-1 1; 1 1], for soft objects
Z+                 % 2D convolution, preserving size
K/Zo               % Divide by 4 and round towards 0. Gives 1 or -1 for 4 or -4
t0>                % Duplicate. 1 for positive entries (soft objects), 0 otherwise
+                  % Add. This corresponds to the factor 2 that multiplies number S
ss                 % Sum of 2D array. Gives 2*S-T
'Soft Hard Equal'  % Push this string
Yb                 % Split by spaces. Gives cell array
w)                 % Swap. Apply (modular) index to select one string

• So, I have no idea what a convolution is, but couldn't you just count all the objects (by finding e.g. the top left corner) and comparing with twice the number of soft objects? – Neil Apr 19 '16 at 11:36
• @Neil that looks very promising, thanks! That way I could reduce from 5 to 2 convolutions. (A convolution is essentially seeing if a specific pattern matches at some position). I'll try it later – Luis Mendo Apr 19 '16 at 13:30
• ... or even just 1 convolution! Thanks a lot! 46 bytes off:-) @Neil – Luis Mendo Apr 19 '16 at 13:44
• It was almost on par with JS ... @Neil which side are you on ;-) – edc65 Apr 19 '16 at 13:57

# JavaScript (ES6), 123121 118 bytes

s=>s.replace(/#+/g,(m,i)=>s[i+l]>" "?0:n+=!m[1]|s[i-l+1]==s[i-l]||-1,n=l=~s.search
|$)|n>l?"Hard":n<l?"Soft":"Equal"  Saved 2 bytes thanks to @edc65! Takes input as a multiline string padded with spaces to form a grid. ## Explanation / Test Very close to MATL length! Basically, it searches for the top line of #s of each object, and if the length of the top line is less than 2 or the first 2 characters below the top line are the same, it is hard, otherwise soft. var solution = s=> s.replace(/#+/g,(m,i)=> // for each run of consecutive # characters s[i+l]>" "? // if the position above the match contains a # 0 // do nothing (this object has already been counted) :n+= // add 1 to the counter (hard) if !m[1] // the match is only 1 # wide |s[i-l+1]==s[i-l] // or the characters below are the same ||-1, // else decrement the counter (soft) n= // n = counter, hard objects increase n, soft decrease l=~s.search\n|$            // l = (negative) line length
)
|n>l?"Hard":n<l?"Soft":"Equal" // return the result string

// Test
document.write("<pre>" + [

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

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

,

###
###
###

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

,

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

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

,

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

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

,

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

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

,

#

,

##

,

#
#

,

###
# #
###

].map((test) => solution(test.slice(2, -2))).join("\n")
)

• There seems to be a problem with single-line input; ### returns Equal. – Dennis Apr 19 '16 at 7:25
• @Dennis You're right. It seems that for single line input my previous code relied on the bug I fixed. Fixed now. – user81655 Apr 19 '16 at 7:33
• IMHO ~g.search(/$/m) is slightly more readable than ~g.search\n||-1. – Neil Apr 19 '16 at 11:42 • @Neil True. There was a bug so I hastily tacked ||-1 on to fix it, but your suggestion made me realise that adding |$ to the regex would save 2 bytes anyway. Thanks! – user81655 Apr 19 '16 at 12:30
• You could use just 1 counter n=l=... n>l?...:n<l?...:... – edc65 Apr 19 '16 at 13:48

# Jelly, 50494643383433 32 bytes

Ḥ+ḊZ
>⁶ÇÇFµċ7_ċ4$Ṡị“¤Ỵf“¢*ɦ“¡⁺ƒ»  ### Background There are 16 different 2×2 patterns of blocks and spaces: | | | | #| | #| #|# | #|# |# |##|# |##|##|##| | | #|# | |##| #|# | |##| #|# | |##| #|# |##|  Of these, since two objects will never touch, | #|# | |# | #|  will never occur in the input, leaving us with 14 possible patterns. Assigning   a value of 0 and # a value of 1, we can encode a 2×2 pattern |ab| |cd|  as 2(2a + c) + (2b + d) = 4a + 2b + 2c + d, leaving the following values for the 14 patterns. | | | | #| | #|# | #|# |##|# |##|##|##| | | #|# | |##| #| |##|# | |##| #|# |##| 0 1 2 2 3 3 4 5 6 6 7 7 8 9  For partial 2×1, 1×2 or 1×1 patterns at the lower and/or right border, we'll treat them as if the were padded with spaces, encoding them as 4a + 2b, 4a + 2c and 4a, respectively. This way, each object (soft or hard) will have exactly one 4 pattern (its lower right corner); each soft object will have exactly two 7 patterns (its lower left and its upper right corner). Thus, subtracting the amount of 4 patterns from the number of 7 patterns encountered in the input will yield (s + h) - 2s = h - s := d, where h and s are the amount of hard and soft objects they form. We print Hard if d > 0, Soft if d < 0 and Equal if d = 0. ### How it works Ḥ+ḊZ Helper link. Input: M (n×m matrix) Ḥ Unhalve; multiply all entries of M by 2. Ḋ Dequeue; remove the first row of M. + Perform vectorized addition. This returns 2 * M[i] + M[i + 1] for each row M[i]. Since the M[n] is unpaired, + will not affect it, as if M[n + 1] were a zero vector. Z Zip; transpose rows with columns. >⁶ÇÇFµċ7_ċ4$Ṡị“¤Ỵf“¢*ɦ“¡⁺ƒ»  Main link. Input: G (character grid)

>⁶                           Compare each character with ' ', yielding 1 for '#'
and 0 for ' '.
This will compute (2a + c) for each pattern, which is
equal to (2b + d) for the pattern to its left.
Ç                         This yields 2(2a + c) + (2b + d) for each pattern.
F                        Flatten; collect all encoded patterns in a flat list.

ċ7                     Count the amount of 7's.
ċ4\$                 Count the amount of 4's.
_                    Subtract the latter from the former.
Ṡ                Yield the sign (1, -1 or 0) of the difference.
“¤Ỵf“¢*ɦ“¡⁺ƒ»  Yield ['Hard', 'Soft', Equal'] by indexing into a
built-in dictionary.
ị               Retrieve the string at the corresponding index.


# Julia, 9995 93 bytes

~=t->2t'+[t[2:end,:];0t[1,:]]'
!x=("Hard","Equal","Soft")[sign(~~(x.>32)∩(4,7)-5.5|>sum)+2]


! expects a two-dimensional Char array as argument. Try it online!

### How it works

This uses almost exactly the same idea as my Jelly answer, with one improvement:

Instead of counting the amount of 4's and 7's, we remove all other numbers, then subtract 5.5 to map (4, 7) to (-1.5, 1.5). This way, the sign of the sum of the resulting differences determines the correct output.

# TSQL, 328 249 bytes

Declaring variables and test data:

DECLARE @l int = 20
DECLARE @ varchar(max)=''
SELECT @+=LEFT(x + replicate(' ', @l), @l)
FROM (values
(' xxxx'),
(' xxxx'),
(' xxxx'),
('x'),
(''),
('xxx'),
('x x  xxx'),
('xxx  x x'),
('     xxx    ')) x(x)


Code:

SELECT substring('Soft EqualHard',sign(sum(iif(substring(@,N,@l+2)like'xx'+replicate('_', @l-2)+'x ',-1,1)))*5+6,5)FROM(SELECT row_number()OVER(ORDER BY Type)N FROM sys.all_objects)x WHERE n<=len(@)AND' x'=substring(@,N-1,2)AND''=substring(@,N-@l,1)


Code deflated:

SELECT
substring('Soft EqualHard',
sign(sum(iif(substring(@,N,@l+2)like'xx'+replicate('_', @l-2)+'x ',-1,1)))*5+6,5)
FROM(SELECT row_number()OVER(ORDER BY Type)N FROM sys.all_objects)x
WHERE n<=len(@)AND' x'=substring(@,N-1,2)AND''=substring(@,N-@l,1)


Explaination:

Script is scanning the text for the pattern:

      space
space x


Each of those is the start of a box

For those positions, the script is then checking for the pattern, don't need to check for first x:

  x
x space


When that exists it is a soft object, otherwise it is a hard object.