Line up for golf!
JavaScript (ES6), 554 499 ... 463 bytes
Takes input as an array of strings and returns an array of arrays of strings. Parsing rule: and
has higher precedence than or
.
Below is a slightly formatted version. The compact version is included in the snippet.
([P,...C])=>
[...Array(m=9349)]
.map(_=>p[a=P.match(/[A-E]\w+/g).sort(_=>(k=k*2%m)&1)]?0:p[a]=a,p={},k=1)
.filter(s=>
s&&eval(
C.join`and `.match(/.+?(or |and |$)/g)
.map(s=>
(
c=s.match(/([A-E]\w+|\d|no|la|fr|be|x|p|or|an)/g),
S=n=>`s.indexOf('${c[p+n]}')`,
E=`]=='${c[0]}'`,
c[p=1]=='no'?(p++,'!(s['):'(s['
)+
(
(x=+c[p])&&p++,
{
la:'s.length-'+x+E,
fr:F=S(1)+-1+E,
be:B=S(1)+'+1'+E,
x:F+'||s['+B,
p:S(2)+(c[p+1]>'f'?-x:'+'+x)+E
}[c[p]]||--x+E
)+
(c.pop()>'o'?')||':')&&')
).join``+1
)
)
How it works
1. Initialization
The input array is split into:
P
= first line, describing the playersC
= array containing all other lines
We first extract the names of all players from P
:
P.match(/[A-E]\w+/g)
We compute all permutations of the names, using several iterations of sort()
:
sort(_ => (k = k*2 % 9349) & 1)
The prime 9349
was (empirically) chosen in such a way that all permutations are guaranteed to be found, given enough iterations of the sort, for up to 5 players.
2. Filtering
The main part of the function consists of filtering these permutations to keep only the ones that match all conditions in C
.
We join the different condition lines with and
and split the resulting string on or
and and
to get an array of conditions.
Each condition is parsed to isolate the relevant tokens:
Token | RegExp part
---------------+------------
Name of player | [A-E]\w+
Digit | \d
'not' | no
'last' | la
'in front of' | fr
'behind' | be
'next to' | x
'spaces' | p
'or' | or
'and' | an
Each condition is translated into JS code:
Condition | JS code
------------------------------+-------------------------------------------------------------
'X is Nth' | s[N - 1] == 'X'
'X is Nth to last' | s[s.length - N] == 'X'
'X is in front of Y' | s[s.indexOf('Y') - 1] == 'X'
'X is behind Y' | s[s.indexOf('Y') + 1] == 'X'
'X is next to Y' | s[s.indexOf('Y') - 1] == 'X' || s[s.indexOf('Y') + 1] == 'X'
'X is N spaces in front of Y' | s[s.indexOf('Y') - N] == 'X'
'X is N spaces behind Y' | s[s.indexOf('Y') + N] == 'X'
Finally, all codes are joined with ||
and &&
operators and the resulting string is eval()
'd.
For example, below are the translations of the test cases as JS code:
(s[1]=='Alice')&&(s[s.indexOf('Alice')-1]=='Bob')&&1
(s[s.indexOf('Bob')-1]=='Alice')&&(s[s.length-1]=='Carol')&&1
(s[s.indexOf('Bob')-1]=='Alice')&&1
(s[s.indexOf('Bob')-1]=='Alice')&&(s[s.indexOf('Alice')-1]=='Bob')&&1
!(s[s.indexOf('Bob')-1]=='Alice')&&!(s[1]=='Carol')||(s[0]=='Bob')&&1
Test cases
let f =
([P,...C])=>[...Array(m=9349)].map(_=>p[a=P.match(/[A-E]\w+/g).sort(_=>(k=k*2%m)&1)]?0:p[a]=a,p={},k=1).filter(s=>s&&eval(C.join`and `.match(/.+?(or |and |$)/g).map(s=>(c=s.match(/([A-E]\w+|\d|no|la|fr|be|x|p|or|an)/g),S=n=>`s.indexOf('${c[p+n]}')`,E=`]=='${c[0]}'`,c[p=1]=='no'?(p++,'!(s['):'(s[')+((x=+c[p])&&p++,{la:'s.length-'+x+E,fr:F=S(1)+-1+E,be:B=S(1)+'+1'+E,x:F+'||s['+B,p:S(2)+(c[p+1]>'f'?-x:'+'+x)+E}[c[p]]||--x+E)+(c.pop()>'o'?')||':')&&')).join``+1))
console.log(JSON.stringify(f([
'The players are Alice, Bob and Carol.',
'Alice is 2nd.',
'Bob is in front of Alice.'
])));
console.log(JSON.stringify(f([
'The players are Alice, Bob, and Carol.',
'Alice is 1 space in front of Bob.',
'Carol is 1st to last.'
])));
console.log(JSON.stringify(f([
'The players are Alice, Bob, and Carol.',
'Alice is in front of Bob.'
])));
console.log(JSON.stringify(f([
'The players are Alice, Bob, and Carol.',
'Alice is in front of Bob.',
'Bob is in front of Alice.'
])));
console.log(JSON.stringify(f([
'The players are Alice, Bob, and Carol.',
'Alice is not in front of Bob and Carol is not 2nd or Bob is 1st.'
])));
Python 3, 527 524 511 bytes
import re,itertools as I
S=re.sub
p=re.findall(r"\w+(?=[,.])",input())
l=[]
try:
while 1:
c=[["not "*("not "in c)+S(" .+o (.+)",r" in[\1+1,\1-1]",S(" .+f","+1==",S(" .+d","-1==",S(r" .+(\d+).+",r"+1==\1",S(r" .+(\d+).+st",r"==len(p)-\1",S(r" .+(\d+).+f",r"+\1==",S(r" .+(\d+) .+d",r"-\1==",c[:-1]))))))),c][len(c)<7]for c in re.split("(or|and) ",input())]
while c[1:]:c[:3]=["("+" ".join(c[:3])+")"]
l+=[c[0]]
except:print([P for P in I.permutations(p)if eval("and ".join(l),dict(zip(P,range(len(p)))))])
Try it online!
Outputs an array of valid permutations. Expects the names to only contain A-Za-z0-9_
(Alice
through Eve
are obviously valid).
PHP, way too long (790 744 725 697 678 669 bytes)
foreach(file(F)as$i=>$s)if($i){$g("#(.*)( and | or |\.)#U",$s,$w,2);foreach($w as $q){preg_match("#^(\w+)(.*?)(\w+)$#",$q[1],$m);$n=$u[-3+$p=($o=strpos)($u=$m[2],p)];$d.="!"[!$o($u,no)]."(".strtr(!$o($u,x)?A.($o($u,f)?$p?"==B-$n":"<B":($o($u,b)?$p?"==B+$n":">B":"==".preg_replace("#.*(\d).*#",($k=$o($u,a))?"$z-$1":"$1",$k?$u:$m[3]))):"(A-B)**2<2",[A=>$m[1],B=>$m[3]]).")$q[2]";}$d=!$x.="&"[!$x]."($d)";}else{($g=preg_match_all)("#(\w+)[.,]#",$s,$m);$z=count($y=$m[1]);}for(;$j++<10**$z;)if(count_chars($p="$j",3)==join(range(1,$i=$z))){for($c=$x;$i--;)$c=strtr($c,["."=>"","not"=>"",$y[$p[$i]-1]=>$i+1]);for(;eval("return$c;")&++$i<$z;)echo$y[$p[$i]-1],"
,"[$i<$z-1];}
takes input from file F
, no trailing newline; and
before or
. Run with -nr
or test it online.
breakdown
foreach(file(F)as$i=>$s)if($i) # loop through input lines
{
$g("#(.*)( and | or |\.)#U",$s,$w,2); # split statement to conditions
foreach($w as $q)
{
preg_match("#^(\w+)(.*?)(\w+)$#",$q[1],$m); # 1. copy names to $m[1],$m[3]
$n=$u[-3+$p=($o=strpos)($u=$m[2],p)]; # 2. remove names, 3. find N in "N spaces"
$d.="!"[!$o($u,no)]."(". # 4. negate, 6. concatenate
strtr( # 5. translate condition:
!$o($u,x)
?A.($o($u,f)?$p?"==B-$n":"<B" # (N spaces) in front of
:($o($u,b)?$p?"==B+$n":">B" # (N spaces) behind
:"==".preg_replace("#.*(\d).*#", # N-th
($k=$o($u,a))?"$z-$1":"$1" # ... (to last)
,$k?$u:$m[3])
))
:"(A-B)**2<2" # next to
,[A=>$m[1],B=>$m[3]])
.")$q[2]";
}
$d=!$x.="&"[!$x]."($d)"; # concatenate statements, clear statement
}else # first line: import names to $y, count to $z
{($g=preg_match_all)("#(\w+)[.,]#",$s,$m);$z=count($y=$m[1]);}
# loop through combinations
for(;$j++<10**$z;)if(count_chars($p="$j",3)==join(range(1,$i=$z))){
for($c=$x;$i--;) # remove dot and "not", replace names
$c=strtr($c,["."=>"","not"=>"",$y[$p[$i]-1]=>$i+1]);
# all conditions met? print combination!
for(;eval("return$c;")&++$i<$z;)echo$y[$p[$i]-1],"\n,"[$i<$z-1];
}