Are there More Hard Objects or Soft Objects

19

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

Maltysen

Posted 2016-04-18T20:51:10.347

Reputation: 25 023

2Are the outputs strict, or can any 3 unambiguous outputs be used (such as H/S/E or -1/0/1)? – trichoplax – 2016-04-18T21:04:39.390

@trichoplax they are strict – Maltysen – 2016-04-18T21:05:29.457

3Meta 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 – 2016-04-18T21:09:08.427

@DLosc sure that's fine, adding. – Maltysen – 2016-04-18T21:17:38.253

@LuisMendo no, adding. – Maltysen – 2016-04-18T21:37:43.877

I assume that by "more hard objects" you mean the number (count) of hard objects, not (for example) the area of hard objects... – agtoever – 2016-04-18T21:53:01.127

Perhaps add a couple of actual test cases? – Luis Mendo – 2016-04-18T22:22:02.667

Are all objects guaranteed to be either hard or soft? – user2357112 supports Monica – 2016-04-18T23:24:27.793

@Maltysen I've added two examples in my answer. Feel free to use include them as test cases – Luis Mendo – 2016-04-18T23:38:00.800

Do we have to take the input as a single strings or can we take it as an array of rows? – Dennis – 2016-04-19T07:24:28.257

@user2357112 yes, clarifying. – Maltysen – 2016-04-19T11:52:58.883

@Dennis either is fine: "string, list of string" – Maltysen – 2016-04-19T11:53:39.807

Answers

8

MATL, 105 104 58 50 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

Luis Mendo

Posted 2016-04-18T20:51:10.347

Reputation: 87 464

1So, 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 – 2016-04-19T11:36:09.300

@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 – 2016-04-19T13:30:32.370

... or even just 1 convolution! Thanks a lot! 46 bytes off:-) @Neil – Luis Mendo – 2016-04-19T13:44:40.413

3It was almost on par with JS ... @Neil which side are you on ;-) – edc65 – 2016-04-19T13:57:50.657

6

JavaScript (ES6), 123 121 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")
)

user81655

Posted 2016-04-18T20:51:10.347

Reputation: 10 181

There seems to be a problem with single-line input; ### returns Equal. – Dennis – 2016-04-19T07:25:53.703

@Dennis You're right. It seems that for single line input my previous code relied on the bug I fixed. Fixed now. – user81655 – 2016-04-19T07:33:31.120

IMHO ~g.search(/$/m) is slightly more readable than ~g.search\\n`||-1`. – Neil – 2016-04-19T11:42:23.583

@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 – 2016-04-19T12:30:39.833

You could use just 1 counter n=l=... n>l?...:n<l?...:... – edc65 – 2016-04-19T13:48:02.780

... and of course increment or decrement n – edc65 – 2016-04-19T13:53:35.283

@edc65 Great idea, thanks! – user81655 – 2016-04-19T14:21:29.967

4

Jelly, 50 49 46 43 38 34 33 32 bytes

Ḥ+ḊZ
>⁶ÇÇFµċ7_ċ4$Ṡị“¤Ỵf“¢*ɦ“¡⁺ƒ»

Try it online! or verify all test cases.

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 ' '.
  Ç                          Call the helper link.
                             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.

     µ                       Begin a new, monadic link. Argument: A (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.

Dennis

Posted 2016-04-18T20:51:10.347

Reputation: 196 637

1

Julia, 99 95 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.

Dennis

Posted 2016-04-18T20:51:10.347

Reputation: 196 637

0

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.

t-clausen.dk

Posted 2016-04-18T20:51:10.347

Reputation: 2 874