Find the Infinity Words!
Jelly, 43 41 40 25 24 23 22 21 14 13 bytes
-7 bytes thanks to fireflame241 (0ị=1ị$
->=ṚḢ
and use of IIA⁼2,2
to test for the 4 rotations)
-1 Thanks to Kevin Cruijssen (use of previously unavailable nilad Ø2
which yields [2,2]
)
=ṚḢȧOIṠIIA⁼Ø2
TryItOnline
Or all test cases (plus "RULES")
How?
An infinity word has:
- the same first and last letter;
- length 5;
- no equal letters next to each other;
- sum of its four alphabet deltas equal to zero;
- sum of its four alphabet deltas signs equal to zero;
- two positive alphabet deltas or two negative alphabet deltas in a row.
All but (1) and (equivalently) (4) may be boiled down to a condition that the alphabet delta signs are some rotation of [1,1,-1,-1]
(where the sign of 0
is 0
)
fireflame241 noted that this is then equivalent to the deltas of the deltas of the alphabet delta signs being in [[2,2],[2,-2],[-2,2],[-2,-2]]
which may be tested by the absolute values being equal to [2,2]
!
How?
=ṚḢȧOIṠIIA⁼Ø2 - Main link: word
Ṛ - reverse word
= - equals? (vectorises)
Ḣ - head (is the first character equal to the last?)
ȧ - and
O - cast word to ordinals
I - increments - the alphabet deltas (or just [] if 1st != last)
Ṡ - sign (vectorises)
I - increments - deltas of those signs
I - increments - deltas of those
A - absolute value (vectorises)
Ø2 - literal [2,2]
⁼ - equals? (non-vectorising version)
Java 8, 231 193 185 122 103 78 bytes
s->s.length==5&&(s[1]-s[0])*(s[3]-s[2])<0&(s[2]-s[1])*(s[4]-s[3])<0&s[4]==s[0]
Try it here.
-38 bytes thanks to @dpa97 for reminding me to use char[]
instead of String
.
-63 bytes thanks to @KarlNapf's derived formula.
-25 bytes by converting it from Java 7 to Java 8 (and now returning a boolean instead of integer).
193 bytes answer:
int c(char[]s){if(s.length!=5)return 0;int a=s[0],b=s[1],c=s[2],d=s[3],e=s[4],z=b-a,y=c-b,x=d-c,w=e-d;return e!=a?0:(z>0&y>0&x<0&w<0)|(z<0&y>0&x>0&w<0)|(z>0&y<0&x<0&w>0)|(z<0&y<0&x>0&w>0)?1:0;}
Explanation:
- If the length of the string isn't 5, we return
false
- If the first character doesn't equal the last character, we return
false
- Then we check the four valid cases one by one (let's indicate the five characters as 1 through 5), and return
true
if it complies to any of them (andfalse
otherwise):- If the five characters are distributed like:
1<2<3>4>5
(i.e.ALPHA
) - If the five characters are distributed like:
1>2<3<4>5
(i.e.EAGLE
,HARSH
,NINON
,PINUP
) - If the five characters are distributed like:
1<2>3>4<5
(i.e.RULER
) - If the five characters are distributed like:
1>2>3<4<5
(i.e.THEFT
,WIDOW
)
- If the five characters are distributed like:
These four rules can be simplified to 1*3<0 and 2*4<0
(thanks to @KarlNapf's Python 2 answer).
JavaScript (ES6), 91 89 87 bytes
Saved 2 bytes thanks to Ismael Miguel
s=>(k=0,[...s].reduce((p,c,i)=>(k+=p>c?1<<i:0/(p<c),c)),k?!(k%3)&&!s[5]&&s[0]==s[4]:!1)
How it works
We build a 4-bit bitmask k
representing the 4 transitions between the 5 characters of the string:
k += p > c ? 1<<i : 0 / (p < c)
- if the previous character is higher than the next one, the bit is set
- if the previous character is lower then the next one, the bit is not set
- if the previous character is identical to the next one, the whole bitmask is forced to
NaN
so that the word is rejected (to comply with rule #6)
The valid bitmasks are the ones that have exactly two consecutive 1
transitions (the first and the last bits being considered as consecutive as well):
Binary | Decimal
-------+--------
0011 | 3
0110 | 6
1100 | 12
1001 | 9
In other words, these are the combinations which are:
k?
: greater than 0!(k%3)
: congruent to 0 modulo 3- lower than 15
The other conditions are:
!s[5]
: there's no more than 5 characterss[0]==s[4]
: the 1st and the 5th characters are identical
NB: We don't explicitly check k != 15
because any word following such a pattern will be rejected by this last condition.
Test cases
let f =
s=>(k=0,[...s].reduce((p,c,i)=>(k+=p>c?1<<i:0/(p<c),c)),k?!(k%3)&&!s[5]&&s[0]==s[4]:!1)
console.log("Testing truthy words...");
console.log(f("ALPHA"));
console.log(f("EAGLE"));
console.log(f("HARSH"));
console.log(f("NINON"));
console.log(f("PINUP"));
console.log(f("RULER"));
console.log(f("THEFT"));
console.log(f("WIDOW"));
console.log("Testing falsy words...");
console.log(f("CUBIC"));
console.log(f("ERASE"));
console.log(f("FLUFF"));
console.log(f("LABEL"));
console.log(f("MODEM"));
console.log(f("RADAR"));
console.log(f("RIVER"));
console.log(f("SWISS"));
console.log(f("TRUST"));
console.log(f("KNEES"));
console.log(f("QUEEN"));
console.log(f("ORTHO"));
console.log(f("GROOVE"));
console.log(f("ONLY"));
console.log(f("CHARACTER"));
console.log(f("OFF"));
console.log(f("IT"));
console.log(f("ORTHO"));
Initial version
For the record, my initial version was 63 bytes. It's passing all test cases successfully but fails to detect consecutive identical characters.
([a,b,c,d,e,f])=>!f&&a==e&&!(((a>b)+2*(b>c)+4*(c>d)+8*(d>e))%3)
Below is a 53-byte version suggested by Neil in the comments, which works (and fails) equally well:
([a,b,c,d,e,f])=>!f&&a==e&&!((a>b)-(b>c)+(c>d)-(d>e))
Edit: See Neil's answer for the fixed/completed version of the above code.