# Play Zip, Zap, Zop

There's a little improv warm up game where you arrange yourselves in a circle and send zips, zaps, and zops around by pointing to a person and saying the next word in the sequence, then they do the same until all of you are warmed up or whatever.

Your task is to create a program that gives the next word in sequence given an input word. (Zip --> Zap --> Zop --> Zip) Since there's a lot of different ways to say these three words and flairs that can be added to them, your program should imitate case and letter duplication and carry suffixes.

To elaborate, your input will be one or more Zs, then one or more Is, As, or Os (all the same letter), then one or more Ps, (all letters up to this point may be in mixed case) followed by some arbitrary suffix (which may be empty). You should leave the runs of Zs and Ps, as well as the suffix exactly as received, but then change the Is to As, As to Os, or Os to Is, preserving case at each step.

## Example Test Cases

zip         ==> zap
zAp         ==> zOp
ZOP         ==> ZIP
ZiiP        ==> ZaaP
ZZaapp      ==> ZZoopp
zzzzOoOPppP ==> zzzzIiIPppP
Zipperoni   ==> Zapperoni
ZAPsky      ==> ZOPsky
ZoPtOn      ==> ZiPtOn
zipzip      ==> zapzip
zapzopzip   ==> zopzopzip
zoopzaap    ==> ziipzaap


## Rules and Notes

• You may use any convenient character encoding for input and output, provided that it supports all ASCII letters and that it was created prior to this challenge.
• You may assume the input word is some variant of Zip, Zap, or Zop. All other inputs result in undefined behavior.
• Valid inputs will full-match the regex Z+(I+|A+|O+)P+.* (in mixed case)

Happy Golfing!

• ziop -> what does this do? – Joshua Feb 27 at 16:09
• @Joshua This is invalid according to the description (see "all the same letter"). – Arnauld Feb 27 at 16:23
• @Arnauld: And the test case for zoopzaap disagrees with the description. – Joshua Feb 27 at 16:25
• @Joshua Why? This only applies to the vowels between the leading z's and the first p. The suffix may contain anything. – Arnauld Feb 27 at 16:27

# JavaScript (Node.js),  69 63 57  54 bytes

s=>Buffer(s).map(c=>s|c%4<1?s=c:c+c*90%320%34%24-8)+''


Try it online!

### How?

We process the input string $$\s\$$ character by character.

We reuse $$\s\$$ as a flag: as soon as a numeric value is stored in it, we know that we must not update anything else.

To identify "p" (112) and "P" (80), we use the fact that their ASCII codes are multiples of $$\4\$$ and the ASCII codes of the other letters at the beginning of the string ("z", "Z" and vowels) are not.

To turn a vowel with ASCII code $$\c\$$ into its counterpart $$\n\$$ while leaving z and Z unchanged, we use the following function:

$$n=c+((((90\times c) \bmod 320)\bmod 34)\bmod 24)-8$$

 letter | ASCII code |  * 90 | % 320 | % 34 | % 24 | - 8 | new letter
--------+------------+-------+-------+------+------+-----+-----------------------
'i'  |     105    |  9450 |  170  |   0  |   0  |  -8 | 105 -  8 =  97 -> 'a'
'a'  |      97    |  8730 |   90  |  22  |  22  |  14 |  97 + 14 = 111 -> 'o'
'o'  |     111    |  9990 |   70  |   2  |   2  |  -6 | 111 -  6 = 105 -> 'i'
'z'  |     122    | 10980 |  100  |  32  |   8  |   0 | 122 +  0 = 122 -> 'z'
'I'  |      73    |  6570 |  170  |   0  |   0  |  -8 |  73 -  8 =  65 -> 'A'
'A'  |      65    |  5850 |   90  |  22  |  22  |  14 |  65 + 14 =  79 -> 'O'
'O'  |      79    |  7110 |   70  |   2  |   2  |  -6 |  79 -  6 =  73 -> 'I'
'Z'  |      90    |  8100 |  100  |  32  |   8  |   0 |  90 +  0 =  90 -> 'Z'


### Commented

s =>                  // s = input string
Buffer(s)           // convert it to a Buffer of ASCII codes
.map(c =>           // for each ASCII code c in s:
s |               //   if s is numeric
c % 4 < 1 ?       //   or c is either 'p' or 'P':
s = c           //     turn s into a numeric value and yield c
:                 //   else:
c +             //     update c
c * 90 % 320  //     by applying the transformation function
% 34 % 24     //     (see above)
- 8           //
) + ''              // end of map(); coerce the Buffer back to a string

• How did you come up with that function? – Thomas Hirsch Feb 27 at 22:50
• @ThomasHirsch It was found with a brute-force search function. The first step (multiplication + 1st modulo) makes sure that the parameters give identical result for both lowercase and uppercase. The 2nd step (the next 2 modulos and the subtraction) checks whether the correct delta values can be obtained from there. – Arnauld Feb 27 at 23:19

# C (gcc),  81 ... 61 48  46 bytes

Saved 2 bytes thanks to @Grimy

Port of my JS answer. Outputs by modifying the input string.

f(char*s){for(;*++s%4;*s+=*s*90%320%34%24-8);}


Try it online!

### Commented

f(char * s) {       // f = function taking the input string s
for(;             //   for each character *s in s:
*++s % 4;       //     advance the pointer; exit if *s is either 'p' or 'P' (it's safe
//     to skip the 1st character, as it's guaranteed to be 'z' or 'Z')
*s +=           //     update the current character:
*s * 90 % 320 //       apply a transformation formula that turns
% 34 % 24     //       a vowel into the next vowel in the sequence
- 8           //       while leaving 'z' and 'Z' unchanged
);                //   end of for()
}                   // end of function

• -2 bytes – Grimy Feb 28 at 12:50
• @Grimy Nice catch, thanks! (I did try *++s%4 at some point but overlooked the resulting optimization...) – Arnauld Feb 28 at 13:02
• Further -3 bytes. This one should also be applicable to your JS answer. – Grimy Feb 28 at 14:37
• @Grimy It's different enough from mine, so you may want to post this as a separate answer. – Arnauld Feb 28 at 14:48

# Stax, 19 bytes

Ç╛√êΣ%,╖FP╚=Lh←⌡·ƒ


Run and debug it

# Retina 0.8.2, 21 bytes

iTIoA\OIia\oi^.+?p


Try it online! Transliterates letters up to and including the first p, although the z and p aren't in the transliteration section so aren't affected. The first O is quoted because it normally expands to 13567 and the second o is quoted because it too is magic; in the first part of the transliteration it expands to the other string. The resulting transliteration is therefore from IAOIiaoi to AOIiaoi then removing the duplicate source letters results in IAOiao to AOIaoi.

# C, 43 bytes

f(char*s){for(;*++s%4;*s^=*s%16*36%98%22);}


Try it online!

Based on Arnauld's answer. I did a brute-force search to find the shortest formula that turns a => o, o => i, i => a, z => z.

# Japt, 22 20 bytes

-2 bytes thanks to Oliver

r"%v+"_di¬iao¯pu}"


Try it online!

# R, 110 76 bytes

-36 bytes thanks to Krill

This function takes an input of one string.

function(a)sub(s<-sub('z+(.+?)p.*','\\1',a,T),chartr('aioAIO','oaiOAI',s),a)


Try it online!

• Collecting the string from pieces in R tends to be very long... But you save a lot of bytes by first extracting a substring s that we'll be translating, and then replacing it with a translated copy: 76 bytes – Kirill L. Feb 27 at 8:46
• @KirillL., Ah that's the clever trick I was trying to find. – CT Hall Feb 27 at 15:18

{S:i{.+?p}=$/~~tr/iaoIAO/aoiAOI/}  Try it online! Simple case-insensitive substitution to shift the vowel section. # Python 3.8 (pre-release), 81 78 bytes lambda s,p='iaoIAO':''.join(('aoiAOI'+c)[(p:=p*(ord(c)%4)).find(c)]for c in s)  Try it online! # Python 2, 98 bytes lambda s:''.join(('aoiAOI'+c)[(-~-('p'in s[:i].lower())*'iaoIAO').find(c)]for i,c in enumerate(s))  Try it online! # Java (JDK), 52 bytes s->{for(int i=0;s[++i]%4>0;)s[i]^=s[i]%16*36%98%22;}  Try it online! • Port of Grimy's C answer. Make sure to upvote his answer! # Perl 5, 31 bytes s/.+?p/$&=~y,iaoIAO,aoiAOI,r/ei


TIO

# SNOBOL4 (CSNOBOL4), 183 bytes

	INPUT (BREAK('Pp') SPAN('Pp')) . P REM . S
A	P 'a' ='o'	:S(A)
Z	P 'A' ='O'	:S(Z)F(P)
I	P 'i' ='a'	:S(I)
K	P 'I' ='A'	:S(K)F(P)
O	P 'o' ='i'	:S(O)
L	P 'O' ='I'	:S(L)
P	OUTPUT =P S
END


Try it online!

# Python 2, 84 bytes

f=lambda s,r='':s[0]in'pP'and r+s or f(s[1:],r+('aoiAOI'+s[0])['iaoIAO'.find(s[0])])


Try it online!

# C# (Visual C# Interactive Compiler), 60 bytes

n=>{for(int i=0;n[i]%4>0;)n[i]^=(char)(n[i++]%16*36%98%22);}


Based off of Grimy's C answer.

Try it online!

• This unfortunately doesn't work as it replaces the vowels in the suffix as well. – Emigna Feb 27 at 7:04
• As @Emigna states above, this replaces all aoi-vowels, instead of just the ones before the first p/P. One thing to golf however: ("iao".IndexOf((char)(c|32))+1)%4 can be -~"iao".IndexOf((char)(c|32))%4 – Kevin Cruijssen Feb 27 at 12:04

# C/C++ (VC++ compiler) 192bytes

this is a rather naive try but anyway

void f(char*I){int c[]={-8,14,6},B=1,v[]={105,97,111},j=0;for(*I;*I>0&B;I++){if(*I==80|*I==112){B=0;break;}if(*I==90|*I==122){}else{for(j;j<3;j++){if(*I==v[j]|*I==v[j]-32){*I+=c[j];break;}}}}}


some slightly more readable Version is this

#include "stdafx.h"

void f(char * theString)
{
signed int change[] = {'a'-'i','o'-'a','o'-'i'}; // add this to the vowel to get the next one
char theVowels[] = {'i','a','o'};
int breaker = 1;
printf("Input %s\n",theString);
for (int i = 0;(theString[i] != '\0') && breaker; i++)
{
switch (theString[i])
{
case 'Z': /*fall through*/
case 'z': break;
case 'P': /*fall through*/
case 'p': breaker = 0;
break;
default:
{
for (int j = 0; j < 3; j++)
{
if ((theString[i] == theVowels[j]) || (theString[i]==(theVowels[j]-'a'+'A')))
{
theString[i] += change[j];
break;
}
}
}
break;
}

}
printf("Output %s\n",theString);
}
int main()
{
char theString[]= "zzzzIIIIp0815-4711"; // a test string
f(theString);
return 0;
}


$_[$&]=$&.tr"IAOiao","AOIaoi"if/z+.+?p/i  Try it online! # 05AB1E (legacy), 22 bytes l'pkIg‚£ć…iaoDÀ‚Du+‡ì  Can definitely be golfed a bit more.. Uses the legacy version of 05AB1E instead of the Elixir rewrite, because + merges fields in same-length lists, whereas the new version would need a pair-zip-join ‚øJ instead. Explanation: l # Lowercase the (implicit) input-string # i.e. "ZipPeroni" → "zipperoni" 'pk '# Get the index of the first "p" # i.e. "zipperoni" → 2 Ig‚ # Pair it with the length of the entire input-string # i.e. 2 and "zipperoni" → [2,9] £ # Split the (implicit) input-string into parts of that size # i.e. "ZipPeroni" and [2,9] → ["Zi","pPeroni"] ć # Extract head: push the head and remainder separately to the stack # i.e. ["Zi","pPeroni"] → ["pPeroni"] and "Zi" …iao # Push string "iao" DÀ # Duplicate, and rotate it once towards the left: "aoi" ‚ # Pair them up: ["iao","aoi"] Du # Duplicate and transform it to uppercase: ["IAO","AOI"] + # Python-style merge them together: ["iaoIAO","aoiAOI"]  # Push both strings to the stack ‡ # Transliterate; replacing all characters at the same indices # i.e. "Zi", "iaoIAO" and "aoiAOI" → "Za" ì # Prepend it to the remainder (and output implicitly) # i.e. ["pPeroni"] and "Za" → ["ZapPeroni"]  # PHP, 30 bytes too simple for a TiO: <?=strtr($argn,oiaOIA,iaoIAO);


Run as pipe with -nF. For PHP 7.2, put the string literals in quotes.

• This transliterates all oia vowels instead of only the ones before the first p/P, doesn't it? – Kevin Cruijssen Mar 1 at 15:39