| Bytes | Lang | Time | Link |
|---|---|---|---|
| 121 | Dyalog APL | 250830T051734Z | Aaron |
| 173 | Perl 5 | 250629T123300Z | good old |
| 095 | Charcoal | 250627T104515Z | Neil |
| 067 | 05AB1E | 250627T083431Z | Kevin Cr |
| 037 | Uiua 0.17.0dev.1 | 250627T042501Z | Tbw |
Dyalog APL, 121 chars
{↑{⍵⌷¨⊂' o'}¨{⍵∘{⊃1+{(⍵-1)↓⍺↑b}/(⌈/,⌊/)⍸⍺=⍵}¨0~⍨∪,⍵}⊃{⌈/(⍸b){⍵[⊂⍺]⌈@((b∧1≥+/¨2*⍨(⊂⍺)-⍳⍴b)⍨)⍵}¨⍵}⍣≡⊂b×(⊢⍴⍳∘(×/))⍴b←↑' '=⍵}
woof. Input is a vector of strings, one for each row of input, on the right. Alright, here goes
{↑{⍵⌷¨⊂' o'}¨{⍵∘{⊃1+{(⍵-1)↓⍺↑b}/(⌈/,⌊/)⍸⍺=⍵}¨0~⍨∪,⍵}⊃{⌈/(⍸b){⍵[⊂⍺]⌈@((b∧1≥+/¨2*⍨(⊂⍺)-⍳⍴b)⍨)⍵}¨⍵}⍣≡⊂b×(⊢⍴⍳∘(×/))⍴b←↑' '=⍵}
b←↑' '=⍵ # Find the elements equal to space and save in (b)itmask
( )⍴ # Send the shape of it to this train
⍳∘(×/) # Generate indices for the product of the shape
⊢⍴ # reshape that into the bitmask's shape
b× # and multiply that by the bitmask. This gives a unique id to every marked element
⊂ # Enclose to pass the whole thing to the next function
{ }⍣≡ # Apply this function until the output stops changing
(⍸b){ }¨⍵ # Apply to each coordinate where the bitmask is 1 to and the entirety of the input matrix
@ # Update the input matrix AT
( ) # The indices where this is true
(⊂⍺)-⍳⍴b # The difference between the coords of the bitmask and the current one I'm looking at
2*⍨ # Squaring each (the x-difference and the y-difference)
1≥+/¨ # where the sum is less than or equal to 1. This finds the cardinal neighbors
b∧ # AND with the bitmask to only flow into holes indicated by the bitmask
( )⍨ # Make all that avaialble as a function because that is what @ expects
⍵[⊂⍺]⌈ # For every element indicated, get the max between the current index I'm looking at and the cardinal neighbor
⌈/ # Find the max among all the maps generated. When the map stops changing, then the flood fill is complete and any touching holes with be isolated from non-touching ones
⊃ # Disclose to get a good ol' fashioned matrix
{ } # Now to isolate the holes
0~⍨∪,⍵ # Ravel the final map, find unique ids, and remove 0
⍵∘{ }¨ # For each of those unique ids, call this function, putting the map as the left argument
⍸⍺=⍵ # Get the coordinates of the elements of the map that match this id
(⌈/,⌊/) # Get the maximum and minimum of each. This gives us the bounding box
{ }/ # Call this function between those maxs and mins
⍺↑b # Take max many rows/cols from the bitmask
(⍵-1)↓ # Drop one less than the min rows/cols from that. This gets the minimal box containing the hole
⊃1+ # Add 1 for index's sake (in a moment) and disclose
{ }¨ # For each of those minimal bounding-box bitmasks
⍵⌷¨⊂' o' # Index each of them into this string
↑ # and mix for a lovely display
💎
Created with the help of Luminespire.
I do have a sneaking suspicion I can golf this down a bit but I already golfed down about 30 chars, so pausing for now.
Perl 5, 173 bytes
$_="3\n @{[<>]} \n";$L=length;for(s/.+/pack"A$L",$&/ge;s/ /3/;y/3/#/){1while s/3(.{$L})?\K | (?=(.{$L})?3)/3/s;local$_=/^3/?next:y/#/ /r;s/^ //mg until/^3/m;s/ +$//mg;print}
A few extra linefeeds in the output, as (I guess) allowed in the challenge. "Any visible character to represent the hole" is "3", of course (because if you rub-rub-rub, there'll be a hole).
Here with a few comments:
# Pad on the left, top and bottom. Seed.
$_="3\n @{[<>]} \n";
$L=length;
for(
# Pad on the right, make all lines equal.
s/.+/pack"A$L",$&/ge;
# Any more spaces? Seed then.
s/ /3/;
# Coalesce the hole processed just before.
y/3/#/
) {
# Flood fill.
1while s/3(.{$L})?\K | (?=(.{$L})?3)/3/s;
# Ugly part: skip the very 1st iteration. Its only purpose is
# to account for cavities (if any) on the sides.
# Otherwise, localise and make other holes and walls appear the same.
local$_=/^3/?next:y/#/ /r;
# Trim on the left..
s/^ //mg until/^3/m;
# ...and on the right. It also converts blank lines to just linefeeds.
# (And IF it was a cheat, it'd be easy to crop them.)
s/ +$//mg;
print
}
Charcoal, 95 bytes
WS⊞υ⪫⪪ι ψB⁺²Lθ⁺²LυψM↘υ¤#≔⟦⟧υFⅉFLθ«UMKA⎇Σλ#λJ⊕κι¤1¿ΣKK⊞υ⪪⪫KAω⁺²Lθ»⎚UTEυEΦιΣλ✂⭆λ§ oΣν⌊Eι⌕λ1⊕⌈⌕Aλ1
Try it online! Link is to verbose version of code. Takes input as a rectangle of newline-terminated strings. Explanation:
WS⊞υ⪫⪪ι ψ
Input the shape and replace spaces with nulls.
B⁺²Lθ⁺²LυψM↘υ¤#
Output the shape surrounded with a border of nulls and then fill that border with #s.
≔⟦⟧υFⅉFLθ«
Look for a hole at each position of the shape.
UMKA⎇Σλ#λ
Fill in any hole found in the previous iteration.
J⊕κι¤1
Try to fill a hole with 1s.
¿ΣKK⊞υ⪪⪫KAω⁺²Lθ
If that was successful then save a snapshot of the canvas.
»⎚UT
Clear the canvas and turn off automatic space-padding.
EυEΦιΣλ✂⭆λ§ oΣν⌊Eι⌕λ1⊕⌈⌕Aλ1
Trim each snapshot and replace the #s with spaces and the 1s with os.
05AB1E, 67 bytes
ðQ4F¬dšøí}©˜ƶ®gäΔ4F¬dšøí}2Fø€ü3}®*εε˜ŽqÆSèà]©˜ê¦¨ε®Qø[¬à#¦}ø0δÜ0ð:
Input as a rectangular matrix of characters (right-padded with spaces).
Output as a list of matrices of characters, including leading/trailing linefeeds (as []).
Try it online or verify all test cases. The header is for copy-pastable input (make sure to right-pad it with spaces!) and the footer is to pretty-print the output list of matrices.
Explanation:
ðQ # Mark all spaces as 1s and "#"s as 0s in the (implicit) input-matrix
4F¬dšøí} # Add a border of 1s around the matrix:
4F } # Loop 4 times:
¬ # Get the first row (without popping the matrix)
d # Transform all to 0s to 1s using an >=0 check
š # Prepend this row to the matrix
øí # Rotate the matrix 90 degrees:
ø # Zip/transpose; swapping rows/columns
í # Reverse each inner row
©˜ƶ®gäΔ4F¬dšøí}2Fø€ü3}®*εε˜ŽqÆSèà]
# Flood-fill the matrix †
© # Store this flood-filled matrix in variable `®` (without popping)
˜ # Flatten it
ê # Uniquify and sort its values
¦ # Remove the leading 0
¨ # Remove the trailing largest island
# (the outer border we added for the island of right-padded spaces)
ε # Map over each unique island:
®Q # Mark those as 1s in matrix `®`
ø[¬à#¦}ø # Remove all left-padded 0s without changing the shape:
ø # Zip/transpose; swapping rows/columns
[ # Start an infinite loop:
¬ # Get the first row (without popping the matrix)
à # Pop and push its maximum
# # If this is 1: stop the infinite loop
¦ # If it's a 0 instead: remove this row of 0s
}ø # After the infinite loop: zip/transpose back
0δÜ # Remove all left-padded 0s:
δ # Map over each row:
0 Ü # Trim trailing 0s
0ð: # Then replace all 0s with spaces
# (after which the list of matrices is output implicitly as result)
†: Just look at the explanation of some of my existing answers with flood-fills, like this one or this one.
Uiua 0.17.0-dev.1, 37 bytes SBCS
↘1⊜(□⊏:" o"°⊚-¤/↧.):°⊡↻⊟.¯1⬚1↙+2△.=@
Takes input and output as arrays of single line strings. The main thing that makes this work is multidimensional partition working on contiguous regions. The initial code pads the outer edge so that at the end we can just drop the first "hole", which is the outside face.