24
\$\begingroup\$

Let's represent a standard masonry brick as [__] (and ignore the fact that the top is open). When these bricks are stacked every other layer is offset by half a brick, as is usual in brick construction:

  [__][__][__][__]
[__][__][__][__]  
  [__][__][__][__]
[__][__][__][__]  

Thus each brick has at most six neighbors and it's impossible for two bricks to directly line up vertically.

The key point is that arrangements of these bricks are not mortared, but merely held together by gravity. So it is important that each brick in the structure is stable, otherwise the entire structure is unstable.

There are three ways an individual brick may be stable:

  1. Any brick on the ground (the lowest line of bricks) is stable.
  2. Any brick that has two bricks directly below it is stable:

      [__]   <- this brick is stable
    [__][__] <- because these bricks hold it up
    
  3. Any brick that has a brick both above and below it on the same side is stable:

      [__]  [__]
    [__]      [__] <- these middle bricks are stable
      [__]  [__]      because the upper and lower bricks clamp them in
    
    [__]          [__]
      [__]      [__]   <- these middle bricks are NOT stable
        [__]  [__]
    

From these rules we can see, for example, the arrangement

  [__][__][__][__]
[__][__][__][__]  
  [__][__][__][__]
[__][__][__][__]  

is unstable because the top right brick is unstable, which is all it takes.

A brick structure is only stable if all of its bricks are stable.

Challenge

Your task is to write a function that takes in a brick structure string and returns a truthy value if the structure is stable, and a falsy value if unstable. (truthy/falsy definition)

The input string may be arbitrarily large but it will always be a rectangular grid of characters, with spaces filling areas void of bricks. The character grid width will be divisible by 4 but the height may be odd or even.

The brick grid always extends above and to the right of the lower left brick position:

         .
         .
         .
  BRK?BRK?BRK?BRK?  
BRK?BRK?BRK?BRK?BRK?
  BRK?BRK?BRK?BRK?  
BRK?BRK?BRK?BRK?BRK? . . .
  BRK?BRK?BRK?BRK?  
BRK?BRK?BRK?BRK?BRK?

Depending on the structure, each BRK? either represents a brick ([__]) or empty space (4 spaces).

Notice that the half-brick cavities are filled with spaces to ensure that the character grid is rectangular.

Scoring

The shortest code in bytes wins.

Notes

  • If desired you may use . instead of space as the empty space character.
  • The empty string is considered stable.
  • If your language doesn't have functions you may use a named string variable as input and assign the result to another variable.
  • If your language doesn't have strings you may do whatever seems appropriate for input.

Test Cases

Various test cases, separated by empty lines. For clarity . is used instead of space for empty spaces.

Stable:

[__]

..[__]..
[__][__]

........[__]........
......[__][__]......
........[__]........

..[__][__]..
[__][__][__]
..[__][__]..
[__]....[__]

............[__]..
..[__][__][__][__]
[__][__][__][__]..
..[__][__][__][__]
[__][__][__][__]..

..[__]........[__]..
[__][__][__][__][__]
..[__][__][__][__]..
....[__][__][__]....
......[__][__]......
........[__]........

Unstable:

..[__]..
........

..[__]..
[__]....

..[__]..
....[__]

..[__][__]..
[__]....[__]
..[__][__]..
[__]....[__]

..[__][__][__][__]
[__][__][__][__]..
..[__][__][__][__]
[__][__][__][__]..

[__][__][__][__][__]
..[__][__][__][__]..
....[__][__][__]....
......[__][__]......
........[__]........
\$\endgroup\$
  • 7
    \$\begingroup\$ I'm pretty sure your definition of stability doesn't match the reality ;-) \$\endgroup\$ – John Dvorak Oct 5 '14 at 3:59
  • 14
    \$\begingroup\$ @JanDvorak I know but who would want to golf a whole physics engine :P \$\endgroup\$ – Calvin's Hobbies Oct 5 '14 at 4:05
  • \$\begingroup\$ ........[__].... ......[__][__].. ....[__][__].... ..[__][__]...... [__][__]........ ..[__].......... (you'll have to mentally stack those lines on top of each other. The point being that your rules allow structures whose centre of gravity is far offset from their point of contact with the ground. It should be possible to tighten them to avoid this, without needing a physics engine, if you felt like it.) \$\endgroup\$ – Nathaniel Oct 5 '14 at 8:55
  • 2
    \$\begingroup\$ Verisimilitude in the physics is a huge can of worms, however. One can come up with many simple cases where the stability depends on coefficient of friction and/or on the weight of the bricks on top. \$\endgroup\$ – COTO Oct 5 '14 at 14:50
  • 10
    \$\begingroup\$ "stable"…heh \$\endgroup\$ – wchargin Oct 5 '14 at 19:18
12
\$\begingroup\$

80386 machine code, 98

The code:

60 8b f1 8b f9 b0 0a f2 ae 8b ef 2b ee b0 00 f2
ae 2b fe 83 ef 02 2b fd 72 41 03 f7 2b f5 33 c9
8a 7c 6e fc 8a 1c 6e b1 02 33 d2 8b c7 f7 f5 83
fa 02 75 03 b7 00 41 8a 66 fc 8a 06 3b fd 7d 02
33 c0 23 c3 0a c4 22 df 0b c3 f6 44 2e fe 01 74
04 d1 e8 73 06 2b f1 2b f9 73 c5 61 d1 d0 83 e0
01 c3

The code scans the ASCII art from the end to the beginning, jumping 2 characters at a time. This does twice the needed checks (it would be enough to jump 4 characters), but simplifies the logic.

Checking starts at the next-to-last row of characters (no need to check the last line). At each line, it starts 3 characters from the right (no need to check too far to the right). For each character, it checks 4 surrounding characters:

A...B
..X..
C...D

There is a bunch of logical conditions to check:

  • If A and C are brick characters, X is supported
  • If B and D are brick characters, X is supported
  • If C and D are brick characters, X is supported
  • If X is a brick character, it has to be supported; otherwise the structure is unstable

It's a lucky coincidence that all brick characters [_] have their LSB set; all other characters .\n have it clear. In addition, the 80386 instruction set has these handy "high" and "low" registers (ah, al, etc), which help parallelize the checks a bit. So all the checking amounts to some obscure bit fiddling.

I started from the following C code:

int check(const char* ptr)
{
    int width, result = 0, pos;

    width = strchr(ptr, '\n') - ptr + 1;
    pos = strlen(ptr) - 1 - width; // pos points to the B character
    ptr += pos - width;

    while (pos >= 0)
    {
        int a = ptr[-4];
        int c = ptr[-4 + 2 * width];
        int b = ptr[0];
        int d = ptr[0 + 2 * width];
        int ab = a << 8 | b;
        int cd = c << 8 | d;
        if (pos < width)
            ab = 0; // A and B don't exist; set them to 0
        int jump = 2; // distance to next brick
        if (pos % width == 2) // leftmost brick?
        {
            cd &= 0xff; // C doesn't exist; set it to 0
            ++jump;
        }
        int support_v = ab & cd;
        support_v = support_v | support_v >> 8; // data in LSB
        int support_h = cd & cd >> 8; // data in LSB
        int support = (support_v | support_h) & 1;
        if (!support & ptr[-2 + width])
            goto UNSTABLE;
        ptr -= jump;
        pos -= jump;
    }
    return 1;
UNSTABLE:
    return 0;
}

I translated the code to assembly language (it's mostly one-to-one), including a golfed implementation of strchr and strlen. The following source code is translated by MS Visual Studio to the machine code at the top of my post.

__declspec(naked) int __fastcall check(const char* ptr) // MS Visual Studio syntax
{
    _asm
    {
        pushad;

        // ecx = ptr
        mov esi, ecx; // esi = ptr
        mov edi, ecx
        mov al, 10;
        repne scasb;
        mov ebp, edi;
        sub ebp, esi; // ebp = width

        mov al, 0;
        repne scasb;
        sub edi, esi;
        sub edi, 2;
        sub edi, ebp; // edi = pos
        jc DONE;

        add esi, edi;
        sub esi, ebp;

        xor ecx, ecx; // ecx = jump

    LOOP1:
        mov bh, [esi - 4 + 2 * ebp]; // bh = C
        mov bl, [esi + 2 * ebp]; // bl = D
        // bx = CD
        mov cl, 2;
        xor edx, edx
        mov eax, edi
        div ebp;
        cmp edx, 2;
        jne LABEL2;
        mov bh, 0
        inc ecx;
    LABEL2:

        mov ah, [esi - 4]; // ah = A
        mov al, [esi]; // al = B
        // ax = AB
        cmp edi, ebp;
        jge LABEL3;
        xor eax, eax;
    LABEL3:

        and eax, ebx; // ax = support_v
        or al, ah; // al = support_v
        and bl, bh; // bl = support_h
        or eax, ebx; // eax = support
        test byte ptr[esi - 2 + ebp], 1;
        jz LABEL4; // not a brick character - nothing to check
        shr eax, 1; // shift the LSB into the carry flag
        jnc DONE;
    LABEL4:
        sub esi, ecx;
        sub edi, ecx;
        jnc LOOP1;

    DONE:
        // here, the result is in the carry flag; copy it to eax
        popad;
        rcl eax, 1;
        and eax, 1;
        ret;
    }
}
\$\endgroup\$
7
\$\begingroup\$

MATLAB - 119 bytes

Minified:

function c=S(B),f=@(m)conv2([(0&B(1,:))+46;B]+3,m,'valid');M=[2 0;-1 -1;0 2];c=isempty(B)||all(all(f(M)&f(fliplr(M))));

Expanded:

function c = isstable( B )

f = @(m) conv2( [(0&B(1,:))+46; B] + 3, m, 'valid' );
M = [2 0;-1 -1;0 2];
c = isempty( B ) || all(all( f( M ) & f(fliplr( M )) ));

Sample Usage:

S4 = [  '..[__][__]..'; ...
        '[__][__][__]'; ...
        '..[__][__]..'; ...
        '[__]....[__]'];

fprintf( 'S4: %d\n', isstable( S4 ) );

S4: 1

U4 = [  '..[__][__]..'; ...
        '[__]....[__]'; ...
        '..[__][__]..'; ...
        '[__]....[__]'];

fprintf( 'U4: %d\n', isstable( U4 ) );

U4: 0

Details

The routine appends a row of . to the top of the input matrix, then converts to a numeric matrix by adding 3 to the ASCII character codes. Given this conversion, a 2D convolution with the kernel

 2  0
-1 -1
 0  2

yields a matrix with 0 at locations where the character pattern

 . *
 _ _
 * .

is present, with * representing "any character". Because of the construction of the kernel, this is the only valid character pattern that will yield a 0.

An identical convolution is performed with the left-right flipped version of the kernel to detect

 * .
 _ _
 . *

An input is stable if either i) it is empty, or ii) no zeros appear in either convolution.

Two frustrations are

  1. MATLAB's default convolution runs past the edges of the operand matrix, producing erroneous 0s in opposing corners for both convolutions, requiring ,'valid' (8 bytes) to be added to conv2 call to limit the output to the area where the convolution is valid.

  2. Handling the empty string case adds 12 bytes.

\$\endgroup\$
6
\$\begingroup\$

JavaScript (E6) 131 261

F=a=>
  [...a].every((e,p)=>
    !(d={']':-3,'[':3}[e])
     |a[p-r]=='_'&(x=a[p+r]!=' ')
     |a[p-r+d]=='_'&(y=a[p+r+d]!=' ')
     |x&y
  ,r=a.search(/\n/)+1)

Test in FireFox/FireBug console

;['[__]', '  [__]  \n[__][__]', '        [__]        \n      [__][__]      \n        [__]        ',
 '  [__][__]  \n[__][__][__]\n  [__][__]  \n[__]    [__]',
 '            [__]  \n  [__][__][__][__]\n[__][__][__][__]  \n  [__][__][__][__]\n[__][__][__][__]  ',
 '  [__]        [__]  \n[__][__][__][__][__]\n  [__][__][__][__]  \n    [__][__][__]    \n      [__][__]      \n        [__]        ']
.forEach(x => console.log(x+'\n'+F(x)))

;['  [__]  \n        ', '  [__]  \n[__]    ' ,'  [__]  \n    [__]',
 '  [__][__]  \n[__]    [__]\n  [__][__]  \n[__]    [__]',
 '  [__][__][__][__]\n[__][__][__][__]  \n  [__][__][__][__]\n[__][__][__][__]  ',
 '[__][__][__][__][__]\n  [__][__][__][__]  \n    [__][__][__]    \n      [__][__]      \n        [__]        ']
.forEach(x => console.log(x+'\n'+F(x)))

Output

    [__]
true

  [__]  
[__][__]
true

        [__]        
      [__][__]      
        [__]        
true

  [__][__]  
[__][__][__]
  [__][__]  
[__]    [__]
true

            [__]  
  [__][__][__][__]
[__][__][__][__]  
  [__][__][__][__]
[__][__][__][__]  
true

  [__]        [__]  
[__][__][__][__][__]
  [__][__][__][__]  
    [__][__][__]    
      [__][__]      
        [__]        
true

  [__]  
false

  [__]  
[__]    
false

  [__]  
    [__]
false

  [__][__]  
[__]    [__]
  [__][__]  
[__]    [__]
false

  [__][__][__][__]
[__][__][__][__]  
  [__][__][__][__]
[__][__][__][__]  
false

[__][__][__][__][__]
  [__][__][__][__]  
    [__][__][__]    
      [__][__]      
        [__]        
false

Ungolfed

F=a=>(
  a=a.replace(/__/g,'').replace(/  /g,'.'),
  r=a.search(/\n/)+1,
  [...a].every((e,p)=>
    e < '0' ||
    (e ==']'
    ? // stable right side
     a[p-r]=='[' & a[p+r]!='.' 
     |
     a[p-r-1]==']' & a[p+r-1]!='.' 
     |
     a[p+r]!='.' & a[p+r-1] != '.'
    : // stable left side
     a[p-r]==']' & a[p+r]!='.' 
     |
     a[p-r+1]=='[' & a[p+r+1]!='.' 
     |
     a[p+r]!='.' & a[p+r+1] != '.'
    )  
  )
)
\$\endgroup\$
  • \$\begingroup\$ What does [...a] do, if you don't mind my asking? I know ES6 allows ...arg as the last argument of a function to capture variadics, but I've never seen it used this way. \$\endgroup\$ – COTO Oct 5 '14 at 14:49
  • \$\begingroup\$ @COTO codegolf.stackexchange.com/a/37723/21348, use case 2 (it's very common, I use it in maybe 80% of my answers) \$\endgroup\$ – edc65 Oct 5 '14 at 15:25
  • \$\begingroup\$ Sunofagun. Just like {:} in MATLAB. That's going to be very useful. Thanks. :) \$\endgroup\$ – COTO Oct 5 '14 at 15:48
1
\$\begingroup\$

Python 279

I think I am pretty bad in code golf challenges and maybe I use the wrong languages for that :D But I love code that can be easily read :) Btw I would like to see a python code that uses less bytes!

def t(b):
    r=b.split()
    l=len(r[0])
    r=['.'*l]+r
    for i in range(len(r)-2,0,-1):
        r[i]+='...'
        for j in range(l):
            if(r[i][j]=='['):
                if(r[i+1][j]<>'_'or(r[i+1][j+3]<>'_'and r[i-1][j]<>'_'))and(r[i+1][j+3]<>'_'or r[i-1][j+3]<>'_'):
                    return False
    return True

Possible examples:

A = "..[__][__][__][__]\n\
[__][__][__][__]..\n\
..[__][__][__][__]\n\
[__][__][__][__].."
print t(A) #False

B = "..[__]........[__]..\n\
[__][__][__][__][__]\n\
..[__][__][__][__]..\n\
....[__][__][__]....\n\
......[__][__]......\n\
........[__]........"
print t(B) #True
\$\endgroup\$
  • \$\begingroup\$ I don't use the dots inside my code, actually your input can uses any char but not _ and [ \$\endgroup\$ – Wikunia Oct 5 '14 at 19:22
  • 1
    \$\begingroup\$ Generally instead of using <>, you would use !=. \$\endgroup\$ – Ethan Bierlein Oct 6 '14 at 0:40
  • \$\begingroup\$ @EthanBierlein wasn't sure but yes != is tge preferred way \$\endgroup\$ – Wikunia Oct 6 '14 at 7:24
1
\$\begingroup\$

JavaScript 2 (ES6) - 148 151 bytes

F=s=>s.split(/\n/).every((b,i,a)=>(r=1,b.replace(/]/g,(m,o)=>(T=z=>(a[i-1+(z&2)]||[])[o-z%2*3]=='_',r&=i>a.length-2?1:T(2)?T(3)|T(0):T(3)&T(1))),r))

Exepects a string of newline separated brick rows (note: if we could use a different separator character like "|" to separate rows this could be made 1 byte shorter).

Test in Firefox console with:

F('..[__]......\n[__][__][__]\n..[__][__]..\n[__]....[__]'); // false
F('..[__][__]..\n[__][__][__]\n..[__][__]..\n[__]....[__]'); // true
\$\endgroup\$
0
\$\begingroup\$

Python, 209

def s(b):
 c=b.split("\n");s="".join(c);l=len(c[0]);t=" "*l+s+"]]"*l;a=lambda x,y,z:t[x+l*y+z]=="]"
 return all([(a(i,1,1)&a(i,1,5))or(a(i,-1,1)&a(i,1,1))or(a(i,-1,5)&a(i,1,5))for i,x in enumerate(t)if x=="["])

Tests:

towers=(
"[__]",

"..[__]..\n"
"[__][__]",

"........[__]........\n"
"......[__][__]......\n"
"........[__]........",

"..[__][__]..\n"
"[__][__][__]\n"
"..[__][__]..\n"
"[__]....[__]",

"............[__]..\n"
"..[__][__][__][__]\n"
"[__][__][__][__]..\n"
"..[__][__][__][__]\n"
"[__][__][__][__]..",

"..[__]........[__]..\n"
"[__][__][__][__][__]\n"
"..[__][__][__][__]..\n"
"....[__][__][__]....\n"
"......[__][__]......\n"
"........[__]........",

"..[__]..\n"
"........",

"..[__]..\n"
"[__]....",

"..[__]..\n"
"....[__]",

"..[__][__]..\n"
"[__]....[__]\n"
"..[__][__]..\n"
"[__]....[__]",

"..[__][__][__][__]\n"
"[__][__][__][__]..\n"
"..[__][__][__][__]\n"
"[__][__][__][__]..",

"[__][__][__][__][__]\n"
"..[__][__][__][__]..\n"
"....[__][__][__]....\n"
"......[__][__]......\n"
"........[__]........",
)
[s(x) for x in towers]

Output:

[True, True, True, True, True, True, False, False, False, False, False, False]
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.