| Bytes | Lang | Time | Link |
|---|---|---|---|
| 069 | Dyalog APL | 250905T225123Z | Aaron |
| 407 | Python3 | 250511T173111Z | Ajax1234 |
| 209 | Python | 141101T103642Z | legionix |
| nan | JavaScript 2 ES6 148 | 141005T152138Z | me and m |
| 098 | 80386 machine code | 141006T214802Z | anatolyg |
| nan | 141005T144611Z | edc65 | |
| 279 | Python | 141005T124647Z | Wikunia |
| 119 | MATLAB | 141005T103613Z | COTO |
Dyalog APL, 69 bytes
{∧/,{(1∊2≤+/¨(3 x)(x 1)((x←1 3)3)⌷¨⊂⍵)∨2≠2 2⌷⍵}⌺3 3⊢1⍪⍨('_'=⍵)+'.'≠⍵}
'.'≠⍵ # Find where the input is not a dot,
('_'=⍵) # find where the input is the inner part of a brick,
+ # and add these. This makes the inner parts a 2 and the rest of a brick a 1
1⍪⍨ # Put a row of 1s on the bottom so "ground" can be supportive
{ }⌺3 3⊢ # Run with a 3x3 stencil
2≠2 2⌷⍵ # If the center of the stencil not a 2, we call it supported (that is, we only care about the rest of this expression if it is the inside of a brick)
( )∨ # or
⌷¨⊂⍵ # Select from the stencil matrix
((x←1 3)3) # the right corners
(x 1) # the left corners
(3 x) # the bottom corners
+/¨ # Add over each of those
2≤ # See if their sum is 2 or more
1∊ # Make sure one of those is true
∧/, # Finally, ravel and and-over to ensure everything in the matrix was reported as supported
💎
Created with the help of Luminespire.
Python3, 407 bytes
E=enumerate
W=lambda X,D,U:{V for _,K in U if(V:=D.get((X,K),-1))>-1}
def f(s):
c,d=0,{}
for x,r in E(s):
q=[]
for y,a in E(r):
if a in'[__]':q+=[(x,y)]
if']'==a:d[c]=q;q=[];c+=1
D={j:a for a,b in d.items()for j in b}
return all((L:=d[b][0][0])==len(s)-1 or len(W(L+1,D,d[b]))==2 or len({*W(L+1,D,d[b][:2]),*W(L-1,D,d[b][:2])})==2 or len({*W(L+1,D,d[b][2:]),*W(L-1,D,d[b][2:])})==2 for b in d)
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]
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
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;
}
}
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] != '.'
)
)
)
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
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
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 toconv2call to limit the output to the area where the convolution is valid.Handling the empty string case adds 12 bytes.