18
\$\begingroup\$

In typography, a counter is the area of a letter that is entirely or partially enclosed by a letter form or a symbol. A closed counter is a counter that is entirely enclosed by a letter form or symbol. You must write a program takes a string as input and prints the total number of closed counters in the text.

Your input:

  • May be a command line input, or from STDIN, but you must specify which.

  • Will consist entirely of the printable ASCII characters, meaning all ASCII values between 32 and 126 inclusive. This does include spaces. More information.

Now, this does vary slightly between fonts. For example, the font you're reading this in considers 'g' to have one closed counter, whereas the google font has 'g' with two closed counters. So that this is not an issue, here are the official number of closed counters per character.

All the symbols with no closed counters:

 !"'()*+,-./12357:;<=>?CEFGHIJKLMNSTUVWXYZ[\]^_`cfhijklmnrstuvwxyz{|}~

Note that this includes space.

Here are all the symbols with one closed counter:

#0469@ADOPQRabdegopq

And here are all the symbols with 2 closed counters:

$%&8B

And last but not least, here are some sample inputs and outputs.

Programming Puzzles and Code-Golf should print 13

4 8 15 16 23 42 should print 5

All your base are belong to us should print 12

Standard loopholes apply should print 12

Shortest answer in bytes is the winner! should print 8

\$\endgroup\$
11
  • 1
    \$\begingroup\$ Two answers have submitted functions instead of full programs. While that is allowed by default, your wording suggests otherwise. Could you clarify? \$\endgroup\$
    – Dennis
    Jun 19, 2015 at 22:07
  • \$\begingroup\$ Would you mind disclosing which going you used to count the counters? \$\endgroup\$ Jun 20, 2015 at 13:06
  • 3
    \$\begingroup\$ None of the fonts I'm viewing the question in corresponds to the counts you have given. E.g. in the browser, the zero has a diagonal slash through it, giving two counters. The font in the android app doesn't, but here the g has two closed counters. Did you determine the counters based on any particular font? \$\endgroup\$ Jun 20, 2015 at 14:10
  • 1
    \$\begingroup\$ @DJMcMayhem 'g' has 1; though where listed in code, g has 2. Slightly confusing to read, but I don't think it's different by location. \$\endgroup\$
    – OJFord
    Jun 21, 2015 at 21:45
  • 1
    \$\begingroup\$ Doesn't 0 have 2 closed counters in certain fonts, especially many monospace fonts? \$\endgroup\$
    – vsz
    Jun 22, 2015 at 6:17

24 Answers 24

28
\$\begingroup\$

Python 3, 63

print(sum(map(input().count,"#0469@ADOPQRabdegopq$%&8B$%&8B")))

A straightforward approach. Iterates over each character with a closed counter, summing the number of occurrences, doing so twice for characters with two closed counters. It would be the same length to instead write

"#0469@ADOPQRabdegopq"+"$%&8B"*2

Python 3 is needed to avoid raw_input.

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

CJam, 41 39 37 34 bytes

"$%&8Badopq#0469@Rbeg"_A<eu+qfe=1b

Thanks to @jimmy23013 for golfing off 3 bytes!

Try it online.

How it works

"$%&8Badopq#0469@Rbeg"             e# Push that string.
                      _A<          e# Retrieve the first 10 characters.
                         eu+       e# Convert to uppercase and append.
                                   e# This pushes "$%&8Badopq#0469@Rbeg$%&8BADOPQ".
                            q      e# Read from STDIN.
                             fe=   e# Count the occurrences of each character. 
                                1b e# Base 1 conversion (sum).
\$\endgroup\$
2
  • 2
    \$\begingroup\$ "$%&8Badopq#0469@Rbeg"_A<eu+. \$\endgroup\$
    – jimmy23013
    Jun 20, 2015 at 6:11
  • \$\begingroup\$ @jimmy23013: I had tried a few variations of eu and el, but I never found that. Thanks! \$\endgroup\$
    – Dennis
    Jun 20, 2015 at 6:17
10
\$\begingroup\$

Pyth, 31 bytes

sm@tjC"cúÁ-ÈN%³rØ|­"3Cdz

Demonstration.

Note that the code may not be displayed properly due to the use of non-ASCII characters. The correct code is at the link.

I made a lookup table of the output desired for each input character, rotated it by 32 to make use of Pyth's modular indexing, stuck a 1 at the beginning, and interpreted it as a base 3 number, giving the number 2229617581140564569750295263480330834137283757. I then converted this number to base 256 and converted it to a string, which is the string used in the answer.

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

sed, 51

With golfing help from @manatwork and @TobySpeight:

s/[$%&8B]/oo/g
s/[^#0469@ADOPQRabdegopq]//g
s/./1/g

Input from STDIN. With this meta-question in mind, the output is in unary:

$ echo 'Programming Puzzles and Code-Golf
4 8 15 16 23 42
All your base are belong to us
Standard loopholes apply
Shortest answer in bytes is the winner!' | sed -f countercounter.sed
1111111111111
11111
111111111111
111111111111
11111111
$ 
\$\endgroup\$
0
7
\$\begingroup\$

Perl, 41

$_=y/#0469@ADOPQRabdegopq//+y/$%&8B//*2

41 characters +1 for the -p flag.

This uses y/// to count the characters.

echo 'Programming Puzzles and Code-Golf' | perl -pe'$_=y/#0469@ADOPQRabdegopq//+y/$%&8B//*2'
\$\endgroup\$
6
\$\begingroup\$

GNU APL, 39 bytes

+/⌈20÷⍨26-'$%&8B#0469@ADOPQRabdegopq'⍳⍞

Try it online in GNU APL.js.

How it works

                                      ⍞ Read input.
          '$%&8B#0469@ADOPQRabdegopq'⍳  Compute the indexes of the input characters
                                        in this string. Indexes are 1-based.
                                        26 == 'not found'
       26-                              Subtract each index from 26.
   20÷⍨                                 Divide those differences by 20.
  ⌈                                     Round up to the nearest integer.
+/                                      Add the results.
\$\endgroup\$
6
\$\begingroup\$

JavaScript, 86

I/O via popup. Run the snippet in any drecent browser to test.

for(c of prompt(t=0))c='$%&8B#0469@ADOPQRabdegopq'.indexOf(c),~c?t+=1+(c<5):0;alert(t)

\$\endgroup\$
6
\$\begingroup\$

K, 54 43 42 37 bytes

+//(30#"$%&8B#0469@ADOPQRabdegopq"=)'

Cut off 5 bytes thanks to @JohnE!

Older version:

f:+/(#&"#0469@ADOPQRabdegopq$%&8B$%&8B"=)'

Original:

f:+/{2-(5="$%&8B"?x;20="#0469@ADOPQRabdegopq"?x;0)?0}'
\$\endgroup\$
3
  • \$\begingroup\$ The #& inside the parens could just as easily be +/, which means you could go further with +//"#0469@ADOPQRabdegopq$%&8B$%&8B"=\:. Finally, it's not necessary to have the f: since the function can be used in tacit form. That would bring you down to 38! \$\endgroup\$
    – JohnE
    Jun 20, 2015 at 0:19
  • \$\begingroup\$ Unfortunately the trick a few other solutions have employed to compact the lookup table comes out dead even with the current 38 byte solution: +//(30#"$%&8B#0469@ADOPQRabdegopq")=\:. This may be the best we can do. \$\endgroup\$
    – JohnE
    Jun 20, 2015 at 2:46
  • \$\begingroup\$ haha, no sooner did I post that than I saved a character: +//(30#"$%&8B#0469@ADOPQRabdegopq"=)' \$\endgroup\$
    – JohnE
    Jun 20, 2015 at 2:51
5
\$\begingroup\$

C, 127 bytes

n;f(char*i){char*o="#0469@ADOPQRabdegopq",*t="$%&8B";for(;*i;i++)if(strchr(o,*i))n++;else if(strchr(t,*i))n+=2;printf("%d",n);}

Pretty straightforward. Ungolfed version:

int num = 0;
void f(char* input)
{
    char *one="#0469@ADOPQRabdegopq";
    char *two="$%&8B";

    for(;*input;input++)
        if(strchr(one, *input))     //If current character is found in first array
            num ++;
        else if(strchr(two, *input))//If cuurent character is found in second array
            num += 2;

    printf("%d", num);
}

Test it here

If function arguments are not allowed, then the stdin version takes up to 141 bytes:

n;f(){char*o="#0469@ADOPQRabdegopq",*t="$%&8B",i[99],*p;gets(i);for(p=i;*p;p++)if(strchr(o,*p))n++;else if(strchr(t,*p))n+=2;printf("%d",n);}

Note that the above version assumes that the input is atmost 98 characters long.

Test it here

Command-line arguments version (143 bytes):

n;main(c,v)char**v;{char*o="#0469@ADOPQRabdegopq",*t="$%&8B",*p=v[1];for(;*p;p++)if(strchr(o,*p))n++;else if(strchr(t,*p))n+=2;printf("%d",n);}

Test it here

\$\endgroup\$
1
  • 1
    \$\begingroup\$ @DJMcMayhem C is really not that bad. Try golfing in Fortran 77. ;) \$\endgroup\$
    – Alex A.
    Jun 22, 2015 at 13:11
5
\$\begingroup\$

Python 2, 96 90 75 67+2 = 69 Bytes

Can't think of any other way to do this... is what I would have thought until I saw xnor's solution. I'll post what I had anyways.

Thanks to FryAmTheEggman for saving 6 bytes

Alright, now I'm happy with this.

Thanks to xnor for the find trick, saving 4 bytes.

Added two bytes since input needs to be enclosed in quotes.

print sum('#0469@ADOPQRabdegopq$%&8B'.find(x)/20+1for x in input())
\$\endgroup\$
3
  • 1
    \$\begingroup\$ I like the clever use of indexes! Also, python 3's a bit shorter because it uses input instead of raw_input. \$\endgroup\$
    – DJMcMayhem
    Jun 19, 2015 at 19:25
  • \$\begingroup\$ @manatwork It works fine in Python 2 with enclosing quotes.. \$\endgroup\$
    – Kade
    Jun 22, 2015 at 14:30
  • \$\begingroup\$ Oh, I see. Sorry, I combined it with @DJMcMayhem's Python 3 comment. \$\endgroup\$
    – manatwork
    Jun 22, 2015 at 14:31
4
\$\begingroup\$

Java, 162

class C{public static void main(String[]a){System.out.print(-a[0].length()+a[0].replaceAll("[#0469@ADOPQRabdegopq]","..").replaceAll("[$%&8B]","...").length());}}

Well if it has to be a full program... It's just a one-liner that matches characters and replaces them with a longer string. Then, returns the difference in length from the original. Unfortunately, java doesn't really have anything to just count the number of matches.

Here it is with line breaks:

class C{
    public static void main(String[]a){
        System.out.print(
                -a[0].length() +
                a[0].replaceAll("[#0469@ADOPQRabdegopq]","..")
                .replaceAll("[$%&8B]","...")
                .length()
                        );
    }
}
\$\endgroup\$
4
\$\begingroup\$

Pyth - 35 bytes

Uses the obvious method of in first + *2 in second. Thanks @FryTheEggman.

s/Lz+"#0469@ADOPQRabdegopq"*2"$%&8B

Try it here online.

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

Javascript, 114 95 bytes

alert(prompt().replace(/[#046@ADOPQRabdegopq]/g,9).replace(/[$%&8B]/g,99).match‌​(/9/g).length)

Thanks to Ismael Miguel for helping me golf this.

\$\endgroup\$
2
  • 2
    \$\begingroup\$ 93 bytes: alert(prompt().replace(/[#046@ADOPQRabdegopq]/g,9).replace(/[$%&8B]/g,99).match(/9/g).length) \$\endgroup\$ Jun 19, 2015 at 23:59
  • \$\begingroup\$ Sorry for counting badly. Yes, its 95. \$\endgroup\$ Jun 20, 2015 at 0:34
3
\$\begingroup\$

Ruby, 59 bytes

a=gets;p a.count('#0469@ADOPQRabdegopq')+2*a.count('$%&8B')

Input from command line or stdin. Shortest so far using a non-esoteric language.

Update: chilemagic beat me

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

Retina, 44 bytes

1

[#0469@ADOPQRabdegopq]
1
[$%&8B]
11
[^1]
<empty line>

Gives output in unary.

Each line should go to its own file or you can use the -s flag. E.g.:

> echo "pp&cg"|retina -s counter
11111

The pairs of lines (pattern - substitute pairs) do the following substitution steps:

  • Remove 1's
  • Substitute 1-counter letters with 1
  • Substitute 2-counter letters with 11
  • Remove everything but the 1's
\$\endgroup\$
3
\$\begingroup\$

J, 43

As a function:

   f=:[:+/[:,30$'$%&8B#0469@ADOPQRabdegopq'=/]
   f 'text goes here'
6

46 bytes (command line)

As a standalone command-line program:

echo+/,30$'$%&8B#0469@ADOPQRabdegopq'=/>{:ARGV

Save the above line as counter2.ijs and call from the command line:

$ jconsole counter2.ijs 'Programming Puzzles and Code Golf'
13
\$\endgroup\$
1
  • \$\begingroup\$ Copy-pasting the input into the code isn't allowed but a function which can take the input as an argument is ok. E.g. f=:your_function_code. \$\endgroup\$
    – randomra
    Jun 20, 2015 at 7:57
2
\$\begingroup\$

Julia, 77 74 bytes

t=readline();length(join(t∩"#0469@ADOPQRabdegopq")*join(t∩"\$%&8B")^2)

This reads text from STDIN and prints the result to STDOUT.

Ungolfed + explanation:

# Read text from STDIN
t = readline()

# Print (implied) to STDOUT the length of the intersection of t with the
# 1-closed counter list joined with the duplicated intersection of t with
# the 2-closed counter list
length(join(t ∩ "#0469@ADOPQRabdegopq") * join(t ∩ "\$%&8B")^2)

Example:

julia> t=readline();length(join(t∩"#0469@ADOPQRabdegopq")*join(t∩"\$%&8B")^2)
Programming Puzzles and Code Golf
13
\$\endgroup\$
2
\$\begingroup\$

rs, 56 bytes

_/
[#0469@ADOPQRabdegopq]/_
[$%&8B]/__
[^_]/
(_+)/(^^\1)

Live demo.

\$\endgroup\$
3
  • \$\begingroup\$ Just an fyi: I created a stub esolangs page for rs. You may wish to add to it: esolangs.org/wiki/Rs \$\endgroup\$
    – mbomb007
    Sep 21, 2015 at 20:01
  • \$\begingroup\$ @mbomb007 WOW!! That just made my day. :D \$\endgroup\$ Sep 21, 2015 at 20:10
  • \$\begingroup\$ Well, "rs" doesn't come up in Google or anything since it's only two letters. This way, people can find it. :) \$\endgroup\$
    – mbomb007
    Sep 21, 2015 at 20:12
2
\$\begingroup\$

GNU APL, 37 characters

+/,⍞∘.=30⍴'$%&8B#0469@ADOPQRabdegopq'

construct a character vector which contains 2-counter characters twice (30⍴)

compare each input char with every character in the vector (∘.=)

sum up ravelled matches (+/,)

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

Javascript 159, 130 Bytes

function c(b){return b.split("").map(function(a){return-1!="$%&8B".indexOf(a)?2:-1!="#0469@ADOPQRabdegopq".indexOf(a)?1:0}).reduce(function(a,b){return a+b})};

unminified:

function c(b) {
    return b.split("").map(function(a) {
        return -1 != "$%&8B".indexOf(a) ? 2 : -1 != "#0469@ADOPQRabdegopq".indexOf(a) ? 1 : 0
    }).reduce(function(a, b) {
        return a + b
    })
};

With the help of @edc65:

function c(b){return b.split('').reduce(function(t,a){return t+(~"$%&8B".indexOf(a)?2:~"#0469@ADOPQRabdegopq".indexOf(a)?1:0)},0)}
\$\endgroup\$
2
  • 2
    \$\begingroup\$ As ~ -1 == 0, you can write ~x? instead of -1 != x?. See me answer for an example of use. \$\endgroup\$
    – edc65
    Jun 20, 2015 at 0:09
  • 2
    \$\begingroup\$ function c(b){return b.split('').reduce(function(t,a){return t+(~"$%&8B".indexOf(a)?2:~"#0469@ADOPQRabdegopq".indexOf(a)?1:0)},0)} No need to have map then reduce \$\endgroup\$
    – edc65
    Jun 20, 2015 at 0:18
1
\$\begingroup\$

Haskell, 117

a="#0469@ADOPQRabdegopq"
b="$%&8B"
c n[]=n
c n(x:s)
 |e a=f 1
 |e b=f 2
 |True=f 0
 where
 e y=x`elem`y
 f y=c(n+y)s

c is a function c :: Int -> String -> Int which takes a counter and a string and goes through the string one letter at a time checking if the current letter is a member of the 1 point array or the 2 point array and calls itself for the rest of the string after incrementing the counter the appropriate amount.

Call with counter = 0 in ghci:

ghci> c 0 "All your base are belong to us"
12
\$\endgroup\$
1
\$\begingroup\$

C#, 157

void Main(string s){int v=0;v=s.ToCharArray().Count(c=>{return "#0469@ADOPQRabdegopq".Contains(c)||"$%&8B".Contains(c)?++v is int:false;});Console.Write(v);}

Ungolfed:

void Main(string s)
{
    int v = 0;

    v = s.ToCharArray()
    .Count(c => 
    {
        return "#0469@ADOPQRabdegopq".Contains(c) || "$%&8B".Contains(c) ? ++v is int:false;
    });

    Console.Write(v);
}

Converting the string into a char array, then seeing if each char is in either counter. If it is in the second one, I just have it incrementing the counter again.

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

Erlang, 103 bytes

This is a complete program that runs using escript. The first line of the file must be blank (adding 1 byte).

main([L])->io:write(c(L,"#0469@ADOPQRabdegopq")+2*c(L,"$%&8B")).
c(L,S)->length([X||X<-L,S--[X]/=S]).

Sample run:

$ escript closed.erl 'Shortest answer in bytes is the winner!'
8$
\$\endgroup\$
3
  • \$\begingroup\$ Welcome to PPCG, c(L,"#0469@ADOPQRabdegopq")+2*c(L,"$%&8B") is longer than c(L,"#0469@ADOPQRabdegopq$%&8B$%&8B") by 5 bytes :). \$\endgroup\$
    – Katenkyo
    Jun 22, 2015 at 8:24
  • \$\begingroup\$ @Katyenko, thanks for the suggestion. Regrettably it doesn't work correctly for certain inputs. "$%&8B" counts for 5, but should be 10. The c/2 function works by filtering out the chars of the string that do not belong to a set of chars, such as "$%&8B". It checks for set inclusion by deleting the char to be tested from the set, then comparing the result with the original set. If they are not equal, then the char was in the set, and it is included. Multiple copies of chars in the set don't have any effect. \$\endgroup\$
    – Edwin Fine
    Jun 22, 2015 at 15:40
  • \$\begingroup\$ Ho, I see, I don't know erlang, was thinking you were using a string to count the counter :3. Anyway, nevermind, and well done :) \$\endgroup\$
    – Katenkyo
    Jun 22, 2015 at 17:53
0
\$\begingroup\$

C, 99 bytes

n;f(char*i){for(char*o="#0469@ADOPQRabdegopq$%&8B",*t=o+20;*i;)n+=2-!strchr(o,*i)-!strchr(t,*i++);}

Explanation

I further golfed the answer of Cool Guy; it got too long to be a comment. Instead of the if/else, I took advantage of ! converting a pointer to bool. I also made o include t so that I could add "is in o" and "is in t" for the total number of counters.

Expanded code

#include <string.h>
int num = 0;

void f(char* input)
{
    char *one = "#0469@ADOPQRabdegopq"  "$%&8B";
    char *two = strchr(one, '$');

    while (*input) {
        num += 2 - !strchr(one, *input) - !strchr(two, *input));
        ++input;
    }
}

Output is in num, which must be cleared before each call.

Test program and results

#include <stdio.h>
int main() {
    const char* a[] = {
        "Programming Puzzles and Code-Golf",
        "4 8 15 16 23 42",
        "All your base are belong to us",
        "Standard loopholes apply",
        "Shortest answer in bytes is the winner!",
        NULL
    };
    for (const char** p = a;  *p;  ++p) {
        n=0;f(*p);
        printf("%3d: %s\n", n, *p);
    }
    return 0;
}
 13: Programming Puzzles and Code-Golf
  5: 4 8 15 16 23 42
 12: All your base are belong to us
 12: Standard loopholes apply
  8: Shortest answer in bytes is the winner!
 37: n;f(char*i){for(char*o="#0469@ADOPQRabdegopq$%&8B",*t=o+20;*i;)n+=2-!strchr(o,*i)-!strchr(t,*i++);}

The code itself contains 37 counters by its own metric.

\$\endgroup\$

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.