40
\$\begingroup\$

This challenge is NinjaBearMonkey's prize for winning my Block Building Bot Flocks! challenge with the Black Knight submission. Congratulations NinjaBearMonkey!

The challenge here is fairly simple, but has a variety of possible approaches. The story goes that in the world of Isometric Illusions, there are 6 different types of creatures:

  1. Ninjas, abbreviated N
  2. Bears, abbreviated B
  3. Monkeys, abbreviated M
  4. NinjaBears, abbreviated NB
  5. BearMonkeys, abbreviated BM
  6. NinjaBearMonkeys, abbreviated NBM

(NinjaBearMonkey is, of course, the last, most powerful type.)

Your task is to take a census of these creatures when they are lined up side-by-side, i.e. when their abbreviation strings are concatenated. The caveat is that you need to make sure not to over-count the parts of some creatures as separate creatures that happen to look similar. The creatures will line up such that:

  • Any instance of NBM is 1 NinjaBearMonkey and 0 other creatures.
  • Any instance of NB not followed by M is 1 NinjaBear and 0 other creatures.
  • Any instance of BM not preceded by N is 1 BearMonkey and 0 other creatures.
  • Otherwise, instances of N, B, and M are single Ninjas, Bears, and Monkeys respectively.

The line is read from left to right.

So, for example, in the line of creatures NBMMBNBNBM, there are 0 Ninjas, 1 Bear, 1 Monkey, 1 NinjaBear, 0 BearMonkeys, and 2 NinjaBearMonkeys.

Challenge

Write a program or function that takes in a string of the characters N, B, and M, and prints or returns how many of each of the 6 types of creatures are present in it.

The output should have the form

#N #B #M #NB #BM #NBM

with the respective creature count replacing each # sign. All 6 counts must be shown, separated by spaces, even when they are 0. However, they may be in any order (e.g. #NBM could come first).

Also:

  • The input string will only contain the characters N, B, and M.
  • If the empty string is input, then all the counts are 0.
  • The output may optionally contain a single leading and/or trailing space, and/or a single trailing newline.

The shortest submission in bytes wins.

Examples

Input: NB
Output: 0N 0B 0M 1NB 0BM 0NBM

Input: NBM
Output: 0N 0B 0M 0NB 0BM 1NBM

Input: NBMMBNBNBM (example from above)
Output: 0N 1B 1M 1NB 0BM 2NBM

Input: MBNNBBMNBM
Output: 1N 1B 1M 1NB 1BM 1NBM

Input: NNNMNBMMBMMBMMMNBMNNMNNNBNNNBNBBNBNMMNBBNBMMBBMBMBBBNNMBMBMMNNNNNMMBMMBM
Output: 17N 6B 14M 5NB 8BM 3NBM

\$\endgroup\$
3
  • 57
    \$\begingroup\$ I approve this challenge. \$\endgroup\$ Jun 18, 2015 at 1:51
  • \$\begingroup\$ Just to confirm: if all you had was 2 NinjaBearMonkeys, you can't form a line? Because they can't stand next to each other? \$\endgroup\$ Jun 18, 2015 at 7:12
  • 3
    \$\begingroup\$ @AlanCampbell No. NBMNBM would be perfectly valid input. Reading it from left to right there are clearly 2 NinjaBearMonkeys. \$\endgroup\$ Jun 18, 2015 at 8:50

28 Answers 28

23
\$\begingroup\$

JavaScript ES6, 86 bytes

f=s=>'NBM BM NB M B N'.replace(/\S+/g,e=>(i=0,s=s.replace(RegExp(e,'g'),_=>++i))&&i+e)

(I just had to answer this.) It goes through each substring of NBM, starting with the longer ones, which take higher priority. It searches for each occurrence of that particular string and removes it (in this case replacing it with the current count so it won't be matched again). It finally replaces each substring with the count + the string.

This Stack Snippet is written in the ES5 equivalent of the above code to make it easier to test from any browser. It is also slightly ungolfed code. The UI updates with every keystroke.

f=function(s){
  return'NBM BM NB M B N'.replace(/\S+/g,function(e){
    i=0
    s=s.replace(RegExp(e,'g'),function(){
      return++i
    })
    return i+e
  })
}

run=function(){document.getElementById('output').innerHTML=f(document.getElementById('input').value)};document.getElementById('input').onkeyup=run;run()
<input type="text" id="input" value="NBMMBNBNBM" /><br /><samp id="output"></samp>

\$\endgroup\$
2
  • \$\begingroup\$ Could you change the regex part to 'NBM<newline>BM<newline>...<newline>N'.replace(/./g, ...)', where the <newline>s are literal newlines and the 's are backticks, forming an ES6 template string? Saves two bytes in the regex (. doesn't match newlines). \$\endgroup\$
    – wchargin
    Jun 22, 2015 at 4:34
  • \$\begingroup\$ @WChargin Unfortunately, no, because the output must be space-separated. \$\endgroup\$ Jun 22, 2015 at 17:50
20
\$\begingroup\$

Pyth, 22 bytes

 f|pd+/zTT=:zTd_.:"NBM

Quite hackish way to save 1 byte, thanks to @Jakube.


Pyth, 23 bytes

FN_.:"NBM")pd+/zNN=:zNd

Demonstration.

Prints in reverse order, with a trailing space and no trailing newline.

.:"NBM") is all the substrings, _ puts them in the right order, /zN counts occurences, and =:zNd in-place substitutes each occurence of the string in question with a space.

FN_.:"NBM")pd+/zNN=:zNd
FN                         for N in                            :
  _                                 reversed(                 )
   .:     )                                  substrings(     )
     "NBM"                                              "NBM"
           pd              print, with a space at the end,
              /zN          z.count(N)
             +   N                    + N
                  =:zNd    replace N by ' ' in z.
\$\endgroup\$
0
17
\$\begingroup\$

Python 2, 78

n=input()
for x in"NBM BM NB M B N".split():n=`n`.split(x);print`len(n)-1`+x,

A variant of Vioz-'s answer. Fun with Python 2 string representations!

Counts occurrences of the substring indirectly by splitting on it, counting the parts, and subtracting 1. Instead of replacing the substrings by a filler symbol, replaces the string by the list that split produced. Then, when we take its string representation, the parts are separated by spaces and commas.

\$\endgroup\$
2
  • 5
    \$\begingroup\$ That's insane! Excellently insane, but still insane. \$\endgroup\$
    – Sp3000
    Jun 18, 2015 at 10:43
  • \$\begingroup\$ Nice work! Didn't think of that :) \$\endgroup\$
    – Kade
    Jun 18, 2015 at 11:42
14
\$\begingroup\$

Ruby, 166 80 72 68 characters

f=->s{%w(NBM BM NB M B N).map{|t|c=0;s.gsub!(t){c+=1};c.to_s+t}*' '}

Explanation:

  • The counting is done in reverse. This is because the longer ninjas and bears and monkeys take precedence over the shorter ones.

  • For NBM, BM, and NB, the sequences are gsub!'d out of the original string with a block to count how many of these sequences exist (yes, the function modifies its argument).

    • However, they can't be replaced with nothing, since otherwise BNBMM would be counted as NBM and BM instead of B, NBM, and M (because when the NBM would be removed, it would put the B and M together and there wouldn't be a way to distinguish it). Originally I returned a single character string (.gsub!('NBM'){c+=1;?|}), but I realized I could just return the result of the += (which is a number, so it can't be any of N B M).
  • For M, B, and N, I can just count how many of them there are in the string (no need to remove them via gsub!). Now it's a loop (don't know why I didn't think of that in the first place), so these are done the same way.


Similar solution in Ostrich, 54 51 chars:

:s;`NBM BM NB M B N`" /{:t0:n;s\{;n):n}X:s;nt+}%" *

Unfortunately not a valid solution, as there is a bug in the current Ostrich version (that is now fixed, but after this challenge was posted).

\$\endgroup\$
3
  • \$\begingroup\$ You can save 3 chars by using the array notation %w(NBM BM NB M B N) and removing the split. \$\endgroup\$
    – DickieBoy
    Jun 18, 2015 at 9:34
  • \$\begingroup\$ @DickieBoy That's actually 4 chars; thanks! \$\endgroup\$
    – Doorknob
    Jun 18, 2015 at 9:59
  • \$\begingroup\$ Ah yes, the dot! \$\endgroup\$
    – DickieBoy
    Jun 18, 2015 at 10:00
14
\$\begingroup\$

Java, 166 162

void f(String a){String[]q="NBM-NB-BM-N-B-M".split("-");for(int i=0,c;i<6;System.out.print(c+q[i++]+" "))for(c=0;a.contains(q[i]);c++)a=a.replaceFirst(q[i],".");}

And with a few line breaks:

void f(String a){
    String[]q="NBM-NB-BM-N-B-M".split("-");
    for(int i=0,c;i<6;System.out.print(c+q[i++]+" "))
        for(c=0;a.contains(q[i]);c++)
            a=a.replaceFirst(q[i],".");
}

It works pretty simply. Just loop over the tokens, replacing them with dots and counting as long as the input contains some. Counts the big ones first, so the little ones don't mess it up.

I originally tried replacing all at once and counting the difference in length, but it took a few more characters that way :(

\$\endgroup\$
5
  • 2
    \$\begingroup\$ As a Java dev, I want to make this shorter and see Java win for a change. After staring at it for awhile I have yet to find a way to make it shorter. \$\endgroup\$
    – DeadChex
    Jun 18, 2015 at 18:40
  • 1
    \$\begingroup\$ Well it definitely isn't going to win overall. The current leader is 22 bytes and there's just no way to do anything meaningful in Java in that size. My println statement alone is larger than that. I'm satisfied with it, though :D \$\endgroup\$
    – Geobits
    Jun 18, 2015 at 18:57
  • 1
    \$\begingroup\$ I'm a bit late but I found a way... change String q[]= to String[]q= \$\endgroup\$
    – DeadChex
    Jun 23, 2015 at 16:14
  • 1
    \$\begingroup\$ Nice! Can't believe I missed that, it's on my standard list of things to look at :) \$\endgroup\$
    – Geobits
    Jun 23, 2015 at 17:07
  • \$\begingroup\$ I only discovered it after trying to get into Code Golf as a JavaDev, I'm quite surprised at some of the stuff you can do \$\endgroup\$
    – DeadChex
    Jun 23, 2015 at 17:14
11
\$\begingroup\$

CJam, 36 32 31 bytes

l[ZYX]"NBM"few:+{A/_,(A+S@`}fA;

Thanks to @Optimizer for golfing off 1 byte.

Try it online in the CJam interpreter.

How it works

l                                e# Read a line L from STDIN.
 [ZYX]"NBM"                      e# Push [3 2 1] and "NBM".
           few                   e# Chop "NBM" into slices of length 3 to 1.
              :+                 e# Concatenate the resulting arrays of slices.
                {          }fA   e# For each slice A:
                 A/              e#   Split L at occurrences of A.
                   _,(           e#   Push the numbers of resulting chunks minus 1.
                      A+         e#   Append A.
                        S        e#   Push a space.
                         @`      e#   Push a string representation of the split L.
                              ;  e# Discard L.
\$\endgroup\$
2
  • \$\begingroup\$ N* -> ` should be enough. \$\endgroup\$
    – Optimizer
    Jun 18, 2015 at 11:53
  • \$\begingroup\$ @Optimize: That works well. Thanks. \$\endgroup\$
    – Dennis
    Jun 18, 2015 at 14:18
10
\$\begingroup\$

Taxi, 6284 bytes

Go to Post Office:W 1 L 1 R 1 L.Pickup a passenger going to Chop Suey.Go to Chop Suey:N 1 R 1 L 4 R 1 L.[A]Switch to plan N if no one is waiting.Pickup a passenger going to Cyclone.N is waiting at Writer's Depot.Go to Zoom Zoom:N 1 L 3 R.Go to Writer's Depot:W.Pickup a passenger going to Crime Lab.Go to Cyclone:N.Pickup a passenger going to Cyclone.Go to Cyclone:N 1 R 1 R 1 R.Pickup a passenger going to Cyclone.Pickup a passenger going to Cyclone.Go to Cyclone:N 1 R 1 R 1 R.Pickup a passenger going to Tom's Trims.Pickup a passenger going to Tom's Trims.Go to Tom's Trims:S 1 L 2 R 1 L.Go to Cyclone:S 1 R 1 L 2 R.Pickup a passenger going to Crime Lab.Pickup a passenger going to Tom's Trims.Go to Tom's Trims:S 1 L 2 R 1 L.Go to Crime Lab:S 1 L 1 L.Switch to plan F if no one is waiting.Pickup a passenger going to Auctioneer School.Go to Rob's Rest:S 1 R 1 L 1 L 1 R 1 R.Switch to plan D if no one is waiting.Go to Bird's Bench:S.Switch to plan B if no one is waiting.Go to Rob's Rest:N.Pickup a passenger going to KonKat's.Go to Bird's Bench:S.Pickup a passenger going to KonKat's.Go to KonKat's:N 1 R 1 L 2 R 1 R 2 R.Pickup a passenger going to Sunny Skies Park.Go to Sunny Skies Park:N 1 L 2 L 1 L.Go to Auctioneer School:S.Pickup a passenger going to Rob's Rest.Go to Rob's Rest:N 2 L 1 R.Go to Cyclone:S 1 L 1 L 2 L.Switch to plan C.[B]Go to Auctioneer School:N 1 R 1 R.Pickup a passenger going to Sunny Skies Park.Go to Sunny Skies Park:N.Go to Cyclone:N 1 L.[C]Pickup a passenger going to Riverview Bridge.Go to Riverview Bridge:N 2 R.Go to Chop Suey:E 2 R.Switch to plan A.[D]Go to Bird's Bench:S.Switch to plan E if no one is waiting.Pickup a passenger going to Sunny Skies Park.[E]Go to Auctioneer School:N 1 R 1 R.Pickup a passenger going to Rob's Rest.Go to Rob's Rest:N 2 L 1 R.Go to Sunny Skies Park:S 1 L 1 L.Go to Cyclone:N 1 L.Switch to plan C.[F]B is waiting at Writer's Depot.Go to Writer's Depot:S 1 R 1 L 2 L.Pickup a passenger going to Crime Lab.Go to Cyclone:N.Pickup a passenger going to Cyclone.Go to Cyclone:N 1 R 1 R 1 R.Pickup a passenger going to Crime Lab.Go to Crime Lab:S 1 L 2 R 2 L.Switch to plan J if no one is waiting.Pickup a passenger going to Auctioneer School.Go to Bird's Bench:S 1 R 1 L 1 L 1 R 1 L.Switch to plan I if no one is waiting.Go to Rob's Rest:N.Switch to plan G if no one is waiting.Pickup a passenger going to KonKat's.[G]Go to Bird's Bench:S.Pickup a passenger going to KonKat's.Go to KonKat's:N 1 R 1 L 2 R 1 R 2 R.Pickup a passenger going to Sunny Skies Park.Go to Sunny Skies Park:N 1 L 2 L 1 L.Go to Auctioneer School:S.[H]Pickup a passenger going to Bird's Bench.Go to Bird's Bench:N 2 L 1 L.Go to Cyclone:N 1 R 1 L 2 L.Switch to plan C.[I]Go to Auctioneer School:N 1 R 1 R.Switch to plan H.[J]Go to Bird's Bench:S 1 R 1 L 1 L 1 R 1 L.Switch to plan L if no one is waiting.Go to Rob's Rest:N.Switch to plan K if no one is waiting.Pickup a passenger going to KonKat's.[K]Go to Bird's Bench:S.Pickup a passenger going to KonKat's.[L]Go to Cyclone:N 1 R 1 L 2 L.Pickup a passenger going to KonKat's.Go to KonKat's:N 2 R 2 R.Pickup a passenger going to Sunny Skies Park.Go to Rob's Rest:S 4 R 1 L 1 L 1 R 1 R.Switch to plan M if no one is waiting.Pickup a passenger going to Sunny Skies Park.[M]Go to Sunny Skies Park:S 1 L 1 L.Go to Chop Suey:N 1 R 1 R 3 R.Switch to plan A.[N]Go to Rob's Rest:N 1 L 3 L 1 L 2 R 1 R.Switch to plan O if no one is waiting.Pickup a passenger going to KonKat's.[O]Go to Bird's Bench:S.Switch to plan P if no one is waiting.Pickup a passenger going to KonKat's.[P]Go to KonKat's:N 1 R 1 L 2 R 1 R 2 R.Switch to plan Q if no one is waiting.Pickup a passenger going to Sunny Skies Park.[Q]Go to Sunny Skies Park:N 1 L 2 L 1 L.N is waiting at Writer's Depot.B is waiting at Writer's Depot.M is waiting at Writer's Depot.NB is waiting at Writer's Depot.BM is waiting at Writer's Depot.NBM is waiting at Writer's Depot.Go to Writer's Depot:N 1 L.Pickup a passenger going to Joyless Park.Pickup a passenger going to Joyless Park.Pickup a passenger going to Joyless Park.Go to Joyless Park:N 3 R 2 R 2 L.Go to Writer's Depot:W 1 R 2 L 2 L.Pickup a passenger going to Joyless Park.Pickup a passenger going to Joyless Park.Pickup a passenger going to Joyless Park.Go to Joyless Park:N 3 R 2 R 2 L.[R]Switch to plan Y if no one is waiting.Pickup a passenger going to Cyclone.0 is waiting at Starchild Numerology.Go to Starchild Numerology:W 1 L 2 R 1 L 1 L 2 L.Pickup a passenger going to Addition Alley.Go to Cyclone:W 1 R 4 L.[S]Pickup a passenger going to Auctioneer School.Go to Zoom Zoom:N.Go to Auctioneer School:W 2 L.Go to Sunny Skies Park:N.Switch to plan V if no one is waiting.Pickup a passenger going to Cyclone.Go to Cyclone:N 1 L.Pickup a passenger going to Crime Lab.Pickup a passenger going to Crime Lab.Go to Crime Lab:S 1 L 2 R 2 L.Switch to plan T if no one is waiting.Pickup a passenger going to Tom's Trims.Go to Tom's Trims:N 1 L 1 L.1 is waiting at Starchild Numerology.Go to Starchild Numerology:S 1 R 1 L 1 L 2 L.Pickup a passenger going to Addition Alley.Go to Addition Alley:W 1 R 3 R 1 R 1 R.Pickup a passenger going to Addition Alley.Go to Cyclone:N 1 L 1 L.Pickup a passenger going to Riverview Bridge.Go to Riverview Bridge:N 2 R.Go to Auctioneer School:W 2 L 1 L.Switch to plan U.[T]Go to Cyclone:N 4 L 2 L.Pickup a passenger going to Narrow Path Park.Go to Narrow Path Park:N 2 R 1 L 1 R.Go to Auctioneer School:W 1 L 1 R 2 L 1 L.[U]Pickup a passenger going to Cyclone.Go to Cyclone:N 4 L.Switch to plan S.[V]Go to Addition Alley:N 1 R 1 R 1 R.Pickup a passenger going to The Babelfishery.Go to The Babelfishery:N 1 R 1 R.Pickup a passenger going to Post Office.Go to Cyclone:N 1 L 1 L 2 R.Pickup a passenger going to Post Office." " is waiting at Writer's Depot.Go to Writer's Depot:S.Pickup a passenger going to Post Office.Go to Post Office:N 1 R 2 R 1 L.Go to Auctioneer School:S 1 R 1 L 1 L.Pickup a passenger going to Tom's Trims.Go to Narrow Path Park:N 3 R 1 R 1 L 1 R.[W]Switch to plan X if no one is waiting.Pickup a passenger going to Sunny Skies Park.Go to Sunny Skies Park:W 1 L 1 R 2 L 1 L.Go to Zoom Zoom:N 1 R.Go to Narrow Path Park:W 1 L 1 L 1 R.Switch to plan W.[X]Go to Tom's Trims:E 1 R 4 R 1 L.Go to Joyless Park:N 1 R 1 L 3 R.Switch to plan R.[Y]

Try it online!

This ain't winning me any Code Golf competitions, but this was a nice reminder for me not to write any more complex programs in Taxi. (Sooo many special cases, and sooo much gas management...)

A more commented version can be found here, if you dare.

\$\endgroup\$
7
\$\begingroup\$

R, 153 134 118

This got longer really quickly, but hopefully I'll be able to shave a few. Input is STDIN and output to STDOUT.

Edit Change of tack. Got rid of the split string and counting parts. Now I replace the parts with a string one shorter than the part. The difference between the string lengths is collected for output.

N=nchar;i=scan(,'');for(s in scan(,'',t='NBM BM NB M B N'))cat(paste0(N(i)-N(i<-gsub(s,strtrim('  ',N(s)-1),i)),s),'')

Explanation

N=nchar;
i=scan(,'');                     # Get input from STDIN
for(s in scan(,'',t='NBM BM NB M B N'))  # Loop through patterns
  cat(                           # output
    paste0(                      # Paste together
      N(i) -                     # length of i minus
      N(i<-gsub(                 # length of i with substitution of
        s,                       # s
        strtrim('  ',N(s)-1)     # with a space string 1 shorter than s
        ,i)                      # in i
      ),
      s)                         # split string
  ,'')

Test run

> N=nchar;i=scan(,'');for(s in scan(,'',t='NBM BM NB M B N'))cat(paste0(N(i)-N(i<-gsub(s,strtrim('  ',N(s)-1),i)),s),'')
1: NNNMNBMMBMMBMMMNBMNNMNNNBNNNBNBBNBNMMNBBNBMMBBMBMBBBNNMBMBMMNNNNNMMBMMBM
2: 
Read 1 item
Read 6 items
3NBM 8BM 5NB 14M 6B 17N 
> N=nchar;i=scan(,'');for(s in scan(,'',t='NBM BM NB M B N'))cat(paste0(N(i)-N(i<-gsub(s,strtrim('  ',N(s)-1),i)),s),'')
1: NBMMBNBNBM
2: 
Read 1 item
Read 6 items
2NBM 0BM 1NB 1M 1B 0N 
> 
\$\endgroup\$
0
7
\$\begingroup\$

Pyth, 19 bytes

jd+Ltl=zc`zd_.:"NBM

This is a mixture of @isaacg's Pyth solution and @xnor's incredible Python trick.

Try it online: Demonstration or Test harness

Explanation

jd+Ltl=zc`zd_.:"NBM   implicit: z = input string
             .:"NBM   generate all substrings of "NBM"
            _         invert the order
  +L                  add left to each d in ^ the following:
         `z             convert z to a string
        c  d            split at d
      =z                assign the resulting list to z
    tl                  length - 1
jd                    join by spaces and implicit print
\$\endgroup\$
6
\$\begingroup\$

Julia, 106 97 bytes

b->for s=split("NBM BM NB M B N") print(length(matchall(Regex(s),b)),s," ");b=replace(b,s,".")end

This creates an unnamed function that takes a string as input and prints the result to STDOUT with a single trailing space and no trailing newline. To call it, give it a name, e.g. f=b->....

Ungolfed + explanation:

function f(b)
    # Loop over the creatures, biggest first
    for s = split("NBM BM NB M B N")

        # Get the number of creatures as the count of regex matches
        n = length(matchall(Regex(s), b))

        # Print the number, creature, and a space
        print(n, s, " ")

        # Remove the creature from captivity, replacing with .
        b = replace(b, s, ".")
    end
end

Examples:

julia> f("NBMMBNBNBM")
2NBM 0BM 1NB 1M 1B 0N 

julia> f("NNNMNBMMBMMBMMMNBMNNMNNNBNNNBNBBNBNMMNBBNBMMBBMBMBBBNNMBMBMMNNNNNMMBMMBM")
3NBM 8BM 5NB 14M 6B 17N 
\$\endgroup\$
4
\$\begingroup\$

Python 2, 93 88 89 84 Bytes

Taking the straightforward approach.

def f(n):
 for x in"NBM BM NB M B N".split():print`n.count(x)`+x,;n=n.replace(x,"+")

Call like so:

f("NBMMBNBNBM")

Output is like so:

2NBM 0BM 1NB 1M 1B 0N
\$\endgroup\$
2
  • \$\begingroup\$ You can remove the space after in. \$\endgroup\$
    – isaacg
    Jun 18, 2015 at 4:59
  • \$\begingroup\$ In Python 2, you can convert to a string representation with `x`. \$\endgroup\$
    – xnor
    Jun 18, 2015 at 8:28
4
\$\begingroup\$

SAS, 144 142 139 129

data;i="&sysparm";do z='NBM','NB','BM','N','B','M';a=count(i,z,'t');i=prxchange(cats('s/',z,'/x/'),-1,i);put a+(-1)z@;end;

Usage (7 bytes added for sysparm):

$ sas -stdio -sysparm NNNMNBMMBMMBMMMNBMNNMNNNBNNNBNBBNBNMMNBBNBMMBBMBMBBBNNMBMBMMNNNNNMMBMMBM << _S
data;i="&sysparm";do z='NBM','NB','BM','N','B','M';a=count(i,z,'t');i=prxchange(cats('s/',z,'/x/'),-1,i);put a+(-1)z@;end;
_S

or

%macro f(i);i="&i";do z='NBM','NB','BM','N','B','M';a=count(i,z,'t');i=prxchange(cats('s/',z,'/x/'),-1‌​,i);put a+(-1)z@;end;%mend;

Usage:

data;%f(NNNMNBMMBMMBMMMNBMNNMNNNBNNNBNBBNBNMMNBBNBMMBBMBMBBBNNMBMBMMNNNNNMMBMMBM)

Result:

3NBM 5NB 8BM 17N 6B 14M
\$\endgroup\$
9
  • \$\begingroup\$ You can save a couple bytes using cats('s/',z,'/x/') in place of 's/'||strip(z)||'/x/'. \$\endgroup\$
    – Alex A.
    Jun 18, 2015 at 3:59
  • 1
    \$\begingroup\$ Nice, that was quite a trip back to 139 :) \$\endgroup\$
    – Fried Egg
    Jun 18, 2015 at 4:16
  • 1
    \$\begingroup\$ 126 bytes: macro a i="&sysparm";do z='NBM','NB','BM','N','B','M';a=count(i,z,'t');i=prxchange(cats('s/',z,'/x/'),-1,i);put a+(-1)z@;end;% \$\endgroup\$
    – Alex A.
    Jun 18, 2015 at 14:51
  • 1
    \$\begingroup\$ 122: data;i="&sysparm";do z='NBM','NB','BM','N','B','M';a=count(i,z,'t');i=prxchange(cats('s/',z,'/x/'),-1,i);put a+(-1)z@;end;. Since you're already reading from sysparm, you may as well just run it as a data step. And if you're running in batch, you don't need run;. \$\endgroup\$
    – Alex A.
    Jun 18, 2015 at 17:19
  • 1
    \$\begingroup\$ But you could get 129 by using a modern style macro which doesn't read from the command line argument: %macro a(i);i="&i";do z='NBM','NB','BM','N','B','M';a=count(i,z,'t');i=prxchange(cats('s/',z,'/x/'),-1,i);put a+(-1)z@;end;%mend; \$\endgroup\$
    – Alex A.
    Jun 18, 2015 at 17:32
3
\$\begingroup\$

PHP4.1, 92 bytes

Not the shortest one, but what else would you expect from PHP?

To use it, set a key on a COOKIE, POST, GET, SESSION...

<?foreach(split(o,NBMoNBoBMoMoBoN)as$a){echo count($T=split($a,$S))-1,"$a ";$S=join('',$T);}

The apporach is basic:

  • Split the string into the names of the creatures
  • Count how many elements there are
  • Subtract 1 (an empty string would give an array with 1 element)
  • Output the count and the creature name
  • Join it all together, using an empty string (which will reduce the string and remove the last creature)

Easy, right?

\$\endgroup\$
3
\$\begingroup\$

Perl, 46

#!perl -p
$_="NBM BM NB M B N"=~s/\w+/~~s!$&!x!g.$&/ger
\$\endgroup\$
1
  • \$\begingroup\$ Explanation on how this works? \$\endgroup\$
    – Cain
    Jun 18, 2015 at 21:39
2
\$\begingroup\$

JavaScript, 108 116 bytes

Just a straight forward approach, nothing fancy

o="";r=/NBM|NB|BM|[NMB]/g;g={};for(k in d=(r+prompt()).match(r))g[d[k]]=~-g[d[k]];for(k in g)o+=~g[k]+k+" ";alert(o);
\$\endgroup\$
2
  • 1
    \$\begingroup\$ Does not work: All 6 counts must be shown, separated by spaces, even when they are 0.. Test case: N \$\endgroup\$
    – edc65
    Jun 18, 2015 at 13:16
  • \$\begingroup\$ @edc65 Woah. I just missed that part. Thanks for pointing that out. Fixed it for the cost of 8chars \$\endgroup\$
    – C5H8NNaO4
    Jun 18, 2015 at 13:37
1
\$\begingroup\$

SpecBAS - 164

1 INPUT s$
2 FOR EACH a$ IN ["NBM","BM","NB","M","B","N"]
3 LET n=0
4 IF POS(a$,s$)>0 THEN INC n: LET s$=REPLACE$(s$,a$,"-"): GO TO 4: END IF
5 PRINT n;a$;" ";
6 NEXT a$

Uses the same approach as a lot of others. Line 4 keeps looping over the string (from largest first), replaces it if found.

SpecBAS has some nice touches over original ZX/Sinclair BASIC (looping through lists, finding characters) which I'm still finding out.

\$\endgroup\$
1
\$\begingroup\$

C, 205 186 184 bytes

A little different approach based on state machine. where t is the state.

a[7],t,i;c(char*s){do{i=0;t=*s==78?i=t,1:*s-66?*s-77?t:t-4?t-2?i=t,3:5:6:t-1?i=t,2:4;i=*s?i:t;a[i]++;}while(*s++);printf("%dN %dB %dM %dNB %dBM %dNBM",a[1],a[2],a[3],a[4],a[5],a[6]);}

Expanded

int a[7],t,i;

void c(char *s)
{
    do {
        i = 0;
        if (*s == 'N') {
            i=t; t=1;
        }
        if (*s == 'B') {
            if (t==1) {
                t=4;
            } else {
                i=t;
                t=2;
            }
        }
        if (*s == 'M') {
            if (t==4) {
                t=6;
            } else if (t==2) {
                t=5;
            } else {
                i=t;
                t=3;
            }
        }
        if (!*s)
            i = t;
        a[i]++;
    } while (*s++);
    printf("%dN %dB %dM %dNB %dBM %dNBM",a[1],a[2],a[3],a[4],a[5],a[6]);
}

Test function

#include <stdio.h>
#include <stdlib.h>

/*
 * 0 : nothing
 * 1 : N
 * 2 : B
 * 3 : M
 * 4 : NB
 * 5 : BM
 * 6 : NBM
 */
#include "nbm-func.c"

int main(int argc, char **argv)
{
    c(argv[1]);
}
\$\endgroup\$
2
  • \$\begingroup\$ Wouldn't using for(;;*s++){...} instead of do{...}while(*s++); save some bytes? Also, you don't need the newline character in the printf. \$\endgroup\$
    – Spikatrix
    Jun 18, 2015 at 8:22
  • \$\begingroup\$ I think you meant for(;*s;s++). But I needed to loop with that last null character. Good call on saving the \n, which is not required. \$\endgroup\$
    – some user
    Jun 18, 2015 at 15:19
1
\$\begingroup\$

C, 146

f(char*s)
{
  char*p,*q="NBM\0NB\0BM\0N\0B\0M",i=0,a=2;
  for(;i<6;q+=a+2,a=i++<2)
  {
    int n=0;
    for(;p=strstr(s,q);++n)*p=p[a>1]=p[a]=1;
    printf("%d%s ",n,q);
  }
}

// Main function, just for testing
main(c,a)char**a;{
  f(a[1]);
}  
\$\endgroup\$
1
\$\begingroup\$

Haskell - 177 bytes (without imports)

n s=c$map(\x->(show$length$filter(==x)(words$c$zipWith(:)s([f(a:[b])|(a,b)<-zip s(tail s)]++[" "])))++x++" ")l
f"NB"=""
f"BM"=""
f p=" "
l=["N","B","M","NB","BM","NBM"]
c=concat

(Sorry for the internet necromancy here.)

The Haskell Platform doesn't have string search without imports, and I wanted to show off and exploit the fact that the searched strings are all substrings of one (without repetitions), so that grouping characters can be done by identifying pairs that are allowed to follow each other, which is what f does here.

I still need the full list l in the end to check for equality and display exactly as required, but would not, had the challenge only been to report the number of occurrences of the possible words in any order.

\$\endgroup\$
1
\$\begingroup\$

Vyxal, 18 bytes

`NBM`KṘ(n/₅‹n+¨…_∑

Try it Online!

`NBM`              # 'NBM'
     K             # Substrings
      Ṙ(           # Iterate over
        n/         # Split current string by current substring of NBM
          ₅‹       # Duplicate and decrement length
            np¨…_  # Prepend current substring of NBM and output with a trailing space
                 ∑ # Concatenate for next iteration
\$\endgroup\$
0
1
\$\begingroup\$

Husk, 27 bytes

;zS`+ös←Lx₁GoΣ`x⁰₁
↔ÖLQ"NBM

Try it online!

uses the splitting technique from the Vyxal answer.

Husk, 28 bytes

m§+osL←k€₁Ẋ`-U¡Ṡ-(→n₁ḣ
Q"NBM

Try it online!

Funny set intersection based idea.

\$\endgroup\$
1
\$\begingroup\$

Japt -S, 18 bytes

Outputs in reverse order.

"NBM"ã ÔËiUèDU=rDS

Try it (footer reverses the output)

"NBM"ã ÔËiUèDU=rDS     :Implicit input of string U
"NBM"ã                 :Substrings of "NBM"
       Ô               :Reverse
        Ë              :Map each D
         i             :  Prepend
          UèD          :    Count of D in U
             U=        :    Reassign to U*
               rD      :      Replace D with
                 S     :      Space
                       :Implicit output joined with spaces

*This is done within the second argument of the è method but as that only expects 1 argument, it's result is ignored within the mapping & prepending.

\$\endgroup\$
1
\$\begingroup\$

Excel, 149 145 bytes

Saved 4 bytes by realizing I can directly use LEN(a) after I rewrote the function from my first - never submitted - draft.

=LET(a,SUBSTITUTE(SUBSTITUTE(SUBSTITUTE(A1,"NBM",1),"BM",2),"NB",3),LEN(a)-LEN(SUBSTITUTE(a,{"N","B","M",3,2,1},))&{"N","B","M","NB","BM","NBM"})

Start by replacing NBM with 1 then BM with 2 and NB with 3. Now we have a string that is as many characters long as they are creatures. One by one, we check the difference between that string's length and it's length with one of the creatures removed. Output is an array of cells.

Screenshot

\$\endgroup\$
0
\$\begingroup\$

Bash - 101

I=$1
for p in NBM BM NB M B N;{ c=;while [[ $I =~ $p ]];do I=${I/$p/ };c+=1;done;echo -n ${#c}$p\ ;}

Pass the string as the first argument.

bash nmb.sh MBNNBBMNBM 

Explained a bit:

# We have to save the input into a variable since we modify it.
I=$1

# For each pattern (p) in order of precedence
for p in NBM BM NB M B N;do
    # Reset c to an empty string
    c=

    # Regexp search for pattern in string
    while [[ $I =~ $p ]];do
        # Replace first occurance of pattern with a space
        I=${I/$p/ }
        # Append to string c. the 1 is not special it could be any other
        # single character
        c+=1
    done

    # -n Suppress's newlines while echoing
    # ${#c} is the length on the string c
    # Use a backslash escape to put a space in the string.
    # Not using quotes in the golfed version saves a byte.
    echo -n "${#c}$p\ "
done
\$\endgroup\$
0
\$\begingroup\$

rs, 275 bytes

(NBM)|(NB)|(BM)|(N)|(B)|(M)/a\1bc\2de\3fg\4hi\5jk\6l
[A-Z]+/_
#
+(#.*?)a_b/A\1
+(#.*?)c_d/B\1
+(#.*?)e_f/C\1
+(#.*?)g_h/D\1
+(#.*?)i_j/E\1
+(#.*?)k_l/F\1
#.*/
#
#(A*)/(^^\1)NBM #
#(B*)/(^^\1)NB #
#(C*)/(^^\1)BM #
#(D*)/(^^\1)N #
#(E*)/(^^\1)B #
#(F*)/(^^\1)M #
\(\^\^\)/0
 #/

Live demo and tests.

The workings are simple but a little odd:

(NBM)|(NB)|(BM)|(N)|(B)|(M)/a\1bc\2de\3fg\4hi\5jk\6l

This creatively uses groups to turn input like:

NBMBM

into

aNBMbcdeBMfghijkl

The next line:

[A-Z]+/_

This replaces the sequences of capital letters with underscores.

#

This simply inserts a pound sign at the beginning of the line.

+(#.*?)a_b/A\1
+(#.*?)c_d/B\1
+(#.*?)e_f/C\1
+(#.*?)g_h/D\1
+(#.*?)i_j/E\1
+(#.*?)k_l/F\1
#.*/

This is the beginning cool part. It basically takes the sequences of lowercase letters and underscores, converts them into capital letters, groups them together, and places them before the pound that was inserted. The purpose of the pound is to manage the sequences that have already been processed.

#

The pound is re-inserted at the beginning of the line.

#(A*)/(^^\1)NBM #
#(B*)/(^^\1)NB #
#(C*)/(^^\1)BM #
#(D*)/(^^\1)N #
#(E*)/(^^\1)B #
#(F*)/(^^\1)M #
\(\^\^\)/0
 #/

The capital letters are replaced by their text equivalents with the associated counts. Because of a bug in rs (I didn't want to risk fixing it and getting disqualified), the empty sequences are converted into (^^), which is replaced by a 0 in the second-to-last line. The very last line simply removes the pound.

\$\endgroup\$
0
\$\begingroup\$

KDB(Q), 76 bytes

{" "sv string[-1+count@'enlist[x]{y vs" "sv x}\l],'l:" "vs"NBM NB BM N B M"}

Explanation

                                                   l:" "vs"NBM NB BM N B M"     / substrings
                        enlist[x]{y vs" "sv x}\l                                / replace previous substring with space and cut
              -1+count@'                                                        / counter occurrence
       string[                                  ],'                             / string the count and join to substrings
{" "sv                                                                     }    / concatenate with space, put in lambda

Test

q){" "sv string[-1+count@'enlist[x]{y vs" "sv x}\l],'l:" "vs"NBM NB BM N B M"}"NNNMNBMMBMMBMMMNBMNNMNNNBNNNBNBBNBNMMNBBNBMMBBMBMBBBNNMBMBMMNNNNNMMBMMBM"
"3NBM 5NB 8BM 17N 6B 14M"
q){" "sv string[-1+count@'enlist[x]{y vs" "sv x}\l],'l:" "vs"NBM NB BM N B M"}""
"0NBM 0NB 0BM 0N 0B 0M"
\$\endgroup\$
0
\$\begingroup\$

Stax, 18 bytes

â╥)3╨‼!∩╢←◘♀←N╟Ijƒ

Run and debug it

\$\endgroup\$
0
\$\begingroup\$

Haskell, 244 bytes

import Data.List
s="NBM"
[]#_=[[]]
a#[]=[]:a#s
l@(a:r)#(b:m)
 |a==b=let(x:y)=r#m in((a:x):y)
 |True=[]:l#m
c?t=length$filter(==t)c
p=["N","B","M","NB","BM","NBM"]
main=getLine>>= \l->putStrLn.intercalate " "$map(\t->show((l#[])?t)++t)p
\$\endgroup\$
2
  • \$\begingroup\$ Some suggestions: You're using p and s only once, so there's no need to give it a name (-> a#[]=[]:a#"NBM", same for p). BTW: words"N B M NB BM NBM" instead of the list of strings saves additional bytes. The import is only for intercalate, it's shorter te re-implement it: ...putStrLn.tail.((' ':)=<<)$map... and get rid of the import. Put the all the guards | in the definition of # in a single line and use 1<2 instead of True: ...#(b:m)|a==b=...l#m|1<2=[]... ... \$\endgroup\$
    – nimi
    Jul 17, 2015 at 13:34
  • \$\begingroup\$ ... ? can be defined shorter with a list comprehension: c?t=sum[1|x<-c,x==t]. Again, you're using ? only once, so use the body directly: ...show(sum[1|x<-l#[],x==t]). \$\endgroup\$
    – nimi
    Jul 17, 2015 at 13:34

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.