# Determine the Sharpness of a Word

## Overview

The sharpness of a word is the sum of the sharpness of each of its letters, using the following rules:

Sharp letters

• A, and V each have a sharpness of 1

• N and Z each have a sharpness of 2

• M and W each have a sharpness of 3

Dull letters

• C and U each have a sharpness of -1

• S has a sharpness of -2

• O has a sharpness of -3

All other letters have a sharpness of 0.

### Example

The word SAUCE has a sharpness of -3 since the A contributes 1, the U and C each contribute -1 and the S contributes -2.

The word MAZE has a sharpness of 6 since the M contributes 3, the Z contributes 2, and the A contributes 1.

Given as input a string containing only uppercase letters*, determine the sharpness of that string.

*If you wish, you may instead take the input as a string containing only lowercase letters. Other common input formats are also acceptable (eg. a list of characters, a list of codepoints, etc.)

This is !

## Test Cases

CODE => -4
GOLF => -3
SAUCE => -3
CATS => -2
MOON => -1
NONSENSE => -1
ZUCKERBERG => 0
STRING => 0
CHALLENGE => 2
NAIL => 3
CARNIVAL => 4
COMPLIMENT => 4
WAVE => 5
UNKNOWN => 5
MAZE => 6


Extra test cases (Provided by @Surculose Sputum)

MIZZENMASTMAN => 17
PNEUMONOULTRAMICROSCOPICSILICOVOLCANOCONIOSIS => -26
MANAGEMENT => 12
CONSCIOUSNESS => -13


# Python 2, 53 bytes

lambda s:sum(3-"MWCUNZS_AVO".find(c)/2*4%7for c in s)


Try it online!

• -1 byte if you interleave the string instead of /2. Try it online! – David Jun 3 at 15:43
• @David Nice improvement! I suggest you post it as an answer which would qualify for my outgolfing bounty. – xnor Jun 3 at 18:32

# Jelly, 21 bytes

“CSO“ANM“U“VZX”iⱮ€§ḅ-


A monadic Link accepting a list of characters which yields an integer.

Try it online!

### How?

“CSO“ANM“U“VZX”iⱮ€§ḅ- - Link: list of characters, W
“CSO“ANM“U“VZX”       - list of lists of characters = [['C','S','O'],['A','N','M'],['U'],['V','Z','X']]
€    - for each (L in those four lists):
Ɱ     -   map across (c in W) with:
i      -     first (1-based) index (of c in L) or 0 if not found
§   - sum each resulting list
- - literal minus one
ḅ  - convert (the list of four sums) from base (-1)

• I'm abysmal at coding but I gotta say, this one's quite particularly beautiful! – Peter Johnmeyer Jun 3 at 12:47

# Python 2, 52 bytes

lambda s:sum(3-"MCNSAO_WUZ_V".find(c)*4%7for c in s)


Try it online!

M C N S A O _ W U Z _ V ?  c
0 1 2 3 4 5 6 0 1 2 3 4 6  "...".find mod 7
0 4 1 5 2 6 3 0 4 1 5 2 3  "...".find*4 mod 7
3-1 2-2 1-3 0 3-1 2-2 1 0  (3-"...".find*4 mod 7)


*4 is to map -1 (failed find) onto 0

# JavaScript (Node.js),  61  53 bytes

A lookup table based on the ASCII code of each letter.

Takes input as a list of codepoints.

a=>a.map(c=>t+='50363133463254'[(c^98)%22]-3|0,t=0)|t


Try it online!

### How?

By XOR'ing the ASCII code with 98 and applying a modulo 22, we gather the characters with a non-zero sharpness into the range [0..13]. This results in a lookup table of 14 entries.

If the result of the formula is out of range, we get undefined - 3 | 0, which is 0.

 char. | code | XOR 98 | MOD 22 | sharpness
-------+------+--------+--------+-----------
'A'  |  65  |   35   |   13   |    +1
'B'  |  66  |   32   |   10   |
'C'  |  67  |   33   |   11   |    -1
'D'  |  68  |   38   |   16   |
'E'  |  69  |   39   |   17   |
'F'  |  70  |   36   |   14   |
'G'  |  71  |   37   |   15   |
'H'  |  72  |   42   |   20   |
'I'  |  73  |   43   |   21   |
'J'  |  74  |   40   |   18   |
'K'  |  75  |   41   |   19   |
'L'  |  76  |   46   |    2   |
'M'  |  77  |   47   |    3   |    +3
'N'  |  78  |   44   |    0   |    +2
'O'  |  79  |   45   |    1   |    -3
'P'  |  80  |   50   |    6   |
'Q'  |  81  |   51   |    7   |
'R'  |  82  |   48   |    4   |
'S'  |  83  |   49   |    5   |    -2
'T'  |  84  |   54   |   10   |
'U'  |  85  |   55   |   11   |    -1
'V'  |  86  |   52   |    8   |    +1
'W'  |  87  |   53   |    9   |    +3
'X'  |  88  |   58   |   14   |
'Y'  |  89  |   59   |   15   |
'Z'  |  90  |   56   |   12   |    +2

• Arguably, you don't even need the Buffer(s) bit, because "a list of codepoints" is acceptable as input. – Steve Bennett Jun 4 at 12:14
• @SteveBennett I didn't notice it was explicitly allowed. Thanks! – Arnauld Jun 4 at 12:19

# APL+WIN, 42 38 bytes.

Prompts for string:

   +/(2/¯3+⍎¨'4562103')['AVNZMWCUS O '⍳⎕]


Try it online! Courtesy of Dyalog Classic

Explanation:

⍎¨'4562103' Split number as a string into individual digits and convert to integers

2/¯3+ Subtract 3 to give the non-zero letter values and double up each value

['AVNZMWCUS O '⍳⎕] Find the index position of non-zero letters that occur in the input.
If a letter is not in list it is given an index of length of list + 1

+/ Sum the values corresponding to the indices of non-zero letters in string

• does the ⍎¨'<string>' trick work with negative numbers as well? – Razetime Aug 26 at 14:27
• @Razetime No because ⍎¨ attempts to execute each element of the string which works fine if each element is an positive integer but in the case of the leading minus sign of a negative number ⍎ throws a syntax error as the minus sign needs at least a right argument to be a valid expression. – Graham Aug 26 at 19:23

# APL (Dyalog Unicode), 31 bytes

-/1⊥¨4|(↓4 3⍴'ANMCSOVZWU  ')⍳¨⊂


Try it online!

Tacit function.

### How it works

-/1⊥¨4|(↓4 3⍴'ANMCSOVZWU  ')⍳¨⊂  ⍝ Input: string S
(↓4 3⍴'ANMCSOVZWU  ')     ⍝ 'ANM' 'CSO' 'VZW' 'U  '
⍳¨⊂  ⍝ Find the index of each char in S in each of above
4|  ⍝ Modulo 4; convert 4 to 0
1⊥¨    ⍝ Sum of each row
-/       ⍝ Alternating sum; (ANM score)-(CSO score)+(VZW score)-(U score)


# C(GCC) 8473 72 bytes

-11 bytes thanks to ceilingcat

t;f(char*s){for(t=0;*s;t+=""[*s++-65]-4);s=t;}


(note that StackExchange strips some non-printing characters)

Try it online!

# Python 2, 62 60 bytes

-2 bytes thanks to @Neil!

lambda w:sum('AVNZMW'.find(c)/2-'CUS_O'.find(c)/2for c in w)


Try it online!

• 'AVNZMW'.find(c)/2-'CUS_O'.find(c)/2 saves 2 bytes, but I like the /2 idea, it saves my a byte on my Charcoal answer! – Neil Jun 3 at 19:04
• @Neil thanks a lot, this really looks like it shouldn't work ;) – ovs Jun 3 at 19:16

# Python 3, 61 bytes

Input is taken as a byte-string $$\ s \$$.

lambda s:sum(b''[c-65]-4for c in s)


Try it online!

We use a lookup table, where each element maps to the sharpness of a given letter. Note the -4, to display the negative sharpness.

# Retina 0.8.2, 61 60 bytes

M|W
N#
N|Z
##
O
S-
S
--
TVAUC##-
+\w|-#|#-

^(-)?.*
$1$.&


Try it online! Link includes test cases. Edit: Saved 1 byte thanks to @mathjunkie. Explanation:

M|W
N#
N|Z
##
O
S-
S
--
TVAUC##-


Turn sharp letters into the appropriate number of # (sharp... geddit? any non-word non-magic non-- character would have worked) signs and dull letters into the appropriate number of - (because they have negative sharpness) signs.

+\w|-#|#-


Delete other letters and all matching pairs of # and - signs.

^(-)?.*
$1$.&


Output the number of signs remaining, prefixed by the first one if it's a -. Since the regex matches a zero-length string, I have to ensure it only matches once.

• Using a transliteration stage saves a byte: Try it online! – math junkie Jun 2 at 19:58
• @mathjunkie I had tried transliteration stages but not in that exact combination, thanks! – Neil Jun 2 at 21:50

# Javascript (Node.js), 115 bytes

let s=0;for(let b='w'+w,c=a=>Math.ceil(10*3**(4/~'OSCUAVNZMW'.indexOf(a[0])))-4||-1;b=b.slice(1);s+=9>c(b)?c(b):0);


Try it online!

This puts all the characters in a string, 'OSCUAVNZMW', and uses a formula to map the character index to the "sharpness" of each letter. The formula is:

$$\f\left(x\right)=ceil\left(10\cdot3^{\frac{4}{x}}-4\right)\$$

Where $$\f(x)\$$ is the sharpness, and $$\x\$$ is the negative index of the character + 1. Sounds convoluted, but there's a reason: the index is modified to make Javascript's string.indexOf() return 0 instead of -1.

This formula does result in 0 instead of -1 for U. However, using Javascript's || functionality in variable assignment, we can just default zero values to -1 instead.

Also, this is my first code golf, advice appreciated!

• Hey welcome. You can also generally use ~ instead of Math.ceil, since it also rounds out decimal numbers, with a bit of tweaking. – Steve Bennett Jun 3 at 8:38
• @SteveBennett Hi, I heard that but couldn't seem to get the right values using -~, even when only using floats. – pranav Jun 3 at 20:11
• ~~ is equivalent to Math.floor for positive numbers – Steve Bennett Jun 4 at 12:16

# Java (JDK), 56 bytes

s->s.map(c->("XXAVNZMWOOSSCU".indexOf(c)/2+3)%7-3).sum()


Try it online!

# Javascript (Babel Node), 131 bytes

d=>(s=0,[...d].map(c=>{s+=/[AV]/.test(c)?1:(/[NZ]/.test(c)?2:(/[MW]/.test(c)?3:(/[CU]/.test(c)?-1:(c=='S'?-2:(c=='O'?-3:0)))))}),s)


Try it online!

This is my first code golfing. I hope I'm doing it right.

I'll be glad to get some js golfing tips (before exploring new golfing languages) and overall golfing tips.

• I'd recommend checking these tips out if you haven't already :) – math junkie Jun 2 at 22:11
• @mathjunkie Hi :) thanks! well.. Most of it I already know. But there is one thing I didn't get actually that may help. It is that answer: codegolf.stackexchange.com/a/35872/95505 Do you familiar with this? – SomoKRoceS Jun 2 at 22:41
• By the way... I saw people using Header and Footer in TIO, what is it exactly? – SomoKRoceS Jun 2 at 22:42
• To your first question: I'm not too experienced with golfing in JS, but there are many others on the site who are, perhaps try asking on the nineteenth byte. To your 2nd question: The header & footer on TIO are usually used for things like a test harness -- extra snippets that illustrate how your code works, but shouldn't be included in the byte count – math junkie Jun 2 at 22:51
• Yeah. On this site, function submissions are generally allowed as an alternative to a full program – math junkie Jun 2 at 23:13

## Javascript (ES6), 65 characters

x=>x.map(l=>s-=~(z='RosLucyEvanzmw'.indexOf(l))?~(z/2)+4:0,s=0)|s


## Javascript (ES6), 67 characters

x=>x.reduce((s,l)=>~(z='RosLucyEvanzmw'.indexOf(l))?s-~(z/2)-4:s,0)


Takes an array of lowercase characters as input.

• save 2 bytes by converting reduce to map: x=>x.map(l=>s-=~(z='RosLucyEvanzmw'.indexOf(l))?~(z/2)+4:0,s=0)|s – Patrick Stephansen Jun 3 at 15:13
• Ah, thanks - I did try that, but couldn't find a result that was actually shorter. – Steve Bennett Jun 4 at 12:02
• Two useful tricks in there for me: passing nonsense args to map as a convenient place to initialise something, and realising that array|integer returns the value of the integer. – Steve Bennett Jun 4 at 12:07

# Stax, 20 19 bytes

ô≈X╙R┤╠Φyf∙H¡»₧ßc≡╡


Run and debug it

# Keg, 42 bytes

?⑷¦A1|V1|N2|Z2|M3|W3|C1±|U1±|S2±|O3±║_0™⑸⅀


Unfortunately, TIO doesn't have the latest version of the interpreter on its servers, so y'all can't try it online.

Quite literally, this maps over each character in the input, assigns it a value based on the sharpness table, and sums the stack.

# Zsh, 73 bytes

a=(O S CU : AV NZ MW)
for c (${(s[])1})(((x=a[(I)*$c*])&&(r+=x-4)))
<<<$r  Try it online! Get the last (I)ndex of the glob *$char* (with (I), it is zero if not found). If it is non-zero, add to $r. # Perl 5-p, 48 bytes $_=y/AV//+2*y/NZ//+3*y/MW//-y/CUSO//-y/SO//-y;O;


Try it online!

# Charcoal, 25 24 bytes

ＩΣＥＳ⁻÷⌕AVNZMWι²÷⌕UCSSOι²


Try it online! Link is to verbose version of code. Edit: Saved 1 byte by porting @ovs' answer. Explanation:

   Ｓ                        Input string
Ｅ                         Map over characters
ι              Current character
⌕                     Find position in
AVNZMW               Literal string AVNZMW
÷        ²             Integer divide by literal 2
⁻                       Minus
ι     Current character
⌕           Find position in
UCSSO      Literal string UCSSO
÷       ²    Integer divide by literal 2
Σ                          Take the sum
Ｉ                           Cast to string
Implicitly print


The halved Find results in one less than the sharpness/dullness, but the other find returns -1 in that case, thus correcting the value.

# 05AB1E, 3938363433 28 bytes

lε.•3и½:∍äaŠ•sk>4%„Ý§uyåi(]O


-5 thanks to @Kevin

## Explained

Original: lε.•3и½:∍äaŠ•sk>4%„Ý§uyåi(]O
Uncompressed: lε"anm vzw cso u"sk>4%"cosu"yåi(]O

l               | Take the implicit input and lowercase it.
ε               | Map the following to this input to find the sharpness of each letter:
"anm vzw cso u" |   Push the string "anm vzw cso u", representing the sharpness of each
|   letter. They are arranged in a way such the index of the character
|   is retrivable via mathematical caluclation.
|
sk              |   Swap this string, and the mapping item, and find the index of the
|   item within the big string
|
>4%             |   Increment the value, and then modulo 4 it in order to find the "raw"
|   sharpness of the string. The result will be in the range [0, 3]
|
"cosu"          |   Now comes the part where we determine if the sharpness needs to be
|   negated or not. First, we push a string containing all letters that
|   have negative sharpness
|
yå              |   We now push the letter being mapped, and test to see if it is in the
|   aforementioned string
|
i(]             |   If it is in the string, negate the sharpness value. After that,
|   close the if-statement and the loop
|
O               | Summate the stack and implicitly output the total sharpness.


## 05AB1E, 22 bytes

.•7ùZ9£nDн•#εXlSk>}O®β


Try it online!

A port of the Jelly answer suggested by @Kevin.

• I'm... not Kevin, but you can try this, adapted from the 05AB1E answer on Does base n have any Rotate-Left-Double numbers?. – sporeball Jun 3 at 5:01
• The DV can be removed and you can replace Y with y for -2. The }} can be ] for -1 (which will close all maps, loops, and if-statements at once - NOTE: you still need to use }} in the 'verify all test cases' TIO). And the "cuso" can be „Ý§u (dictionary string "cos u") for -2. 28 bytes - try it online. – Kevin Cruijssen Jun 3 at 6:27
• .•7ùZ9£nDн•#εIlSk>}O®β - 22 bytes by porting the Jelly answer, although I would also keep your current approach in your answer, since it's a completely different approach. – Kevin Cruijssen Jun 3 at 6:42

# Pyth, 28 bytes

J"MNA CSOWZV U"sm-3%xJd7@#JQ


Try it online!

# Ruby, 70 bytes

->s{'OAVNZMWCUS'.chars.zip(1226655440.digits).sum{s.count(_1)*(_2-3)}}


Try it online! (3 bytes longer because TIO doesn't suppoort ruby 2.7 _1 syntax)

# C (gcc), 88 87 bytes

Saved a byte thanks to ceilingcat!!!

char*t="O_S_CU__AVNZMW",*i;v;f(char*s){for(v=0;*s;v+=i?i-t-6>>1:0)i=index(t,*s++);v=v;}


Try it online!

# Bash + GNU utilities, 100 $$\\cdots\$$ 92 91 bytes

sed 's/[AV]/+1/g
s/[NZ]/+2/g
s/[MW]/+3/g
s/[CU]/-1/g
s/S/-2/g
s/O/-3/g
s/[A-Z]//g'<<<0$1|bc  Try it online! # Java (OpenJDK 8), 205 bytes int u(String s){if(s.length()>0){int n=0;switch(s.charAt(0)){case'M':case'W':n++;case'N':case'Z':n++;case'A':case'V':n+=4;case'O':n--;case'S':n--;case'C':case'U':n--;}return n+u(s.substring(1));}return 0;}  Try it online! • If you switch to Java 14, you can spare a lot of bytes: int u(String s){return s.isEmpty()?0:u(s.substring(1))+switch(s.charAt(0)){case'M','W'->3;case'N','Z'->2;case'A','V'->1;case'C','U'->-1;case'S'->-2;case'O'->-3;default->0;}} (173 bytes) which can still be further golfed. – Olivier Grégoire Jun 3 at 14:46 # T-SQL, 133 bytes Added some line changes to make it readable SELECT SUM(iif('P'=type,nullif(substring( translate(@,'AVNZMWCUSO','4455662210'), number,1),substring(@,number,1))-3,0)) FROM spt_values  Try it online • @KevinCruijssen I finally had the time to rewrite my answer for the related question. Thank you very much for pointing this out, the related question is worded differently. So I was able to shave off a few bytes and make some other improvements which should have been included my first answer. And yes, I did spend some hours on it – t-clausen.dk Jun 8 at 13:00 # Add++, 49 bytes D,k,@,"MCNSAO_WUZ_V"$€=12Rz€¦*bM-1+4*7%3$_ L,€kBs  Try it online! An implementation of xnor's formula, go upvote that. ## How it works The second line defines the main function which runs the helper function k over €ach character in the input, then sums the result. k works as follows: D,k,@,"MCNSAO_WUZ_V"$€=12Rz€¦*bM-1+4*7%3$_ ; Monadic function, takes a single character s ; and returns the sharpness of s ; For example, s = 'C' STACK = ['C'] "MCNSAO_WUZ_V" ; Push this string STACK = ['C' 'MCNSAO_WUZ_V']$€=			   ; Check each character for equality with s		STACK = [[0 1 0 0 0 0 0 0 0 0 0 0]]
12R		   ; Push [1 2 3 4 5 6 7 8 9 10 11 12]			STACK = [[0 1 0 0 0 0 0 0 0 0 0 0] [1 2 3 4 5 6 7 8 9 10 11 12]]
z		   ; Zip arrays						STACK = [[[0 1] [1 2] [0 3] [0 4] [0 5] [0 6] [0 7] [0 8] [0 9] [0 10] [0 11] [0 12]]]
€¦*		   ; Product of each					STACK = [[0 2 0 0 0 0 0 0 0 0 0 0]]
bM	   ; Maximum (call the result r)			STACK = [2]

# SimpleTemplate, 118 bytes

Yes, not short at all. :(
My language has absolutely no way what-so-ever of creating an array with keys and values defined, so, everything has to be generated.

{@setA 1,1,2,2,3,3,-1,-1,-2,-3}{@setL"AVNZMWCUSO"}{@eachL}{@setA.[_]A.[__]}{@/}{@eachargv.0}{@incbyA.[_]R}{@/}{@echoR}


This simply outputs the sharpness of the word.

Ungolfed

The code above is just a mess! Here's a readable version:

{@set rates 1,1,2,2,3,3,-1,-1,-2,-3}
{@set letters "AVNZMWCUSO"}
{@each letters as letter key i}
{@set rates.[letter] rates.[i]}
{@/}

{@set result 0}

{@each argv.0 as char}
{@inc by rates.[char] result}
{@/}
{@echo result}


Should be fairly easy to understand.

The {@inc by rates.[char] result} will increment the result by rates.[char] (similar to result += rates[char] on other languages).

You can try this on http://sandbox.onlinephpfunctions.com/code/54adfe0b61fe1a12f3be085d0e058123a7842627
There you have the golfed version, ungolfed and a function implementation (the {@echo result} is replaced with {@return result}).