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 in a 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)+ add 0 1 2.. thus creating lists of neighbours

    • g:{?'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 to g for "graph"

    • +/'p=/:/:g for each of g'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 family

    • 0|/ boolean-or of all family matrices

    • (#a;1) replace 0s with "infinity" (the length of a is as good as infinity here) and keep 1s as they are - this is a graph of how closely related p 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 which p are among the first 2 (2#) of the intersection between people ((p^p^)) and each (') neighbour list in g, and make it a boolean table
  • they must be of opposite sexes

    • (~h=\:h:4!c p) remember that c 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.