Counter counter

18

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

James

Posted 2015-06-19T18:50:42.730

Reputation: 54 537

1Two answers have submitted functions instead of full programs. While that is allowed by default, your wording suggests otherwise. Could you clarify? – Dennis – 2015-06-19T22:07:33.110

Would you mind disclosing which going you used to count the counters? – Martin Ender – 2015-06-20T13:06:50.423

@MartinBüttner I'm not sure if I understand what you mean. – James – 2015-06-20T14:07:06.907

3None 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? – Martin Ender – 2015-06-20T14:10:06.977

@MartinBüttner I just used whichever font came up when I wrote the question. (In the output, not the source) I guess I assumed that everybody is seeing the same font, but apparently they're not. Maybe it's a location thing? – James – 2015-06-20T14:19:02.067

1@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. – OJFord – 2015-06-21T21:45:28.910

1Doesn't 0 have 2 closed counters in certain fonts, especially many monospace fonts? – vsz – 2015-06-22T06:17:19.060

So what does it vary by? OS? Browser? I wrote this on Firefox on Ubuntu. – James – 2015-06-22T19:27:29.503

@DJMcMayhem It varies by formatting. You used code markup for posting which letters. Look at your question. The monospaced g and 0 have 2 closed counters, which is confusing people. – mbomb007 – 2015-06-22T21:09:17.427

@mbomb007 but on my computer, both g and 0 have 1 closed counter, even with the formatting. – James – 2015-06-22T21:22:36.800

Then I think you're in the minority, but since you defined the sets, it's fine. – mbomb007 – 2015-06-22T21:37:59.410

Answers

10

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.

isaacg

Posted 2015-06-19T18:50:42.730

Reputation: 39 268

29

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.

xnor

Posted 2015-06-19T18:50:42.730

Reputation: 115 687

12

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).

Dennis

Posted 2015-06-19T18:50:42.730

Reputation: 196 637

2"$%&8Badopq#0469@Rbeg"_A<eu+. – jimmy23013 – 2015-06-20T06:11:12.980

@jimmy23013: I had tried a few variations of eu and el, but I never found that. Thanks! – Dennis – 2015-06-20T06:17:00.943

8

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
$ 

Digital Trauma

Posted 2015-06-19T18:50:42.730

Reputation: 64 644

7

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'

hmatt1

Posted 2015-06-19T18:50:42.730

Reputation: 3 356

6

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}'

kirbyfan64sos

Posted 2015-06-19T18:50:42.730

Reputation: 8 730

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! – JohnE – 2015-06-20T00:19:38.330

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. – JohnE – 2015-06-20T02:46:30.303

haha, no sooner did I post that than I saved a character: +//(30#"$%&8B#0469@ADOPQRabdegopq"=)' – JohnE – 2015-06-20T02:51:54.197

6

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.

Dennis

Posted 2015-06-19T18:50:42.730

Reputation: 196 637

6

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)

edc65

Posted 2015-06-19T18:50:42.730

Reputation: 31 086

5

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())

Kade

Posted 2015-06-19T18:50:42.730

Reputation: 7 463

1I like the clever use of indexes! Also, python 3's a bit shorter because it uses input instead of raw_input. – James – 2015-06-19T19:25:16.990

@manatwork It works fine in Python 2 with enclosing quotes..

– Kade – 2015-06-22T14:30:09.807

Oh, I see. Sorry, I combined it with @DJMcMayhem's Python 3 comment. – manatwork – 2015-06-22T14:31:50.600

5

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

Spikatrix

Posted 2015-06-19T18:50:42.730

Reputation: 1 663

1@DJMcMayhem C is really not that bad. Try golfing in Fortran 77. ;) – Alex A. – 2015-06-22T13:11:42.457

4

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()
                        );
    }
}

Geobits

Posted 2015-06-19T18:50:42.730

Reputation: 19 061

4

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.

Maltysen

Posted 2015-06-19T18:50:42.730

Reputation: 25 023

4

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.

SuperJedi224

Posted 2015-06-19T18:50:42.730

Reputation: 11 342

293 bytes: alert(prompt().replace(/[#046@ADOPQRabdegopq]/g,9).replace(/[$%&8B]/g,99).match(/9/g).length) – Ismael Miguel – 2015-06-19T23:59:49.150

Sorry for counting badly. Yes, its 95. – Ismael Miguel – 2015-06-20T00:34:25.673

3

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

David Bailey

Posted 2015-06-19T18:50:42.730

Reputation: 141

3

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

hoosierEE

Posted 2015-06-19T18:50:42.730

Reputation: 760

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. – randomra – 2015-06-20T07:57:23.740

3

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

randomra

Posted 2015-06-19T18:50:42.730

Reputation: 19 909

2

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

Alex A.

Posted 2015-06-19T18:50:42.730

Reputation: 23 761

2

rs, 56 bytes

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

Live demo.

kirbyfan64sos

Posted 2015-06-19T18:50:42.730

Reputation: 8 730

Just an fyi: I created a stub esolangs page for rs. You may wish to add to it: http://esolangs.org/wiki/Rs

– mbomb007 – 2015-09-21T20:01:30.047

@mbomb007 WOW!! That just made my day. :D – kirbyfan64sos – 2015-09-21T20:10:58.917

Well, "rs" doesn't come up in Google or anything since it's only two letters. This way, people can find it. :) – mbomb007 – 2015-09-21T20:12:04.397

2

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 (+/,)

Jürgen Sauermann

Posted 2015-06-19T18:50:42.730

Reputation: 121

1

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)}

Thomas Junk

Posted 2015-06-19T18:50:42.730

Reputation: 131

2As ~ -1 == 0, you can write ~x? instead of -1 != x?. See me answer for an example of use. – edc65 – 2015-06-20T00:09:16.603

2function 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 – edc65 – 2015-06-20T00:18:47.143

1

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

Craig Roy

Posted 2015-06-19T18:50:42.730

Reputation: 790

1

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.

Cooler Ranch

Posted 2015-06-19T18:50:42.730

Reputation: 21

1

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$

Edwin Fine

Posted 2015-06-19T18:50:42.730

Reputation: 111

Welcome to PPCG, c(L,"#0469@ADOPQRabdegopq")+2*c(L,"$%&8B") is longer than c(L,"#0469@ADOPQRabdegopq$%&8B$%&8B") by 5 bytes :). – Katenkyo – 2015-06-22T08:24:16.737

@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. – Edwin Fine – 2015-06-22T15:40:30.603

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 :) – Katenkyo – 2015-06-22T17:53:16.193

0

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.

Toby Speight

Posted 2015-06-19T18:50:42.730

Reputation: 5 058