Питон 3 + сципи, 162, 160 байт
≔⊕Lθ
Попробуйте онлайн!
Старые версии:
WS⊞υι
Попробуйте онлайн!
Объяснение
Оставляет тяжелую работу (сегментацию) библиотечной функции. #
.
Остальное относительно просто. Все делается с транспонированными осями, чтобы избежать беспорядка при индексации. Мы перемещаем один помеченный компонент на одну единицу за раз, пока не пройдем через все из них, не имея возможности больше ничего перемещать.
Более конкретно, .
selects the pixels for label WS⊞υι≔⊕Lθθ≔⌕A⪫υψ#η≔⟦⟧ζWη«≔⟦⊟η⟧εFεF⁺κ⟦θ¹±¹±θ⟧F№ηλ«⊞ελ≔⁻ηεη»⊞ζε»WΦζ⬤κ∧﹪μθ¬⊙ζ›№ξ⊖μ⁼ξκFιUэκ⊖λEυэι§.#⊙ζ№ν⁺×θκμ
и мы их тут же обнуляем; w=>g=a=>a.some((c,i)=>!a.some(n=_=>a.some((d,j)=>(r=~j%w?j+1:j,d-c)?d*n[r]:(n[j]=[j,j%w?j-1:j,r,j-w,j+w].some(k=>n[k]))*!(j%w)),n[i]=++t))?g(a.map((c,i)=>n[i+1]?t:!n[i]*c)):a.map(c=>c>0);t=1
shifts the selection. import re
r=range
def f(b,x,y,t):
b[x][y]=t
for j,k in[[1,0],[0,1],[-1,0],[0,-1]]:
try:
if x+j>=0 and y+k>=0 and b[x+j][y+k]==1:f(b,x+j,y+k,t)
except:1
def g(b):
n=map(chr,r(97,123))
while'1'in str(b):f(b,*[(x,y)for x in r(len(b))for y in r(len(b[x]))if b[x][y]==1][0],next(n))
return re.sub('\w','#',(q:=lambda r:r if r==(t:=re.sub('\.\w+',lambda x:j if re.findall('^'+(j:=x.group())[1]+'|(?<=[^'+'\.'+j[1]+'])'+j[1],r)else j[1:]+'.',r))else q(t))('\n'.join(''.join(['.',i][bool(i)]for i in k)for k in b)))
принимает решение на основе того, есть ли помеченные пиксели (1) в базовом столбце или (2) (после смещения) столкновения с другими метками. Если // Converts a stringified number into a tuple
type StrNumToTuple<T, N = []> = T extends `${N["length"]}` ? N : StrNumToTuple<T, [...N, 0]>;
// Converts the input (e.g. [[0,0,1],[1,0,1],[0,1,1]])
// to a union of on cells ([2,0]|[0,1]|[2,1]|[1,2]|[2,2]) (where the numbers are represented by tuples)
type ToUnion<T> =
// Map over the 2d array
{ [I in keyof T]: { [J in keyof T[I]]:
T[I][J] extends 1
// if the cell is on, add this tuple
? [StrNumToTuple<J>, StrNumToTuple<I>]
: never
// put the results into a union
}[keyof T[I]] }[number]
// Retrieves an arbitrary element of a union
type PopUnion<T> = (T extends T ? (x: () => T) => 0 : 0) extends (x: infer U) => 0 ? U extends () => infer V ? V : 0 : 0;
type Inc<T> = [...T, 0];
type Dec<T> = T extends [0, ...infer X] ? X : [];
// Get the orthogonal neighbor positions of a position
type Neighbors<T> = T extends [infer A, infer B] ? ( ([Dec<A>, B] | [Inc<A>, B] | [A, Dec<B>] | [A, Inc<B>]) ) : 0;
type FindGroup<All, Group = PopUnion<All>, LastGroup = 0> =
[Group] extends [LastGroup]
// If the group didn't change last iteration, return it
? Group
// Add to Group the Neighbors of Group that are in All
: FindGroup<All, Group | Extract<Neighbors<Group>, All>, Group>
// Converts a union of positions into a tuple of unions of positions, grouped by connectedness
type Group<Ungrouped, Groups = []> =
[Ungrouped] extends [never]
// If there are no positions left in Ungrouped, return Groups
? Groups
// Find a group, remove its elements from Ungrouped, and add it to Groups
: Group<Exclude<Ungrouped, FindGroup<Ungrouped>>, [...Groups, FindGroup<Ungrouped>]>
// Shift a group to the left. Returns {} (a top type of sorts) if it's at the leftmost border
type ShiftLeft<Group> =
Group[0] extends [0, ...0[]]
// If all X coordinates are >= 1, map over the positions in Group
? Group extends Group
// Decrement the X coordinate of this position
? [Dec<Group[0]>, Group[1]]
// Unreachable
: 0
// Otherwise, return {}
: {}
// The meat of the logic. Continually shifts groups left until it can't anymore
type Defrag<Groups, PrevGroups = 0> =
Groups extends PrevGroups
// If Groups == PrevGroups, return Groups
? Groups
// Otherwise, recurse:
: Defrag<
{
// Map over Groups
[K in keyof Groups]:
// If (Groups[number] (all live positions) - Groups[K]) shares no elements with ShiftLeft<Groups<K>>,
Extract<Exclude<Groups[number], Groups[K]>, ShiftLeft<Groups[K]>> extends 0
// Change this group to ShiftLeft<Groups[K]>
? ShiftLeft<Groups[K]>
// Otherwise, leave it unchanged
: Groups[K]
},
Groups
>
type Main<Input, FinalLivePositions = Defrag<Group<ToUnion<Input>>>[number]> = {
// Map over every row of Input
[I in keyof Input]: Input[I] extends infer Row ? {
// Map over every cell of Row
[J in keyof Row]:
// If this coordinate is in FinalLivePositions, set this cell to 1; otherwise, set it to 0
[StrNumToTuple<J>, StrNumToTuple<I>] extends FinalLivePositions ? 1 : 0
} : 0
}
is set we write the erased pixels back and move to the next label, if //@ts-ignore
type a<T,N=[]>=T extends`${N["length"]}`?N:a<T,d<N>>;type b<T>={[I in keyof T]:{[J in keyof T[I]]:T[I][J]extends 1?[a<J>,a<I>]:never}[keyof T[I]]}[number];type c<T>=(T extends T?(x:()=>T)=>0:0)extends(x:infer U)=>0?U extends()=>infer V?V:0:0;type d<T>=[...T,0];type e<T>=T extends[0,...infer X]?X:[];type f<T>=T extends[infer A,infer B]?[e<A>,B]|[d<A>,B]|[A,e<B>]|[A,d<B>]:0;type g<P,T=c<P>,U=0>=[T]extends[U]?T:g<P,T|Extract<f<T>,P>,T>;type h<P,G=[]>=[P]extends[never]?G:h<Exclude<P,g<P>>,[...G,g<P>]>;type i<T>=T[0]extends[0,...0[]]?T extends T?[e<T[0]>,T[1]]:0:{};type j<T,U=0>=T extends U?T:j<{[K in keyof T]:Extract<Exclude<T[number],T[K]>,i<T[K]>>extends 0?i<T[K]>:T[K]},T>;type M<T,U=j<h<b<T>>>[number]>={[I in keyof T]:T[I]extends infer X?{[J in keyof X]:[a<J>,a<I>]extends U?1:0}:0}
не установлено, мы сдвигаем их и сбрасываем индекс метки.