| Bytes | Lang | Time | Link |
|---|---|---|---|
| 035 | Uiua | 240828T112930Z | nyxbird |
| 341 | Python3 | 240826T140517Z | Ajax1234 |
| 407 | Java 8 | 180308T110444Z | Kevin Cr |
| 122 | Javascript | 180309T104923Z | edc65 |
| 089 | Perl 5 | 180308T064443Z | Ton Hosp |
| 026 | MATL | 180307T230059Z | Luis Men |
| 040 | Stax | 180308T012942Z | recursiv |
| 118 | Wolfram Language Mathematica | 180308T072223Z | alephalp |
| 290 | Python 2 | 180307T215230Z | hyper-ne |
Uiua, 35 bytes
⬚0↯:°⊚▽≠⍜⊂≡(⧻⊜π.⍜⬚∞⊡¬)⊙(,¤⟜⊚)⟜:⊂.⧻.
⬚0↯:°⊚▽≠⍜⊂≡(⧻⊜π.⍜⬚∞⊡¬)⊙(,¤⟜⊚)⟜:⊂.⧻.
⊂.⧻. # push [length length], keeping original
⟜: # duplicate around the original
⊙(,¤⟜⊚) # between the [len len]s:
⟜⊚ # push the location of the 1s,
¤ # a 'fixed' version of the array
, # and duplicate the locations of the 1s
⍜⊂ # push [len len] to the locations
≡( ) # for each location:
⍜⬚∞⊡¬ # set its cell in the fixed array to 0
⧻⊜π. # and count the number of groups of 1s
⍜⊂ # pop the count for [len len]
▽≠ # keep the points that aren't equal to [len len]'s count
°⊚ # convert them to a binary array
⬚0↯: # pad to size [len len] with 0s
Python3, 341 bytes
E=enumerate
def G(g):
while g:
q,s=[(t:=[*g][0])],[t];g-={t}
for x,y in q:
for X,Y in[(1,0),(0,1),(0,-1),(-1,0)]:
if(u:=(x+X,y+Y))in g:s+=[u];q+=[u];g-={u}
yield{*s}
def f(b):
B=[[0 for _ in i]for i in b]
for i in G({(x,y)for x,r in E(b)for y,v in E(r)if v}):
for j in i:
if len([*G(i-{j})])!=1:x,y=j;B[x][y]=1
return B
Java 8, 503 489 459 455 407 bytes
int R,C,v[][];m->{int r[][]=new int[R=m.length][C=m[0].length],i=R*C,a=i(m),x,y,q;for(;i-->0;m[y][x]=0,r[y][x]=i(m)!=a?1:0,m[y][x]=q)q=m[y=i/C][x=i%C];return r;}int i(int[][]m){int r=0,i=R*C,x,y;for(v=new int[R][C];i-->0;)if(m[y=i/C][x=i%C]>v[y][x]){d(m,x,y);r++;}return r;}void d(int[][]m,int c,int r){v[r][c]=1;for(int k=-3,x,y;k<4;k+=2)if((x=c+k%2-k/2)>=0&x<C&(y=r+k/2)>=0&y<R&&m[y][x]>v[y][x])d(m,x,y);}
-66 bytes thanks to @ceilingcat.
Explanation:
int R,C, // Amount of rows/columns on class-level
v[][]; // Visited-matrix on class-level
m->{ // Method with int-matrix as both parameter and return-type
int r[][]=new int[R=m.length][C=m[0].length],
// Create the result-matrix, and set `R` and `C`
i=R*C, // Index-integer
a=i(m), // The current amount of islands
x,y, // Temp integers for the coordinates
q; // Temp integer for the value
for(;i-->0 // Loop `i` over each cell:
; // After each iteration:
m[y][x]=0, // Temporarily change the current value to 0
r[y][x]=i(m)!a? // If the current and initial amount of islands are not
// the same anymore:
1 // Set the result-value at this coordinate to 1
: // Else:
0, // Set the result-value at this coordinate to 0
m[y][x]=q) // Then change the current value back to what it was
q=m[y=i/C][x=i%C]; // Save the current value at this coordinate
return r;} // Return the result-matrix
// Separated method to determine the amount of islands in a matrix
int i(int[][]m){
int r=0, // Result-count, starting at 0
i=R*C, // Index integer
x,y; // Temp integers for the coordinates
for(v=new int[R][C]; // Reset the visited array
i-->0;) // Loop over the cells:
if(m[y=i/C][x=i%C] // If the current cell is a 1,
>v[y][x]){ // and we haven't visited it yet:
d(m,x,y); // Check every direction around this cell
r++;} // And raise the result-counter by 1
return r;} // Return the result-counter
// Separated method to check each direction around a cell
void d(int[][]m,int c,int r){
v[r][c]=1; // Flag this cell as visited
for(int k=-3,x,y;k<4;k+=2)// Loop over the four directions:
if((x=c+k%2-k/2)>=0&x<C&(y=r+k/2)>=0&y<R
// If the cell in the direction is within bounds,
&&m[y][x] // and it's a path we can walk,
>v[y][x]) // and we haven't visited it yet:
d(m,x,y);} // Do a recursive call for this cell
Javascript 122 bytes
Input/output as a multiline string.
m=>m.replace(/./g,(v,p,m,n=[...m],f=p=>n[p]==1&&(n[p]=0,v=f(p-1)+f(p+1)+f(p-w)+f(p+w)-1?1:0,1))=>(f(p),v),w=~m.search`\n`)
For each walkable cell, put a block and try to fill the 4 neighbor cells. If the current cell is not a cut point, then starting from any open neighbor will fill all of them. Else, I will need more than one fill operation to reach all neighbor cells.
Less golfed
m=>{
w = m.search('\n') + 1; // offset to the next row
result = [...m].map( // for each cell
( v, // current value
p // current position
) => {
n = [...m]; // work on a copy of the input
// recursive fill function from position p
// returns 1 if managed to fill at least 1 cell
fill = (p) => {
if (n[p] == 1)
{
n[p] = 0;
// flag will be > 1 if the fill from the current point found disjointed areas
// flag will be 0 if no area could be filled (isolated cell)
var flag = fill(p+1) + fill(p-1) + fill(p+w) + fill(p-w);
// v is modified repeatedly, during recursion
// but I need the value at top level, when fill returns to original caller
v = flag != 1 ? 1 : 0;
return 1; // at least 1 cell filled
}
else
return 0; // no fill
}
fill(p)
return v // orginal value or modified by fill function
})
}
Test
var F=
m=>m.replace(/./g,(v,p,m,n=[...m],f=p=>n[p]==1&&(n[p]=0,v=f(p-1)+f(p+1)+f(p-w)+f(p+w)-1?1:0,1))=>(f(p),v),w=~m.search`\n`)
var test=`in:
11101001
11011101
00000001
11101111
11110101
00011111
10110001
11111111
out:
01000000
00001001
00000001
00000101
00110000
00010000
00000000
11100000
in:
1111111111111111
1000000000000001
1111111111111101
0000000000000101
1111111111110101
1000000000010101
1011111111010101
1010000001010101
1010111101010101
1010101111010101
1010100000010101
1010111111110101
1010000000000101
1011111111111101
1000000000000001
1111111111111111
out:
0000000000000000
0000000000000000
0000000000000000
0000000000000000
0000000000000000
0000000000000000
0000000000000000
0000000000000000
0000000000000000
0000000000000000
0000000000000000
0000000000000000
0000000000000000
0000000000000000
0000000000000000
0000000000000000
in:
1011010001111010
1111111011101101
1110010101001011
1111001110010010
1111010000101001
0111101001000101
0011100111110010
1001110011111110
0101000011100011
1110110101001110
0010100111000110
1000110111011010
0100101000100101
0001010101100011
1001010000111101
1000111011000010
out:
0000000000111010
1011110001001000
0000000000000011
0000000100010000
0000010000101000
0000001000000100
0000000011000000
1001100000011110
0000000001000010
0110100001000110
0000100101000010
1000100000000000
0100001000000100
0000000100100001
0000010000111000
0000010000000010
`.match(/\d[10\n]+\d/g);
for(i = 0; test[2*i]; ++i)
{
input = test[2*i]
check = test[2*i+1]
result = F(input)
ok = check == result
console.log('Test '+ i + ' ' + (ok?'OK':'FAIL'),
'\n'+input, '\n'+result)
}
Perl 5, -p0 105 101 96 93 90 89 bytes
Uses b instead of 1 in the input.
Make sure the matrix on STDIN is terminated with a newline
#!/usr/bin/perl -p0
s%b%$_="$`z$'";s:|.:/
/>s#(\pL)(.{@{-}}|)(?!\1)(\pL)#$&|a.$2.a#se&&y/{c/z />0:seg&/\B/%eg
Uses 3 levels of substitution!
This 87 byte version is both in input and output format easier to interpret, but is not competing since it uses 3 different characters in the output:
#!/usr/bin/perl -0p
s%b%$_="$`z$'";s:|.:/
/>s#(\w)(.{@{-}}|)(?!\1)(\w)#$&|a.$2.a#se&&y/{c/z />0:seg&/\B/%eg
It's easy to save another byte (the regex s modifier) in both versions by using some different (non-alphanumeric) character as row terminator (instead of newline), but that makes the input quite unreadable again.
How it works
Consider the substitution
s#(\w)(.{columns}|)(?!1)(\w)#c$2c#s
This will find two letters that are different and next to each other horizontally or vertically and replace them by c. In a maze whose paths consist of entirely the letter b nothing will happen since the letters are the same, but as soon as one of the letters is replaced by another one (e.g. z) that letter and a neighbor will be replaced by c and repeated application is a flood-fill of the connected component with c from seed z.
In this case I however don't want a complete flood-fill. I want to fill only one of the arms neighboring z, so after the first step I want the z gone. That already works with the c$2c replacement, but I later want to restart a flood-fill along another arm starting from the same point and I don't know which of the cs was originally z anymore. So instead I use
s#(\w)(.{columns}|)(?!\1)(\w)#$&|a.$2.a#se
b | a is c, b | c is c and z | a is {. So in a maze with paths made up of b and a seed z in the first step b will get replaced by c and z will get replaced by { which is not a letter and does not match \w and so will not cause further fills. The c however will keep a further flood-fill going and one neighbor arm of the seed gets filled. E.g. starting from
b c
b c
bbzbb becomes bb{bb
b b
b b
I can then replace all c by some non letter (e.g. -) and replace { by z again to restart the flood-fill:
- -
- -
bbzbb becomes cc{bb
b b
b b
and repeat this process until all neighbors of the seed have been converted. If I then once more replace { by z and flood-fill:
- -
- -
--z-- stays --z--
- -
- -
The z remains behind at the end because there is no neighbor to do a transformation with.
That makes clear what happens in the following code fragment:
/\n/ >
Find the first newline. The starting offset is now in @-
s#(\w)(.{@{-}}|)(?!\1)(\w)#$&|a.$2.a#se
The regex discussed above with @{-} as number of columns (since plain @- confuses the perl parser and doesn't properly substitute)
&&
The /\n/ always succeeds and the substitution is true as long as we can still flood-fill. So the part after && is executed if the flood-fill of one arm is done. If not the left side evaluates to an empty string
y/{c/z / > 0
Restart the flood-fill and return 1 if the previous flood-fill did anything. Else return the empty string. This whole piece of code is wrapped inside
s:|.: code :seg
So if this is executed on a starting string $_ with a z at the seed position the piece of code inside will be executed many times mostly returning nothing but a 1 every time a neighbor arm gets flood-filled. Effectively $_ gets destroyed and replaced by as many 1s as there are connected components attached to z. Notice that the loop needs to be executed up to sum of component sizes + number of arms times but that is OK since it will "number of characters including newlines * 2 + 1" times.
The maze gets disconnected if there are no 1's (empty string, an isolated vertex) or if there are more than 1 arms (more than 2 1s). This can be checked using the regex /\B/ (this gives 0 instead of 1 on older perl versions. It's arguable which one is wrong). Unfortunately if it doesn't match this will give an empty string instead of 0. However the s:|.: code :seg was designed to always return an odd number so by doing an & with /\B/ this will give 0 or 1.
All that is left is walking the whole input array and at each walkable position seed with z and count connected arms. That is easily done with:
s%b%$_="$`z$'"; code %eg
The only problem is that in the non-walkable positions the old value is retained. Since we need 0s there that means the original input array must have 0 in the non walkable positions and 0 matches \w in the original substitution and would trigger flood-fills. That's why I use \pL instead (only match letters).
MATL, 26 bytes
n:"GG0@(,w4&1ZIuz]=~]vGZye
The input is a numerical matrix, using ; as row separator.
Try it online! Or verify all test cases.
Explanation
n % Implicit input: matrix. Push number of elements, N
: % Range: gives [1 2 ... N]
" % For each k in [1 2 ... N]
GG % Push input matrix twice
0@( % Write 0 at position k (in column-major order: down, then across).
% The stack now contains the original matrix and a modified matrix
% with 0 at position k
, % Do twice
w % Swap
4 % Push 4. This specifies 4-element neighbourhood
&1ZI % Label each connected component, using the specified
% neighbourhood. This replaces each 1 in the matrix by a
% positive integer according to the connected component it
% belongs to
u % Unique: gives a vector of deduplicate elements
z % Number of nonzeros. This is the number of connected components
] % End
=~ % Are they different? Gives true of false
] % End
v % Concatenate stack into a column vector
GZye % Reshape (in column-major order) according to size of input matrix.
% Implicit display
Stax, 40 bytes
Çóê↓â.Φ}╞│*w<(♦◙¼ñ£º█¢,D`ì♥W4·☺╛gÇÜ♠╗4D┬
This program takes input as a space separated string containing rows. The output is in the same format. Here's the unpacked ascii representation.
{2%{_xi48&GxG=-}_?m}{'1'2|e{"12|21".22RjMJguHgu%
The fundamental operation for counting an island works like this.
- Replace the first
'1'with a'2'. - Regex replace
'12|21'with'22'. - Split on spaces.
- Transpose matrix.
- Repeat from 2. until a string is repeated.
- Repeat from 1. until there is no longer a
'1'in the string. The number of repetitions is the number of islands.
.
{ start map block over input string, composed of [ 01]
2% mod by 2. space and 0 yield 0. 1 yields 1. (a)
{ start conditional block for the 1s.
_ original char from string (b)
xi48& make copy of input with current character replaced with 0
G jump to unbalanced }, then return; counts islands (c)
xG counts islands in original input (d)
= are (c) and (d) equal? 0 or 1 (e)
- b - e; this is 1 iff this character is a bridge
} end conditional block
_? execute block if (a) is 1, otherwise use original char from string
m close block and perform map over input
} goto target - count islands and return
{ start generator block
'1'2|e replace the first 1 with a 2
{ start generator block
"12|21".22R replace "12" and "21" with "22"
jMJ split into rows, transpose, and rejoin with spaces
gu generate values until any duplicate is encountered
H keep the last value
gu generate values until any duplicate is encountered
% count number of iterations it took
Bonus 44 byte program - this version inputs and outputs using the grid format.
Wolfram Language (Mathematica), 118 bytes
(r=ReplacePart)[0#,Cases[#~Position~1,p_/;(c=Max@MorphologicalComponents[#,CornerNeighbors->1<0]&)@r[#,p->0]>c@#]->1]&
Python 2, 290 bytes
lambda m:[[b([[C and(I,J)!=(i,j)for J,C in e(R)]for I,R in e(m)])!=b(eval(`m`))for j,c in e(r)]for i,r in e(m)]
def F(m,i,j):
if len(m)>i>=0<=j<len(m[i])>0<m[i][j]:m[i][j]=0;F(m,i,j+1);F(m,i,j-1);F(m,i+1,j);F(m,i-1,j)
b=lambda m:sum(F(m,i,j)or c for i,r in e(m)for j,c in e(r))
e=enumerate
-11 bytes thanks to Rod
-11 bytes thanks to Lynn