The Magical Piper

An old test for programmers, taken from here (Note: in italian).

Along a road (denoted by '_'), there is a Magical Piper (denoted by 'P') and several mice (represented by the two characters 'o' and '~', that denote respectively the head and the tail of the small rodents).

Write a program that prints how many mice are going towards the Piper and how many mice are going away from him.

The input is a string, the output are two numbers, the first the number of mice that go towards the Piper, the second the number of mice that go away from the Piper.

The input can be also a list or vector of characters, and the output a vector or list of two numbers. You can assume that the input is always correct.

Both programs as well as functions are allowed.

Examples:

input: __o~P_o~~o

output: 1 2

input: __P~oo~o~_o~~o__

output: 3 2

input: ~oPo~

output: 2 0

input: o~P~o

output: 0 2

This is code golf, so the shortest program in any language wins.

Note: In the question, I've removed the requirement of an error message, given the majority prefer it this way. Sorry for people that already coded the error test!

• I'd prefer if there weren't error checking, but the input was guaranteed to be well formed
– xnor
Feb 6, 2020 at 9:33
• @xnor, I maintaned the error to be true to the original text, but if you think that it is more interesting for this site to assume always correct input, I will change the question. Feb 6, 2020 at 10:14
• As for me, it would be indeed more interesting with error-checking. Feb 6, 2020 at 12:09
• @KevinCruijssen, added to the question, thanks! Feb 6, 2020 at 15:18
• Ah, ok. In any case I think the best thing to do is to left everything as it is now. Feb 6, 2020 at 23:33

Ruby, 128 .. 51 49 bytes

->w{%w(o ~).map{|c|w.scan(/_*._*(.)/).count [c]}}


Try it online!

No error checking.

How:

• Ignore all underscores
• All mice going towards the piper have the head in an odd-numbered (0-based) position.
• All mice heading away from the piper have the tail in an odd-numbered position.
• Take every 2nd character (skipping underscores), count heads and tails.

Ruby (with error checking), 109 99 97 bytes

->w{w.chars+w.scan(/[o~]./)-%w(o ~ _ ~o o~)==[?P]?%w(o ~).map{|c|w.scan(/_*._*(.)/).count [c]}:q}


Try it online!

Thanks Value Ink for -2 bytes on both.

• [?o,?~].map [...] .count [c] saves 2 bytes. Feb 8, 2020 at 0:14

05AB1E, 1311 10 bytes

'_KāÈÏaTS¢


Port of @GB's Ruby answer, so make sure to upvote him!!
-1 byte thanks to @Grimmy by taking the input as character-list.

Explanation:

'_K        '# Remove all "_" from the (implicit) input-list
ā        # Push a list in the range [1, length] (without popping the list)
È       # Check for each whether it's even
Ï      # Only keep the characters at these even 1-based indices
a     # Check for each remaining character whether it's a letter or not
# (1 if 'o'; 0 if '~')
TS   # Push 10 as digit-list: [1,0]
¢  # And count each in the list of 0s and 1s
# (after which this pair of counts is output implicitly as result)

• -1 by taking input as a character list Feb 6, 2020 at 17:26
• @Grimmy Ah, smart use of the aTS, thanks! Feb 6, 2020 at 18:04

JavaScript (Node.js),  80 66 65 58  57 bytes

Returns [towards, away].

s=>Buffer(s).map(c=>c%3?i^=~c:a[c&1]+=++i&1,a=[i=0,0])&&a


Try it online!

How?

An efficient way to determine whether the character is a mouse body part is to take the ASCII code modulo $$\3\$$.

Quite conveniently, we can also distinguish between P and _ or between o and ~ by simply testing the parity of the corresponding ASCII codes.

 character  | 'P' | '_' | 'o' | '~'
------------+-----+-----+-----+-----
ASCII code |  80 |  95 | 111 | 126
modulo 3   |   2 |   2 |   0 |   0
modulo 2   |   0 |   1 |   1 |   0


Commented

s =>                       // s = input string
Buffer(s)                // turn s into a buffer
.map(c =>                // for each ASCII code c:
c % 3 ?                //   if c modulo 3 is not equal to 0:
i ^= ~c              //     invert the least significant bit of i if c is even
//     (must be 80, for 'P')
:                      //   else:
a[c & 1] += ++i & 1, //     update a[0] if c is even (126 -> '~')
//     or a[1] if c is odd (111 -> 'o')
//     increment i and add its parity
a = [i = 0, 0]         //   start with a = [0, 0] and i = 0
) && a                   // end of map(); return a[]


JavaScript (Node.js),  112  102 bytes

A version with error checking, as per the original rules of the challenge.

s=>Buffer(s).some(c=>n?n-=c:c-111&&c-126?c-95?c-80|p++:0:!++a[n=c^17,p^c&1],a=[n=0,p=0])|p-1?'error':a


Try it online!

• @KevinCruijssen Thanks! But I was already editing to another 57. Feb 6, 2020 at 15:21
• Then I'll post it as a separated answer. :) Feb 6, 2020 at 15:22

Python 2, 47 bytes

Port of G B's Ruby answer.

lambda s:map(s.replace('_','')[::2].count,'~o')


Try it online!

Python 2, 252 $$\\cdots\$$ 71 72 bytes

Saved 29 bytes thanks to JoKing!!!
Saved 2 bytes thanks to ovs!!!
Saved a byte thanks to ReinstateMonica!!!
Saved 21 bytes thanks to KevinCruijssen!!!
Added a byte to fix a bug kindly pointed out by JoKing.

def f(s):
r=[0,0];i=0
for c in s:
if'_'!=c:r[c>'o']+=i;i^=1
return r


Try it online!

Inputs a string and outputs a list.

• @JoKing Very clever stuff - thanks! :-) Feb 6, 2020 at 14:31
• 93 bytes with '~'==c -> c>'o' and i=1-i -> i^=1.
– ovs
Feb 6, 2020 at 14:51
• def f(s,i=1,r=[0,0]): also saves a byte over assigning i and r in the function Feb 6, 2020 at 16:09
• @ovs Nice golfs - thanks! :-) Feb 6, 2020 at 16:25
• And an additional -19 bytes by removing the if'P'==c:r=r[::-1] line completely and modifying the other line a bit. Since P is guaranteed to be at an even index if we ignore all _, the character P will always result in an i=0 increase, so it won't matter for the r[c>'o']+=i;. Now it's basically a port of my Java 8 answer. :) Feb 6, 2020 at 18:14

J, 27 19 bytes

[:+/_2=&'~'\'_'-.~]


Try it online!

how

We'll work through an example: __o~P_o~~o

• '_'-.~] Remove the underscores:

o~Po~~o

• _2=&'~'\ For every two characters, create a boolean mask where 1 indicates that the character in that position is ~:

0 1  o~
0 0  Po
1 1  ~~
0 0  o

• [:+/ Sum the colums:

1 2


Retina, 31 bytes

+o~(?!o)
1
T1~~1.*P
*\C1
~


Try it online!

Explanation

+o~(?!o)
1


Recursively replace all mice walking to the left with a 1. Mice walking to right stay as they are.

T1~~1.*P


Swap 1 and ~ at all positions up to the P.

*\C1


Every 1 stands for a mouse walking towards the Magical Piper. Count the 1's and output the result without changing the current string.

~


Count ~'s (implicit output). every ~ stands for a mouse walking away from the Magical Piper.

Retina, 23 18 bytes

port of G B's Ruby answer. 5 bytes shorter thanks to Neil.

_

(.).
$1 *\C~ o  Try it online! • It's golfier to delete even-numbered characters: Try it online! – Neil Feb 6, 2020 at 14:35 • @Neil This looks much better. thanks a lot. – ovs Feb 6, 2020 at 14:46 JavaScript (Node.js), 57 54 bytes s=>Buffer(s).map(c=>a[~c&1]+=c%19&&i++%2,a=[i=0,0])&&a  Alternative JavaScript answer for the existing JavaScript answer (of equal byte-count at time of writing), which I figured was worth posting as separated answer. Loosely based on my Java 8 answer and inspired by @GB's Ruby answer. -3 bytes thanks to @Arnauld. Try it online. Explanation: s=> // Method with string as parameter and integer-array as result Buffer(s).map(c=> // Loop over the characters in the String as codepoint integers: a[~c&1]+= // Increase the count at index (c+1)%2 by: c%19&& // If c modulo-19 is NOT 0: i++%2, // Add i%2 to the count, and increase i by 1 afterwards a=[i=0,0] // Start the counts at [0,0] and i at 0 )&&a // After the loop, return this resulting pair of counts  The codepoints of the four used characters are 'P'=80, '_'=95, 'o'=111, '~'=126, which I'm using to my advantage: • The ~c&1 will result in index 0 for the character 'o' and index 1 for character '~'. (It will also result in index 1 for character 'P' and index 0 for character '_', but this won't cause any issues.) • The modulo-19 is to ignore '_' characters. Using the modulo-19 on the four codepoints results in 4,0,16,12 for P_o~ respectively, for which the '_' is falsey and the other three are truthy. The i++%2 will only add 1 to the count for odd-indexed characters. All even-indexed characters won't affect the counts. Since the 'P' is guaranteed to be at an even-indexed position (when ignoring the '_' with c%19&&), the count for character 'P' will always increase by 0, and thus stay the same. • 54 bytes Feb 6, 2020 at 15:38 • @Arnauld Thanks! I had the feeling a=[i=0,0] might have been possible somehow like in your answer, but couldn't figure it out. Now that I see the i++%2 I can't believe I hadn't thought of it just yet. And nice ~c&1 trick! Feb 6, 2020 at 15:43 JavaScript (ES6) Port of G B's Ruby answer. a=>a.filter(v=>v!='_').map((v,i)=>i%2||v=='P'?0:a[+(v=='o')]++,a=[0,0])&&a  C (gcc), 87 85 bytes Saved 2 bytes thanks to @xibu Prints the results as towards away. a,b,p;f(char*s){for(a=b=p=0;*s;s++)*s%3?p|=~*s:p+*s++&1?b++:a++;printf("%d %d",a,b);}  Try it online! Commented a, // a = 'towards' counter b, // b = 'away' counter p; // p = piper flag f(char *s) { // f is function taking a pointer to a string s for( // main loop: a = b = p = 0; // start with a = b = p = 0 *s; // go on while the end of the string is not reached s++ // advance the pointer after each iteration ) // *s % 3 ? // if the current character is not a mouse body part: p |= ~*s // set the piper flag if the ASCII code is even (80 -> 'P') : // else: p + *s++ & 1 ? // test the parity of (p + current ASCII code) and advance // the string pointer to skip the next body part // if odd: b++ // increment b : // else: a++; // increment a printf("%d %d", a, b); // print the results } //  C (gcc), 72 70 bytes Saved 2 bytes thanks to @mypronounismonicareinstate This version saves the results in an array passed as a 2nd argument. I don't know for sure if it's acceptable. p;f(char*s,int*r){for(*r=r[1]=p=0;*s;s++)*s%3?p|=~*s&1:r[p^*s++&1]++;}  Try it online! • per I/O rules, the second variant is acceptable, but I think you should keep the original entry somewhere in the answer. Feb 6, 2020 at 15:19 • I think r[0] -> *r in the second program. Feb 6, 2020 at 15:23 • suggestion for -2 Bytes TIO – xibu Feb 6, 2020 at 16:25 • The typeless variables are certainly not C++, is that a C feature? Also could you please explain what your code is doing? 🤔🤔🤔 Feb 13, 2020 at 14:40 • @VaradMahashabde There's no such thing as a typeless variable in C, but the default type is int when it's not specified. (Of course, you should not do that in any serious piece of code.) I've added a commented version. You may also see my JS answer for more details about the ASCII code logic. Feb 14, 2020 at 9:04 05AB1E, 17 bytes 'P¡RJ'_K2ô„~oÂ‚¢  Try it online! MATL, 25 bytes 80&Yb"@g'o~'&mXz2eq!Xs]P+  Try it online! • @Arnauld is indeed correct. I.e. o~P~o should result in 0 2 and ~oPo~ should result in 2 0, but they both result in 1 1 in your answer. Feb 6, 2020 at 13:20 • Thanks for noticing! My bad Feb 6, 2020 at 13:54 • Solved now, with a few more bytes Feb 6, 2020 at 14:50 Pyth, 14 bytes /J%2-z\_\~/J\o  Try it online! A port of @GB's wonderful Ruby answer, go give him your upvotes! Terrible explanation: / \~ // Count the occurrences of "~" in: J // J= %2 // Every second character of -z\_ // every character of the input not in "_" // Implicit print /J\o // Count the occurrences of "o" in J // Implicit print  Python 3.8, 62 bytes lambda s:((v:=s.replace("_","")[::2]).count("o"),v.count("~"))  You can try it online. Link has two extra bytes to give a name to the function. Port of this Ruby answer, so be sure to upvote it as well! • Your TIO link shows an error. – xnor Feb 6, 2020 at 15:47 • @xnor thanks for catching that! corrected. – RGS Feb 6, 2020 at 15:50 Burlesque, 11 bytes '_;;\[2enf:  Try it online! Using the algorithm of G B's answer Burlesque, 24 bytes 'P;;^p<-_+'_;;m{2co}FLf:  Try it online! Without error checking. 'P;; # Split at P ^p<- # Reverse those following the piper _+ # Rejoin '_;; # Remove all road m{2co} # Split each into pairs (i.e. mice) FL # Flatten array f: # Count mice  Burlesque, 44 bytes 'P;;sa2!=qpPif^p<-_+'_;;m{2co}FLf:sa2!=qpPif  Try it online! With error checking. As above but with checks to see if lists are correct lengths and throw impossible action if not, halting execution. Jelly, 1413 10 bytes A monadic link taking the string as input. ḟ”_m2ċⱮ⁾~o  You can try it online. Port of this Ruby answer so check it as well! ḟ”_ remove all the underscores from the string m2 Then take a slice modulo 2 and for what remains Ɱ“~o go over the characters ~ and o ċ counting how many times they show up.  Output is [towards, away]. -3 bytes thanks to @Jonathan and @Nick. • You have the wrong quoting instruction on the right (“ should be ⁾). Save two bytes using modulo-indexing by two after dequeuing TIO Feb 6, 2020 at 18:32 • @JonathanAllan thanks! I looked for something like m2 but couldn't find it and so went for my lengthier version :p thanks! – RGS Feb 6, 2020 at 18:51 PHP, 108 bytes for(;$b=$argn[$j++];){$b!='P'?:$p++;$b=='~'?($p&++$j?$m++:$n++):($b!='o'?:($p&++$j?$n++:$m++));}echo"$n$m";


Try it online!

Back to more serious golfing, ends like a port of Noodle9's original answer (but much more readable)

ORIGINAL VERSION:

$a=explode('P',$argn);for($i=2;$i--;$j=0)for($b=$a[$i];$b[$j];){$t=$b[$j].$b[++$j];if($t=='~o')$i&++$j?$n++:$m++;if($t=='o~')$i&++$j?$m++:$n++;}echo"$n $m";  Try it online! Ok, this is really horrible and far from optimal, but I had real fun writing this unreadable atrocity. Forgive me :D (the reason behind the explode was to check validity for original post rule of only one P in the string) EDIT: ORIGINAL VERSION WITH ERROR CHECKING (displays 0 on error) PHP, 213 bytes $a=explode('P',$argn);for($i=0;isset($a[$i]);$j=0,++$i<3?:die(0.))for($b=$a[$i];$b[$j];){strpos(" _~o",$b[$j])?:die(0.);$t=$b[$j].$b[++$j];$t=='~o'?($i&$j++?$m++:$n++):($t!='o~'?:($i&$j++?$n++:$m++));}echo"$n$m";


Try it online!

Caveat: won't display the zero results, but ,$n=0,$m=0 can be added after $i=0 to make it so. GolfScript, 46 bytes 36 bytes 1/{"_"=!},'P'%~-1%+2/.{"~o"=},,.@,\-  Significant improvement from my previous algorithm. Enough so that I'm willing to actually explain it. 1/ #Split the string into an array of chars {"_"=!}, #Replace the prev array with one without underscores 'P'%~ #Split the array at the piper -1% #Reverse the second array (right side) #This has the function of making all correct mice #face the same way, which makes for easier matching + #Concatenate 2/ #Split the array into groups of two #so we only an array of mouse-strings! . #Duplicate our array, since we have to count twice {"~o"=},, #First count all valid mice . #Duplicate that count @, #Bring back that second array and count the mice \- #Subtract (correct mice) from (all mice) #which only leaves (correct mice) then (wrong mice)  PowerShell, 46 42 bytes Inspired by G B $a=,0*7
$args|%{$a[$i++%2+$_%6]++}
$a[4,1]  Try it online!  | ascii | %6 | even | odd ----------------------------- P | 80 | 2 | 2 | 3 _ | 95 | 5 | 5 | 6 o | 111 | 3 | 3 | 4 ~ | 126 | 0 | 0 | 1  The 4 and 1 are odd-numbered positions from the G B answer. No error checking. PowerShell, 46 bytes switch($args|?{$i++%2}){o{$t++}~{$a++}}+$t;+$a  Try it online! • Sorry for casting a down vote; it was an accident! Could you please edit it slightly so that downvote is unlocked? Feb 13, 2020 at 14:53 Unix TMG, 115 bytes p:ignore(<<_>>)r<P>[a=^t][t=^a][a=^t]r parse(d);r:<o~>[t++]r|<~o>[a++]r|();a:0;t:0;d:decimal(t)decimal(a)={2< >1};  Expanded: p:ignore(<<_>>)r<P>[a=^t][t=^a][a=^t]r parse(d); r:<o~>[t++]r|<~o>[a++]r|(); a:0; t:0; d:decimal(t)decimal(a)={2< >1};  Python 3, 86 bytes m=input() n=m.index('P') c=count t=m[:n].c('~o')+m[n:].c('o~') a=m.c('~')-t print(t,a)  Try it online! Takes in the string, and finds the location of the Piper. Then just counts the number of mice behind him facing forwards, plus the number of mice ahead of him facing backwards - this is the total number of mice facing towards him. The number of mice facing away is just the total number of mice (the total number of '~'s) minus the number facing him. Uses c to replace the count function, for byte saving. • Your TIO link gives an error when input is provided. To alias count, you'd need to refer to str.count. – xnor Feb 6, 2020 at 15:49 Java 8, 72 71 bytes s->{int i=1,r[]=new int[2];for(int c:s)r[~c&1]+=c!=95?i^=1:0;return r;}  Port of @GB's Ruby answer, so make sure to upvote him!! -1 byte thanks to @Arnauld. Try it online. Explanation: s->{ // Method with char-array parameter and int-array return-type int i=1, // Toggle integer, starting at 1 r[]=new int[2]; // Result int-array of size 2, filled with 0s by default for(int c:s) // Loop over the input char-array: r[~c&1]+= // Increase the count at index (c+1)%2 by: c!=95? // If the character is NOT '_': i^=1 // Toggle the integer (1→0 or 0→1), and add that to the current count // this will only add 1 for every odd-indexed character, ignoring '_' : // Else: 0; // Keep the count the same by increasing with 0 return r;} // And finally return this resulting pair of counts  K (ngn/k), 18 bytes {+/2!0N 2#0,x^"_"}  Try it online! { } function with argument x x^"_" remove all _s 0, prepend a 0 0N 2# split in pairs 2! mod 2 as ascii codes: P→80→0, o→111→1, ~→126→0 +/ sum C++ (gcc), 189184 170 bytes #include <string> struct a{int t;int f;};a f(std::string s){a r={0,0};for(int i=0,d=1;i<s.length();++i)s[i]%3?s[i]%2?1:d=0:s[i++]%2?d?r.f++:r.t++:d?r.t++:r.f++;return r;}  Try it online! Expanded Version (Full Program) : // Example program #include <iostream> #include <string.h> using namespace std; struct pseudo_arr {int to_piper; int from_piper;}; pseudo_arr countRats(string s) { pseudo_arr rats = {0,0}; int direction = 1; for(auto i = s.cbegin();i!=s.cend();++i) { // Exploitable properties of the 4 characters // 'P' == 80 - %3 == 2, %3 == 0 // '_' == 95 - %3 == 2, %2 == 1 // '~' == 126 - %3 == 0, %2 == 0 // 'o' == 111 - %3 == 0, %2 == 1 switch(*i) { case 'P' : // char(80) == 'P' direction = 0; // Crossed piper, change direction break; case 'o' : // If see head, skip tail ++i; // if before piper then away, else towards direction ? rats.from_piper++ : rats.to_piper++; break; case '~' : // if see tail, skip head ++i; // if before piper then towards, else away direction ? rats.to_piper++ : rats.from_piper++; break; } } return rats; } int main() { string input; getline(cin, input); pseudo_arr rat_count = countRats(input); cout << rat_count.to_piper << " " << rat_count.from_piper; }  • 166 bytes Feb 10, 2020 at 22:01 • @ceilingcat Nice char-int conversion! I should have realised the curly braces on my own 😅. But how come it works with #include <string.h> removed? Feb 13, 2020 at 7:01 • I don’t think you’re using anything in string.h Feb 13, 2020 at 7:09 • @ceilingcat I am using std::string, but for some implementations <string> is included inside <iostream>. But not all. I could save a couple by removing the .h Feb 13, 2020 at 10:09 Z80Golf, 28 bytes 00000000: 1680 cd03 8030 0578 ff79 ff76 fe5f 28f0 .....0.x.y.v._(. 00000010: 1930 ed0c fe7e 28e8 0d04 18e4 .0...~(.....  Try it online! start: ld d,$80
call $8003 jr nc, ok results: ld a, b rst$38
ld a, c
rst $38 halt ok: cp '_' jr z, start add hl, de jr nc, start inc c cp '~' jr z, start dec c inc b jr start  Prints two bytes: towards and away. One maybe-clever trick here is using add hl, de as a sort of flip-flop mechanism. Since de is fixed to $8000, it will overflow and set the carry flag every other time.