Can this village have a wedding?
K (ngn/k), 230 203 200 196 bytes
{t:{x*\:x};n:#*x:4(+|0,)/x;p:&2!c:+/"╒ё╡Ё©ъъ"<\:a:,/x;|//(~h=\:h:4!c p)&t[~|/p=/:?,/2#'(p^p^)'g]&3<{&/'x+\:x}/(~=#p)*(#a;1)0|/t'?(+/'p=/:/:g:{?'x,/'x x}/(!#a)+(-e;e,:n;e;n*e:-1 1;())4^"┴┬─│"?a)^0}
the input and the k code must be encoded in koi8-r. test with (linux only):
git clone https://bitbucket.org/ngn/k
cd k/g
../k can-this-village-have-a-wedding.k
please make sure your editor handles koi8-r correctly. for instance, if using vim, you can type :e ++enc=koi8-r
after opening the file or put set fencs=utf-8,koi8-r
in your ~/.vimrc
k functions are written in {
}
, have an implicit argument x
, and consist of ;
-separated expressions.
the sequence of expressions is evaluated left-to-right but the code within each expression is right-to-left.
t:{x*\:x}
helper function that creates a multiplication table (outer product) for a list x
x:4(+|0,)/x
surrounds the input x
with zeroes. literally: 4 times (4(
)/
) add zero(-es) on top (0,
), reverse (|
), and transpose (+
).
n:#*x
let n
be the width of the input. literally: length (#
) of the first (*
)
a:,/x
let a
be the flattened input
c:+/"╒ё╡Ё©ъъ"<\:a
for each character in a
, count how many of "╒ё╡Ё©ъъ"
are before it (in koi8-r). this will give odd numbers for russian letters and even for non-letters. also, the remainder mod 4 will indicate the sex - upper/lowercase.
p:&2!c
take c
mod 2 (2!
) and make a list of the indices where (&
) it's 1. "p
" for "people".
the rest of the code will build three p
×p
matrices that represent conditions for marriage:
the couple must be >3 steps apart in the graph of relatives. a "step" is a relationship of parent-child or spouse or sibling.
4^"┴┬─│"?a
for each ina
find its index among"┴┬─│"
and fill in 4 if not found.(-e;e,:n;e;n*e:-1 1;())
replace"┴"
with (-1;1;-n),"┬"
with (-1;1;n),"─"
with (-1;1),"│"
with (-n;n), and others with an empty list(!#a)+
add0 1 2
.. thus creating lists of neighboursg:{?'x,/'x x}/
transitive closure - extend (,/
) each ('
) neighbour list (x
) with the neighbour lists of its neighbours (x x
) and unique it (?
), until convergence ({
}/
); assign tog
for "graph"+/'p=/:/:g
for each ofg
's neighbour lists build a boolean mask for which people are in it. ignore non-people.?(
)^0
remove scalar 0s ((
)^0
) as they are a by-product of empty neighbour lists, and make the rest unique (?
). this gives us a list of families as boolean masks.t'
build a family matrix for each family0|/
boolean-or of all family matrices(#a;1)
replace 0s with "infinity" (the length ofa
is as good as infinity here) and keep 1s as they are - this is a graph of how closely relatedp
are. we need to find the shortest paths in it.(~=#p)*
put 0s on the diagonal. literally: multiply (*
) by the negation (~
) of the unit matrix (=
) of that size (#p
){&/'x+\:+x}/
until convergence, try to improve dist(i,j) with dist(i,k)+dist(k,j) (similar to the floyd-warshall algorithm)3<
more distant than first cousins
they must not be already married
t[~|/p=/:?,/2#'(p^p^)'g]
check whichp
are among the first 2 (2#
) of the intersection between people ((p^p^)
) and each ('
) neighbour list ing
, and make it a boolean table
they must be of opposite sexes
(~h=\:h:4!c p)
remember thatc
mod 4 encodes gender information
finally, |//
...&
...&
... and-s the three matrices and tests if there's a truthy element in the result
Jelly, 160 159 bytes
Ø.UN,ƊAN,Ɗ+Ṫ¥+œị2,5yⱮ$ɼ=5,6⁼Ø.Ɗɗ¡ƬṪ¥ƒ⁸’1¦⁺œị®⁼5ƊпṖṪ+2¦œị®⁻.Ɗ¡ƬṪ¥ⱮØ+$“”¹?
Odȷ%⁴ỊḢịƊ€€H“¥©“©©‘;U¤œṣjƭƒ$€ƬṪ©=.ŒṪ+2¦œị®ɗⱮØ+f2,4ƊÐḟWÇ€Ẏ$Ƭḣ3ẎƲ€Œcf/ÐḟḢ€€ȧœị¥O>⁽¡FIFẸ
Try it online!
A monadic link that takes a list of Jelly strings and returns 1 for true and 0 for false.
I’m sure this could be golfed more. Full explanation to follow.