text:
Pairs with Sum divisible by 5
You will be given two integers \$M\$ and \$N\$. Find the number of pairs \$(x,y)\$ such that \$1 \le x \le M\$, \$1 \le y \le N\$ and \$(x+y)\bmod5 = 0\$.
For example, if \$M = 6\$ and \$N = 12\$, pairs which satisfies such conditions are, \$(1,4), (4,1), (1,9), (2,3), (2,8), (3,2), (3,7), (3,12), (4,6), (6,4), (4,11), (5,5), (5,10), (6,9)\$
Total \$14\$.
Sample
Input : 6 12
Output: 14

Input : 11 14
Output: 31

Input : 553 29
Output: 3208

Input : 2 2
Output: 0

Input : 752486 871672
Output: 131184195318

This is a code-golf challenge so code with lowest bytes wins!
Update
Jonathan Allan's solution has the smallest code size, 5 bytes. However, it doesn't produce an answer for the last given test.
I have decided to go with the next answer with the shortest size that produces the correct answer for the largest test, there is a tie between two golfers who competed neck-to-neck.
I proudly present the winners of this challenge Lyxal and Kevin Cruijssen with only 7 bytes code! Congratulations!
As many of you, I found Arnauld's answer most helpful in finding the correct solution. So, I am accepting Arnauld's answer.
Thank you, Golfers!

Why are the pairs (4,5) and (5,4) listed in your example?

Thanks, It should be $(4,6), (6,4)$. I have fixed it. :)

I know many here will agree to select your answer as it is the most helpful for finding the solution. Both winners also found it useful, so this answer deserves to be at the one at the top of the list. Thank you for your nice explanation :)

Ah, ok. I didn't notice the update you made to the post.

JavaScript (ES6), 29 bytes
Takes input as (m)(n).
m=>g=n=>n&&(m+n%5)/5+g(n-1)|0

Try it online!
How?
Let's consider a grid of width \$m\$ with 1-indexed coordinates and an X mark on each cell for which:
$$(x+y)\equiv 0\pmod 5$$
Here are the first few rows for \$m=9\$:
   | 1 2 3 4 5 6 7 8 9
---+-------------------
 1 | - - - X - - - - X
 2 | - - X - - - - X -
 3 | - X - - - - X - -
 4 | X - - - - X - - -
 5 | - - - - X - - - -
 6 | - - - X - - - - X
 7 | - - X - - - - X -

Computing \$m+(y\bmod5)\$ is equivalent to left-padding each row in such a way that all X marks are vertically aligned and appear on columns whose index is a multiple of \$5\$.
With such a configuration, the number of marks is directly given by \$\lfloor{L_y/5}\rfloor\$, where \$L_y\$ is the updated length of the \$y\$-th row.
 y | y%5 |         padded row         | length | // 5
---+-----+----------------------------+--------+------
 1 |  1  |  + - - - X - - - - X       |   10   |   2
 2 |  2  |  + + - - X - - - - X -     |   11   |   2
 3 |  3  |  + + + - X - - - - X - -   |   12   |   2
 4 |  4  |  + + + + X - - - - X - - - |   13   |   2
 5 |  0  |  - - - - X - - - -         |    9   |   1
 6 |  1  |  + - - - X - - - - X       |   10   |   2
 7 |  2  |  + + - - X - - - - X -     |   11   |   2
---+-----+----------^---------^-------+--------+------
         |  0 0 0 0 0 0 0 0 0 1 1 1 1 |
         |  1 2 3 4 5 6 7 8 9 0 1 2 3 |
We use this method to recursively compute the number of marks on each row.

Python 2, 32 bytes
lambda m,n:(m*n+abs(m%5-~n%5))/5

Try it online!
Adapts an idea from Arnauld. We estimate that \$1/5\$ of the \$mn\$ pairs will satisfy the mod-5 condition. Th actual value may be one higher depending on the mod-5 values of \$m\$ and \$n\$, and we bump up \$mn\$ a bit so that these make it pass the next multiple of 5.

34 bytes
lambda m,n:m*n/5+(0<-m%5*(-n%5)<5)

Try it online!
34 bytes
lambda m,n:m*n/5+(m*n%5+m%5+n%5)/9

Try it online!
36 bytes
lambda m,n:m*n/5+((5-m%5)*(5-n%5)<5)

Try it online!
36 bytes
lambda m,n:m*n/5+(abs(m%5+n%5*1j)>4)

Try it online!
36 bytes
lambda m,n:m*n/5+(m%5*5/4+n%5*5/4>5)

Try it online!
36 bytes
lambda m,n:m*n/5+(m%5+n%5+m*n%5/4>5)

Try it online!

34 bytes with a shorter residual correction condition.

@SurculoseSputum Just posted that a moment before :)

What is 1j in the 3rd code sample?

@CodyGray It's the complex unit $i$.

It is lesser known, but python supports native complex units :-)

@ghosts_in_the_code I get failing tests with that, can you check? TIO

@ghosts_in_the_code 13%5 = 3 is what I expect. It's modulo, finding a value that's equivalent to 13 when considered mod 5, and that lies in in range(0,5), which in this case is 3.

@xnor Apologies, I had forgotten how mod works in python on negative integers

Jelly, 5 bytes
p§5ḍS

Try it online!
How?
p§5ḍS - Link: positive integer, M; positive integer N
p     - (implicit [1..M]) Cartesian product (implicit [1..N])
 §    - sums
  5ḍ  - five divides? (vectorises)
    S - sum

J, 20 bytes
1#.[+/\0=5|1}.1+i.@+

Try it online!
The standard O(m*n) cartesian product solution saves 3 bytes [:+/@,0=5|2++/&i., but I figured I'd try a different approach:
A solution using a sliding window.
I think J is able to optimize this approach automatically into O(m+n)... in any case, I get an out of memory error on the final test case on TIO with the cartesian product approach but not with this one
Let's take 6 f 12 as an example:

Generate the numbers 1 through 18 1+i.@+:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18

Kill the first 1 1}.:
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18

Turn it into a 0-1 list indicating which numbers are divisible by 5 0=5|
0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0

Create a sliding window of length of the left arg (either arg would work though), summing each piece of the window [+/\:
       1
  /`````````\ etc...
0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0
\_________/
      1

Final result: 1 1 1 2 1 1 1 1 2 1 1 1

Sum of all those numbers 1#.:
14

Python 3, 50 bytes
lambda n,m:sum((i//n+i%n)%5==3for i in range(n*m))

Try it online!

APL (Dyalog Unicode), 11 bytes
Full program.
≢⍸0=5|+/¨⍳⎕

Try it online! (largest test case would work with 3TB RAM)
Reads quite literally as the problem specification:
≢ count ⍸ where 0= 0 equals 5| the mod-5 of +/ the sum of ¨ each of ⍳ all indices until ⎕ the input.

MATL, 9 bytes
:i:!+5\~z

Try it online!
How it works
:    % Implicit input: M. Range [1 2 ... M]
i:   % Input: N. Range [1 2 ... N]
!    % Transpose
+    % Add, element-wise with broadcast. Gives an N×M matrix
5    % Push 5
\    % Modulus, element-wise
~    % Negate
z    % Number of nonzeros. Implicit display

Pyth, 12 11 bytes
-1 thanks to @Mukundan
lf!%sT5*FSM

Try it online!
lf!%sT5*FSM
         SM    Map 1-indexed range to each input
       *F      Cartesian product of the two ranges
 f              Filter by:
    sT          - sum of elements..
  !%  5         - .. is divisible by 5
l               Take the length

-1 byte by replacing +F with s. Try it online!

@Mukundan Good point, thanks

Python 2, 36 bytes
f=lambda m,n:m and(m%5+n)/5+f(m-1,n)

Try it online!
Use @Arnauld's formula, be sure to checkout and upvote his answer!

Original solution, using a longer formula:
Python 2, 42 41 bytes
-1 byte thanks to @xnor!
f=lambda m,n:m and(n%5>~m%5)+n/5+f(m-1,n)

Try it online!
For each number i between 1 to m, there are (n%5+i%5>4)+n/5 numbers between 1 to n that can be paired with i.

R, 40 bytes
function(m,n)sum(!(rep(1:m,e=n)+1:n)%%5)

Try it online!

Also 40 bytes

Ha ha!  Yes!  I tried it, too!  All the possibly-useful built-in R functions seem to have too-many characters to be useful... (outer=40chars, mapply=41chars, matrix=42chars...).  What a frustrating golfing language!

39 bytes with a warning.

That's better than mine!  You need to post it!

05AB1E, 9 8 7 bytes
L5%+5÷O

Try it online!
A 100% port of @Arnauld's Javascript answer

-1 byte by porting Arnauld's JavaScript answer: L5%+5÷O.

MathGolf, 7 bytes
╒5%+5/Σ

Port of @Arnauld's JavaScript answer, so make sure to upvote him!
Try it online.
Explanation:
╒        # Push a list in the range [1, (implicit) first input]
 5%      # Modulo-5 on each value in the list
   +     # Add the second (implicit) input to each
    5/   # Integer-divide each value by 5
      Σ  # And sum the list
         # (after which the entire stack joined together is output implicitly as result)

K (ngn/k), 15 bytes
+//~5!+/:/1+!:'

Try it online!

Japt, 11 10 bytes
õ ï+Võ)èv5

Try it
õ ï+Võ)èv5     :Implicit input of integers U & V
õ              :Range [1,U]
  ï            :Cartesian product with
    Võ         :  Range [1,V]
   +           :  Reduce each pair by addition
      )        :End Cartesian product
       è       :Count the elements
        v5     :  Divisible by 5

Wolfram Language (Mathematica), 56 bytes
Length@Solve[1<=x<=#&&1<=y<=#2&&Mod[x+y,5]==0,Integers]&

Try it online!

perl -M5.010 -al, 49 bytes
for(1..$_){for$y(1..$F[1]){($_+$y)%5||$c++}}say$c

Try it online!
This is a pretty stupid, "iterate over all pairs, count the ones where sum mod 5 equals zero", solution. There's a nicer formula, but I couldn't (yet) reduce that down enough. It will take a very long time to calculate the answer to (752486, 871672), but there was no time limit stated.
The TIO link has a $c = 0; in the header. This isn't needed for a single line solution; it's only there to make it work with multiple inputs (and as such, TIO won't count those bytes).

Same logic, 41 bytes

Charcoal, 16 12 bytes
ＩΣ÷⁺﹪…·¹Ｎ⁵Ｎ⁵

Try it online! Link is to verbose version of code. Port of @KevinCruijssen's answer, although that's apparently a port of @Arnauld's answer. Explanation:
     …·        Inclusive range from
       ¹        Literal `1` to
        Ｎ       First input number
    ﹪           Vectorised Modulo
         ⁵      Literal `5`
   ⁺            Vectorised Plus
          Ｎ     Second input number
  ÷             Vectorised Integer divide by
           ⁵    Literal `5`
 Σ              Take the sum
Ｉ               Cast to string
                Implicitly print

Previous 16-byte brute force answer:
ＮθＩ№⭆Ｎ⭆θ﹪⁺²⁺ιλ⁵0

Try it online! Link is to verbose version of code. Explanation:
Ｎθ

Input M.
⭆Ｎ⭆θ

Input N and loop over N and M, casting the result to a string.
﹪⁺²⁺ιλ⁵

Take the sum modulo 5, adjusted for 1-indexing (sigh).
Ｉ№...0

Count the 0s in the result.

C (gcc), 36 bytes
f(n,m){n=m*n/5+((5-m%5)*(5-n%5)<5);}

Try it online!
A port of one of xnor's formulas.

Erlang (escript), 69 bytes
Posting before I run out of time.
f(X,Y)->[[A,B]||A<-lists:seq(1,X),B<-lists:seq(1,Y),((A+B)rem 5)==0].

Try it online!

Vyxal s, 6 bytes
ɾ5%+5ḭ

Try it Online!


text:
Judge a book by its cover
Overview
The cover of a book will be provided in the following ASCII format:
______
|    |
|    |
|    |
|    |
------

The input can be in any reasonable format (eg. a list of strings, a nested list of characters, etc.)
Inside the "borders" of the cover, other printable ascii characters might appear, which contribute to the "popularity index" (the definition of which has been completely made up for this challenge).
Popularity Index
Let the popularity index of a book be the number of unique characters that appear on the book cover (this does not include space characters or the edges of the cover). A book may have no unique characters on the cover (the cover is blank), in which case the popularity index is 0.
Examples
______
|\/ /|
|/\/ |
| /\/|
|/ /\|
------

Two unique characters (/ and \) so the popularity index is 2.
______
| / /|
|/ / |
| / /|
|/ / |
------

One unique character (/), so the popularity index is 1
______
|    |
|    |
|    |
|    |
------

No unique characters, the popularity index is 0
______
|\^$@|
|/\/ |
| 456|
|/ /\|
------

8 unique characters, so the popularity index is 8.
______
|\^$@|
|987 |
| 456|
|/hi!|
------

14 unique characters, so the popularity index is 14.
______
|////|
|////|
|////|
|////|
------

One unique character (/), so the popularity index is 1.
Damaged Books
The edges of the book might also be damaged:
Top         Left        Right       Bottom

_~____      ______      ______      ______
|    |      |    |      |    |      |    |
|    |      |    |      |    |      |    |
|    |  or  {    |  or  |    }  or  |    |
|    |      |    |      |    |      |    |
------      ------      ------      ---~--

The book may have several of these "scratches". The scratches at the top and bottom will always be represented by a ~, while the scratches on the left and right will always be represented by a { and } respectively. Each scratch will decrease the popularity index by 1.
As a result, it is possible for a book to have a negative popularity index.
Task
Given an ASCII representation of a book cover in the format described above, determine the "popularity index" of the book
Assumptions

You can assume that the characters |, _, and - will not appear elsewhere on the book cover (only on the edges). However, the scratch characters ({, }, and ~) may appear on the book cover in which case they should be treated as any other unique character on the cover.
All characters appearing on the cover will be printable ascii
Note that spaces do not count as a unique character. As the examples above show, a "blank" cover is one that only contains spaces.
The book will always be the same size (height and width are fixed). Hence, the popularity index will never exceed 16.

Scoring
This is code-golf. Happy golfing!
More Examples
______
|{. }|
|/. /|
|/. /}
|/. /|
------

4 unique characters ({, }, . and /) and one scratch (} on the right) so the popularity index is 3.
______
{    |
| .. |
|    }
{    |
--~~--

One unique character (.) and 5 scratches so the popularity index is -4.
Test Cases
______
|    |
|    |
|    |
|    |
------    ->    0

______
|\/ /|
|/\/ |
| /\/|
|/ /\|
------    ->    2

______
| / /|
|/ / |
| / /|
|/ / |
------    ->    1

______
|\^$@|
|/\/ |
| 456|
|/ /\|
------    ->    8

______
|!!!!|
|+  +|
| ** |
|<**>|
------    ->    5

______
|\^$@|
|987 |
| 456|
|/hi!|
------    ->    14

______
|THIS|
| is |
| a. |
|BOOK|
------    ->    11

______
|////|
|////|
|////|
|////|
------    ->    1

______
|abcd|
|efgh|
|ijkl|
|mnop|
------    ->    16

______
|{. }|
|/. /|
|/. /}
|/. /|
------    ->    3

______
{    |
| .. |
|    }
{    |
--~~--    ->    -4

~~~~~~
{    }
{    }
{    }
{    }
~~~~~~    ->    -20

______
|~~~~|
|.   |
{....}
|.   |
------    ->    0

______
|{~~}|
|    |
|    |
|    |
------    ->    3

__~~__
|{~~}|
|    |
|    |
{    |
-----~    ->    -1

Sandbox post

Why am I thinking of Pawn Stars' book expert while reading this?... :p

The more scratches a book has, the more popular the book is. Like, I borrowed TAOCP from the library with about 56 scratches on the cover of a book.

Would a single string (or a flat array) of 36 characters be a valid input? (i.e. the book without any line feed)

Welp, today I learnt that if I want to become a New York Time's Number One Besteller, I simply ensure the cover is in good condition and I put unique gibberish on the front. Good to know!

@Arnauld Yes that's fine, since the length will always be the same

Is the top supposed to be underscores or dashes?

@BrianMinton The top of the book is composed of underscores

By the way there's a software challenge at the Internet Archive too: https://blog.archive.org/2019/01/05/helping-us-judge-a-book-by-its-cover-software-help-request/

The examples in the "Damaged books" section have dashes at the top.

@BrianMinton Thanks for pointing that out. It was a copy-paste error, should be fixed now.

@Λ̸̸ I Believe it is working in the opposite way: the more popular a book is, the more scratches it will have.

I joined this community just to upvote this question!! It's fascinating!

This is so complicated.

This is relevant: https://www.gocomics.com/getfuzzy/2012/02/19

JavaScript (ES6),  75 74  73 bytes
Takes a single string of 36 characters as input, i.e. without any line feed.
s=>s.replace(o=/[^ |_-]/g,(c,n)=>t+=n*9%56%37%9&1?o[c]^(o[c]=1):-1,t=0)|t

Try it online!
How?
We filter out spaces, pipes, underscores and hyphens:
/[^ |_-]/g

It means that we're going to match only:

scratches on the border (-1 point)
or non-space characters on the cover (+1 point the first time each of them appears)

Given the 0-indexed position \$n\$ of the character in the input string, the shortest solution I've found so far to figure out if we're located on the border or on the cover is the following convoluted modulo chain:
$$\big(((9\times n)\bmod 56)\bmod 37\big)\bmod 9$$
which gives:
$$\begin{pmatrix}
0&1&2&3&4&5\\
6&7&8&9&10&11\\
12&13&14&15&16&17\\
18&19&20&21&22&23\\
24&25&26&27&28&29\\
30&31&32&33&34&35
\end{pmatrix}
\rightarrow
\begin{pmatrix}
\color{blue}0&\color{blue}0&\color{blue}0&\color{blue}0&\color{blue}0&\color{blue}8\\
\color{blue}8&7&7&7&7&\color{blue}6\\
\color{blue}6&5&5&5&5&\color{blue}4\\
\color{blue}4&3&3&3&3&\color{blue}2\\
\color{blue}2&1&1&1&1&\color{blue}0\\
\color{blue}0&\color{blue}0&\color{blue}8&\color{blue}8&\color{blue}8&\color{blue}8
\end{pmatrix}$$
With even numbers on the border and odd numbers on the cover.
Generalization of the formula
Given a square matrix of width \$w>3\$ whose cells are indexed from \$0\$ to \$w^2-1\$, the \$n\$-th cell is located on a border iff the result of the following expression is even:
$$\big(((p\times n)\bmod m_0)\bmod m_1\big)\bmod p$$
with:
$$p=2w-3$$
$$m_0=2\cdot (w-1)^2+w$$
$$m_1=2\cdot (w-2)^2+w-1$$
Try it online!
NB: This is an empirical result that I didn't attempt to prove.

How did you come up with that modulo chain? Did you brute force it? Why did you attempt the modulo chain approach in the first place? Seems like there's a new approach to solving matrix problems here which I haven't seen before!

@FilipHaglund My initial formula was n/6%5<1|n%6%5<1 (which gives the opposite result) and I was not able to reduce it further with conventional tricks. I know from experience that such modulo chains often work well if the input domain is arranged in a non-random manner and the output domain is small. So I tried that, and it was brute-forced indeed.

@FilipHaglund See my post update for an a-posterioti generalization of the formula.

@Arnauld From your mod-ops, I found another possibility to figure out whether a number is on the edge in this case. Don't know whether it will help golfing, though... Take a look at the matrix n² % 35. You'll see that perfect squares are on the edges. Best, Joe.

@Arnauld...
(4*sqrt(n²mod35)&1)+(26*sqrt(n²mod35)&1)

@Joe85AC I think the best we can do is (n*n%35)**.5%1. It's pretty neat and looks simpler than the current formula, but it's actually 1 byte longer.

@Arnauld -- With PHP, I just arrived at fmod(sqrt($s*$s%35),1) which is basically the same :-)

Here's another one: 25%(n*n%35)%7>2 again, +1byte, unfortunately

@Arnauld -- (n*n%35)**.5%1 works only for the 6x6-square. So your approach that allows for generalization is the much better one, I have to say!

@Joe85AC Interestingly, your formula works for 1, 2, 4, 6, 12, 18, 30, 42, 60, 72, 102, 108, 138, 150, ...

@Arnauld -- That seems to be A014574. The sequence of the average of two twin primes... Seems we're on the verge of publishing a "number theory" paper... :-)
https://mathworld.wolfram.com/TwinPrimes.html

@Arnauld http://rwth.it/tpt.pdf

@Joe85AC So you did it. :) Cool.

@Arnauld I actually didn't have full equivalency up until now. Check the new version if it's of some interest for you: rwth.it/tpt.pdf Anyways, I don't know if this is only sort of a "warm up exercise" or an "actual" result.

Python 2, 73 bytes
Input is taken as a 2D list \$ b \$. Output is the popularity index of \$ b \$.
lambda b:len({j.pop(1)for j in b[1:5]*4}-{' '})-sum(map(`b`.count,'{}~'))

Try it online!

The left part of the expression does two things: adds up the popularity index inside the cover, and removes its characters. The right part subtracts the damaged characters from the remaining characters.

J, 51 41 40 bytes
_1#.1#.(+./~#:33)e.&'{}~'`(' '~:~.)/.&,]

Try it online!
-11 bytes thanks to Bubbler!
Both the function table trick to compress #:@68208171135 down to +./~@#:@33, and the "negative one base trick" are due to them.
how

+./~@#:@33 Let's begin with this phrase, which produces:
1 1 1 1 1 1
1 0 0 0 0 1
1 0 0 0 0 1
1 0 0 0 0 1
1 0 0 0 0 1
1 1 1 1 1 1

How?  33 in binary #:@33 is 1 0 0 0 0 1.  Then create an "or" function table +./ of this list with itself.  It is easy to verify this will be 1 exactly on the border cells.
/.&,] Flatten the input and use the mask above to split the input into two groups, using J's Key /. adverb:
Apply this verb to the              Damage:
1st group, the edges:    -------->  Is each character an
     |                              element of '{}~'?
 ___/____                           Returns a 0-1 list.
 e.&'{}~'`(' '~:~.)
          ^^^^^^^^^
               |                    Reputation:
    And this one to the second -->  Is space not equal
    (the interior)                  to each character
                                    of the uniq? Also
                                    a 0-1 list.

We now have two 0-1 lists.  For example:
Reputation: 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0
Damage:     1 1 0 1 1

J will extend the shorter list to the length of the longer, filling it with zeros:
0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0
1 1 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0

1#. Sum rowwise:
1 4

_1#. This uses a -1 base as a golfy way to subtract 1 from 4. By definition of base:
(1 * -1^1) + (4 * -1^0)
(1 * -1) + (4 * 1)
3

41 bytes.

Save one more byte with +./~@#:@33 -> (+./~#:33).

@Bubbler Impressive stuff. I learned at least 2 new tricks. The compression of the mask using +. is great, and I've never seen negative base used like that either. Hats off to you!

Python 2, 68 bytes
lambda s:len(set((s[8:-6]*4)[::7])-{' '})-20+sum(map(s.count,'_|-'))

Try it online!
Instead of counting defects on the borders, counts non-defects _|- throughout and subtract from 20, using the rule that only the borders can contain these characters. The (s[8:-6]*4)[::7] is a nice string-slicing way to extract the characters on the cover without borders.

@dingledooper Nice one, you should post an answer for the outgolfing bounty.

05AB1E, 25 22 bytes
4Fćˆøí}˜ðKÙg¯˜…~{}S¢O-

-3 bytes by taking inspiration of @JonathanAllan's Jelly answer, so make sure to upvote him!
Input as a matrix of characters.
Try it online or verify all test cases.
Original 25 bytes approach:
|¦¨ε¦¨}ðýê¦gIJ…_|-S¢O20-+

Default input as loose lines in STDIN.
Try it online or verify all test cases.
Explanation:
4F            # Loop 4 times:
  ć           #  Extract head; pop and push remainder-matrix and first line separately
   ˆ          #  Pop and add this first line to the global array
    ø         #  Zip/transpose the remaining matrix; swapping rows/columns
     í        #  Reverse each line
              #  (`øí` basically rotates the matrix once clockwise)
}˜            # After the loop: flatten the remaining matrix
  ðK          # Remove all spaces
    Ù         # Uniquify it
     g        # And pop and push the amount of remaining unique (non-space) characters
¯˜            # Push the global array, and flatten it as well
  …~{}        # Push string "~{}"
      S       # Convert it to a list of characters: ["~","{","}"]
       ¢      # Count each in the flattened global array
        O     # Sum those
-             # Subtract the two from one another
              # (after which the result is output implicitly)

|             # Push all lines of inputs as a list of strings
 ¦¨           # Remove the first and last lines
   ε  }       # Map over each line:
    ¦¨        #  And also remove the first and last character of each line
       ðý     # Join everything together with space delimiter
         ê    # Uniquify and sort the characters
          ¦   # Remove the first character (the space)
           g  # And pop and push its length
I             # Push the input-list of lines again
 J            # Join them all together
  …_|-        # Push string "_|-"
      S       # Convert it to a list of characters: ["_","|","-"]
       ¢      # Count each in the joined input
        O     # Take the sum of those counts
         20-  # Subtract 20 from this
+             # Add the two together
              # (after which the result is output implicitly)

This sure beats what I was thinking of. I was heading down a probably 50-100 byte answer before I saw this. Good job!

@Lyxal And here I actually had the feeling 1 or 2 more bytes could be saved. :) Out of curiosity, what approach did you had in mind? One with a matrix similar as the J/APL answers?

I was going to port my literal interpretation approach in my python answer. As I said, many more bytes than yours. This answer is most likely golfable ;P

Actually, this seems like a more golfed version of my said approach. Huh. Who'da thunk it?

@Lyxal Yeah, the count part is rather similar indeed. First part is a bit different, although that's to expected, since I doubt unique and sort the characters for example is cheap in Python. ;)

Jelly, 25 bytes
Ḣ;ɼȧZṚµ4¡FQḟ⁶,®f“~{}”¤Ẉ_/

A full program accepting a single argument as a list of Python strings which prints the result.
Try it online! Or see the test-suite.
How?
Ḣ;ɼȧZṚµ4¡FQḟ⁶,®f“~{}”¤Ẉ_/ - Main Link: list of lists of characters (the lines)
      µ4¡                 - repeat this monadic chain four times:
Ḣ                         -   head (gives us the head AND modifies the chain's input)
  ɼ                       -   recall from the register (initially 0) and apply & store:
 ;                        -     concatenate
    Z                     -   transpose the chain's (now modified) input
   ȧ                      -   logical AND (the concatenate result with the transpose, to get us the transpose of the chain's input)
     Ṛ                    -   reverse (together with the transpose this is rotate one quarter)
         F                - flatten (what we have left is the cover's inner characters)
          Q               - de-duplicate
           ḟ⁶             - discard spaces
                     ¤    - nilad followed by link(s) as a nilad:
              ®           -   recall from the register (the cover's edge characters)
               f“~{}”     -   keep only "~{}" characters
             ,            - pair (the two lists of characters)
                      Ẉ   - length of each
                        / - reduce by:
                       _  -   subtraction
                          - implicit print (a list containing only one entry prints just that entry)

Python 3, 106 102 80 bytes
lambda b:len({*(b[8:12]+b[15:19]+b[22:26]+b[29:33])})-21+sum(map(b.count,"-|_"))

Try it online!
I know there's a shorter Python answer, and I know that this isn't the most optimal method. But I had come up with a solution which I golfed, and I'm posting it for the sake of completion. -2 thanks to @Kevin and -2 thanks to OVS. A further -22 thanks to @mathjunkie.

return len(set(m))-s-1 can be return~s+len(set(m)) for -2

And another -1 by switching to Python 2 and use print instead of return. :) Try it online.

Back to Python 3 with {*m} instead of set(m) ;). And if you replace 20 with 19 the -1 can be removed, this doesn‘t save any bytes though.

80 bytes, by converting to a lambda and using map instead of c=b.count: Try it online!

This currently fails for cases with no spaces. There are a few ways to deal with this...

Python 2, 66 bytes
lambda s:len(set((s[8:-6]*4)[::7]+' '))-21+sum(map(s.count,'_|-'))

Try it online!

A slight improvement over @xnor's answer. Instead of removing spaces from the cover, we add spaces to the cover, and then subtract 1.

APL (Dyalog Unicode), 42 35 bytes
-6 thanks to Bubbler.
Anonymous tacit prefix function. Requires ⎕IO←0 (0-based indexing).
-⍨/(,∘.⍱⍨6⍴5↑1){≢∪⍣⍺⊢⍵~⊃⍺↓⊂'_|-'}⌸∊

Try it online!
∊ ϵnlist (flatten)
(…){…}⌸ apply the below function to each of the two groups identified by 1s and 0s in:
 5↑1 take five elements from 1; [1,0,0,0,0]
 6⍴ cyclically reshape that to length six; [1,0,0,0,0,1]
 ∘.⍱⍨ NOR-table of that;
  [[0,0,0,0,0,0],
   [0,1,1,1,1,0],
   [0,1,1,1,1,0],
   [0,1,1,1,1,0],
   [0,1,1,1,1,0],
   [0,0,0,0,0,0]]
 , ravel (flatten)
 The following function will be applied to each multiset (0: edge characters; 1: inner characters):
 '_|-' a string; "_|-"
 ⊂ enclose it; ["_|-"]
 ⍺↓ drop 1 element if the set is that of inner characters, otherwise drop 0 elements
 ⊃ get the first element; the above string or "   " if gone
 ⍵~ remove those characters from the edge/inner set
 ∪⍣⍺⊢ if inner, then get the unique of that, else leave as-is
 ≢ tally that
-⍨/ subtract the first count from the last count

36 bytes

Charcoal, 49 41 bytes
≔⪫Ｅ⁶Ｓ¶θＰθＢ⁶ ＷΦＫＡ¬№υκ⊞υ⊟ι⎚Ｉ⊖⁻Ｌυ⁻²⁰ΣＥ-_|№θι

Try it online! Link is to verbose version of code. Explanation:
≔⪫Ｅ⁶Ｓ¶θ

Input the cover.
ＰθＢ⁶

Print the cover, but immediately erase the border.
ＷΦＫＡ¬№υκ⊞υ⊟ι

Collect all the unique characters, which always includes a space, as we used that to erase the border. (I feel that I should be able to use Ｗ⁻ＫＡυ⊞υ⊟ι but Charcoal crashes when I try.)
⎚Ｉ⊖⁻Ｌυ⁻²⁰ΣＥ-_|№θι

Clear the canvas and output the popularity, adjusting for the extra space collected and the number of missing border characters in the original string.
I also tried counting the popularity by directly inspecting the string instead of printing it and deleting the border but it turns out that that costs bytes overall.
Previous 49-byte solution does not assume that edge characters only appear on the edge:
≔⪫Ｅ⁶Ｓ¶θＰθＢ⁶ ＷΦＫＡ¬№υκ⊞υ⊟ιＰθ↘ＵＯ⁴ ≔ΦＫＡ№{~}ιθ⎚Ｉ⊖⁻ＬυＬθ

Try it online! Link is to verbose version of code. Explanation:
≔⪫Ｅ⁶Ｓ¶θ

Input the cover.
ＰθＢ⁶

Print the cover, but immediately erase the border.
ＷΦＫＡ¬№υκ⊞υ⊟ι

Collect all the unique characters, which always includes a space, as we used that to erase the border. (I feel that I should be able to use Ｗ⁻ＫＡυ⊞υ⊟ι but Charcoal crashes when I try.)
Ｐθ↘ＵＯ⁴

Print the cover again, but this time keep only the border.
≔ΦＫＡ№{~}ιθ

Collect all the {, ~ and } characters.
⎚Ｉ⊖⁻ＬυＬθ

Clear the canvas and output the calculated popularity, adjusting for the extra space collected.

@mathjunkie Sorry about that, I introduced a silly typo while golfing (ω should have been ¶).

perl -M5.010 -n -0543, 82 bytes
$i--for/\}$|^\{|~(?=[-_~]*$)/gm;s/^[-_~]+$|^.|.$//gm;$c{$_}++for/\S/g;say$i+keys%c

Try it online!
First, the penalty is calculated by counting {s which start a line, }s which end a line, and any ~ in the top and bottom lines. We then strip off the boundaries, and count the number of unique non-space characters; the penalty is added to that, to get the final score.
Reads a single cover from STDIN, writes the score to STDOUT.

Java (OpenJDK 8), 326 320 319 305 bytes
int f(List<Character>[]c){int s=0,i=3;c[0].addAll(c[5]);for(;i>1;)S(50*i---55,c[0]);c[5].clear();for(;i<5;c[5].addAll(c[i++]))s+=c[i].remove(0)-c[i].remove(4);S(32,c[5]);for(;c[5].size()>0;i++)S(c[5].get(0),c[5]);return i-5-c[0].size()+s;}void S(int x,List c){c.removeAll(Collections.singleton((char)x));}

Try it online!
THANKS to Muskovets's suggestion of using List (-4), I also omitted the generic type in the second method (List instead of List< Character>)
Takes input as List< Character>[] and outputs an int.
It uses 2 methods (I'm not entirely sure how "allowed" that is)
See inside for comments and ungolfed and alternate solutions (including a single method one).
Interesting part: the ascii values for { | } are 123, 124, 125. That means that this part changes the popularity index by:
for each row: the left char minus the right char
{...} 123 - 125 = -2
{...| 123 - 124 = -1
|...} 124 - 125 = -1
|...| 124 - 124 = 0
It only works with only exactly the given possible inputs, none else (illegal inputs like }...{). Pretty lucky.
This was done with ArrayList, but possibly some other Collection with a shorter name or better methods would be better. Edit: indeed there is: Vector. updated. (Now List)

Takes input as String[]. Only one method.
Java (OpenJDK 8), 267 261 bytes
int f(String[]c){int s=0,i=4;c[0]=c[0].replace("_","")+c[5].replace("-","");c[5]="";for(;i>0;c[5]+=c[i--].substring(1,5))s+=c[i].charAt(0)-c[i].charAt(5);for(;c[5].trim().length()>0;i++)c[5]=c[5].replace(c[5].trim().substring(0,1),"");return i-c[0].length()+s;}

Try it online!

Uhm... List is a collection. It'd give you -4 :)

+1 for triple minus in F(50*i---55

301 bytes

Perl 5 + -p0 -MList::Util+uniq, 55 bytes
s/^.{5}|.
.|.{5}$/$\-=$&=~y!~{}!!;""/ge;$\+=uniq/\S/g}{

Try it online!

PHP, 104 93 bytes
Original code from yesterday:

while($s<35+$c=ord($b[$t=$s++]))$p-=$t>5&$s<31?$t%6?$s%6?0&$i[$c]=1:$c:-$c:$c%5;echo$p+count($i)-$i[32];

Try it online!

Improved with a mod-expression from @Arnauld, cf. his solution.

for(;$s<36;$s++)$p-=$s*9%56%37%9&1?!$i[$b[$s]]=1:ord($b[$s])%29%8>0;echo$p+count($i)-$i[" "];

Try it online!

Explanation

I was using the fact that char codes of intact top and bottom edges are %5=0, and left and right edges have char codes that are in sequence.
I've now researched the char codes of the six edge chars a little better.Try [45,95,124,123,125,126]%18%15. There are also other options like $c%29%8==0 and so on.

Please Help Me Get Better
I appreciate any comments or suggestions.

Hi, welcome to the site! I'm not a PHP golfer myself but you can check out tips for golfing in PHP for some suggestions (there's a similar thread for basically every language).

Could you also add a link to somewhere your code could be tested online, such as: Try it online! (for that site, you'll probably need to add some code to the header and footer to demonstrate how to call your function/program)

@mathjunkie -- I added a link. Thanks for the question!

Bash + standard tools 121 Bytes
bc<<<$(sed '1d;$d;s/.//;s/.$//' $1|grep -o '[^ ]'|sort -u|wc -l)-$(sed -E '2,${$!s/(.)..../\1/}' $1|tr -cd '[~{}]'|wc -c)

Input is a file of the book.
Try it online! (+6 bytes because input needs to be a string here)

C (gcc), 174 bytes
//N(c)=p-=*s++!=c
p,v,i,j,k;f(char*s){char u[16]={};for(v=p=0,i=6;i--;N(95));for(j=4;j--;N(124))for(N(124),i=4;i--;)strchr(u,k=*s++-32)||(u[v++]=k);for(i=6;i--;N(45));s=p+v;}

Try it online!
Pretty standard: I count the number of unique characters in the cover (which is built up in a string), then deduct the amount of damage.

Suggest {v=p=0};for(i=6 instead of {};for(v=p=0,i=6

Erlang (escript), 184 bytes
Just a port of Lyxal's Python answer. (Where's the unique function in Erlang?)
u([H|T])->[H]++u([I||I<-T,I/=H]);u(X)->X.
f(X)->length(u(lists:flatten([lists:droplast(tl(I))||I<-lists:droplast(tl(string:split(X,"
",all)))])))-21+length([I||I<-X,([I]--"_|-")==[]]).

Try it online!

This doesn't work when the cover has no spaces: Try it online! should be 16 but gives 15

Ruby, 101 98 bytes
->s{a,*b,c=s.lines;(b.flat_map{x,*y,z,_=_1.chars;a+=x+z;y}|[' ']).size-22+(a+c).tr('{}~','').size}

100 byte version because TIO doesn't support ruby 2.7
->s{a,*b,c=s.lines;(b.flat_map{|i|x,*y,z,_=i.chars;a+=x+z;y}|[' ']).size-22+(a+c).tr('{}~','').size}

Try it online!

source: Stack Exchange

license: Creative Commons - Attribution Share-Alike - https://creativecommons.org/licenses/by-sa/4.0/

sort: votes


text:
Cut resistance of a list
Background
Define a run in a list as a maximal contiguous subsequence of identical values. For example, the list
0 0 0 1 1 0 3 3 3 2 2

has five runs of lengths 3, 2, 1, 3, 2 respectively. (Think of run-length encoding.)
Then define a cut operation as removing one item from each run of a list. Applied to the list above, the result will be 0 0 1 3 3 2.
Finally, the cut resistance of a list is the number of cut operations needed to reduce it to an empty list. The cut resistance of the list above is 3:
0 0 0 1 1 0 3 3 3 2 2
0 0 1 3 3 2
0 3
(empty)

Note that the cut resistance can be higher than the maximal run length, since multiple runs may fuse in the way:
1 0 0 1 1 0 0 1 0 0 1 0 0 1 0 1 0
0 1 0 0 0
0 0
0
(empty)

Related OEIS sequence: A319416 (cut resistance of n written in binary)
Task
Given a (possibly empty) list of nonnegative integers, compute its cut resistance.
Standard code-golf rules apply. The shortest code in bytes wins.
Test cases
0 0 0 1 1 0 3 3 3 2 2 => 3
1 0 0 1 1 0 0 1 0 0 1 0 0 1 0 1 0 => 4
1 2 3 4 5 99 100 101 => 1
4 4 4 4 4 4 3 3 3 3 2 2 1 2 2 3 3 3 3 4 4 4 4 4 4 => 7
(empty) => 0

J, 20 bytes
1-~&#(#~2=/\_&,)^:a:

Try it online!
Using the 0 0 0 1 1 0 3 3 3 2 2 example:

(......_&,) Prepend infinity _:
_ 0 0 0 1 1 0 3 3 3 2 2

(..2=/....) Are consecutive pairs equal? Returns 0-1 list of same length as input:
_ 0 0 0 1 1 0 3 3 3 2 2  <-- Before
 0 1 1 0 1 0 0 1 1 0 1   <-- After

(#~.......) Apply the above mask as a filter to the input.  This drops the first member of every group:
0 0 1 3 3 2

^:a: Repeat until we reach a fixed point, returning all intermediate results (0-padded on the right):
0 0 0 1 1 0 3 3 3 2 2
0 0 1 3 3 2 0 0 0 0 0
0 3 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0

1-~&# Subtract 1 from 1-~ the above result, after applying the "length of" verb to both lists &#.  Since the length of 1 is 1, it remains unchanged, while the result above becomes 4:
4 - 1
3

Python 2, 55 bytes
f=lambda l:l>[]and-~f([x for x in l[1:]if x==l.pop(0)])

Try it online!
False for 0.
57 bytes
f=lambda l:l>[]and-~f([a for a,b in zip(l,l[1:])if a==b])

Try it online!

K (ngn/k), 13 bytes
Same as Jonah's, scan of x at where equal each-prior.
#1_{x@&=':x}\

Using 0 0 0 1 1 0 3 3 3 2 2 as example:
=': means equal each prior
  =': 0 0 0 1 1 0 3 3 3 2 2
      0 1 1 0 1 0 0 1 1 0 1  / zeros in the result correspond to the first item of the run

then indexing into the argument with this mask effectively drops the first item of each run. the indices are:
 &=': 0 0 0 1 1 0 3 3 3 2 2 / where equal each-prior?
1 2 4 7 8 10

 0 0 0 1 1 0 3 3 3 2 2@1 2 4 7 8 10 / indexing
0 0 1 3 3 2

we can turn this into a function and repeat until the result stops changing
 {x@&=':x}\0 0 0 1 1 0 3 3 3 2 2
(0 0 0 1 1 0 3 3 3 2 2
 0 0 1 3 3 2
 0 3
 !0)

drop 1 and take the length
 #1_{x@&=':x}\0 0 0 1 1 0 3 3 3 2 2
3

Try it online!

Nice.  I’d like to see a breakdown to understand the correspondence.

Nice, I thought I was doing well with my 16 bytes. I wouldn't have worked that out on my own - it feels counterintuitive to only take the matches!

Thanks for the explanation.  So the {...} defines the function and the \ means "repeat it until it stops changing'?

Yes, and it saves intermediate results.  f/ in K is equivalent to f^:_ in J and f\   in K is equivalent to f^:a: in J

ngn/k changed the behaviour of f#x to act as {x@&f x}, so this solution is now 11 bytes

05AB1E, 8 bytes
.Γγ€¨˜}g

Try it online or verify all test cases.
Explanation:
.Γ        # Continue until the result no longer changes,
          # collecting all intermediate results in a list
  γ       #  Split the list into chunks of equal adjacent elements
   €¨     #  Remove the last value of each chunk
     ˜    #  Flatten the list of lists
      }g  # After the cumulative fixed-point loop, pop and push the length
          # (which is output implicitly as result)

Pyth, 10 bytes
tl.uqF#C,t

Try it online!
There's a bug in the parser related to this use of qF#, because if I append three variables at the end (e.g. NNQ), it crashes the parser. This is because qF should be treated as having arity 1, but it's being treated as having arity 2. However, it works fine as is,
because the rest of the program is implicit, so the parser completes before anything goes wrong.
Explanation:
tl.uqF#C,t
  .u          Repeatedly apply the following function until the result stops changing:
         t    Remove the first element of the input (tail)
        ,     Pair with the input
       C      Transpose, resulting in all 2 element sublists.
      #       Filter on
    qF        the two elements being equal.
 l            Length
t             Subtract 1 for the empty list.

Note that the intermediate lists look like:
[1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 0]
[[0, 0], [1, 1], [0, 0], [0, 0], [0, 0]]
[[[0, 0], [0, 0]], [[0, 0], [0, 0]]]
[[[[0, 0], [0, 0]], [[0, 0], [0, 0]]]]
[]

Try it online!
This occurs because the program doesn't both to select one element from each pair of numbers. Instead, the pair itself is used as the element of the list in the next iteration. This saves 2 bytes.

JavaScript (Node.js), 38 bytes
f=s=>s+s?f(s.filter(t=>s===(s=t)))+1:0

Try it online!
JavaScript (Node.js), 42 bytes
f=s=>s+s?f(s.filter(t=>s[++i]==t,i=0))+1:0

Try it online!

Haskell, 41 bytes
f[]=0
f(h:t)=1+f[x|(x,y)<-zip(h:t)t,x==y]

Try it online!
f a=1+f[x|(x,y)<-zip a$tail a,x==y] is the same length.

Wolfram Language (Mathematica), 38 bytes
If[#!={},#0[Join@@Rest/@Split@#]+1,0]&

Try it online!

Perl 5 -p, 32 bytes
$\++while s/\b(\d+ )(\1*)/$2/g}{

Try it online!
Requires a trailing space on the input.  (Though I did put some code in the header to add it if it isn't already there so that testing is easier.)

It doesn't print 0 on an empty list though. (Last test case)

Prolog, 115 bytes
a([_],[]).
b(_,[],E,E).
b(A,[B|C],D,E):-A=B,b(A,C,[B|D],E);b(B,C,D,E).
c(A,B):-A=[],B=0;A=[P|L],b(P,L,[],C),!,c(C,D),B is D+1.

Clear version:
% Cut operation.
% Cut operation.
% Previous A, Head B, Tail C, List D, List E.
b(_, [], E, E).
b(A, [B|C], D, E) :-
    A = B,
    b(A, C, [B|D], E)
;   b(B, C, D, E).

% Count cut.
% List A, Number of cut B.
c(A, B) :-
    A = [],
    B = 0
;   A = [P|L],
    b(P, L, [], C),!,
    c(C, D),
    B is D + 1.

APL (dzaima/APL), 18 bytes
≢1↓{⍵⊇⍨⍸2=/¯1,⍵}⍡≡

{⍵⊇⍨⍸2=/¯1,⍵} - dfn that finds the next stage
⍡≡ iterate until reaching a fixpoint
≢2↓ drop the first two elements, then take the length
-2 bytes thanks to Marshall
-1 byte thanks to Bubbler
Try it online!

18 bytes by avoiding {} on the right side of ⍡.

Husk, 7 bytes
←V¬¡Ψf=

Try it online!
Explanation
It's not often that I get to use the higher order modifier function Ψ, but here it's very convenient.
←V¬¡Ψf=   Implicit input: a list.
   ¡      Iterate (producing an infinite list)
     f    filtering by condition:
    Ψ     the next element
      =   is equal to this one.
          The last element is always discarded.
 V        1-based index of first result that is
  ¬       falsy (for lists, this means empty).
←         Decrement.

C (gcc), 108 104 101 bytes

Saved four seven bytes thanks to ceilingcat.

*o,*O,r;e(int*s){for(r=1;~*s;r++)for(o=s;~*o;*O=-1){for(;*o==*++o;);for(O=--o;~*O;)*O++=O[1];}s=~-r;}

Try it online!

@ceilingcat Thank you.

All your tests return 0 - is that a bug in the answer or your test code?

@Noodle9 It was a bug in testing; basically, the input gets modified and I called it twice. Thanks for noting.

No probs, that why it's best to store the returned value of a test and print/verify that stored result.

@ceilingcat Thank you.

R, 56 55 bytes
f=function(x)`if`(sum(x|1),1+f(x[-cumsum(rle(x)$l)]),0)

Try it online!
Edit: recursive function is 1 byte shorter, and returns 0 for empty input
(original, non-recursive version returned FALSE for empty input)

T-SQL, 125 bytes
I am using table as input
DECLARE @ INT=1u:SET
@-=1DELETE x FROM(SELECT*,lag(a,1,-1)over(order by i)b
FROM t)x
WHERE a<>b
IF @@rowcount>0GOTO u
PRINT-@

The posted code is using a permanent table as input. Try it online is using a table variable spending 1 additional byte

Brain-Flak, 68 bytes
([]){{}({}()<>)<>([])}<>({()<{(({}<>)<>[({})]){{}<>{}(<>)}{}}<>>}{})

Try it online!
([]){{}({}()<>)<>([])}<>  # add 1 to everything so I don't have to handle 0

({()<                     # until stack is "empty", counting iterations:
  {                       # for each number
    (
      ({}<>)              # copy to other stack
    <>[({})])             # and compare to next number (or zero if at end)
    {{}<>{}(<>)}{}        # if not equal, remove newly added number
  }<>                     # switch stacks for next iteration
>}{})                     # output number of iterations

Factor + grouping.extras, 78 bytes
[ 0 swap [ [ ] group-by values [ rest ] map-concat [ 1 + ] dip ] until-empty ]

Try it online!
Explanation:
It's a quotation (anonymous function) that takes a sequence from the data stack as input and leaves an integer on the data stack as output.

0 swap Tuck away our cut resistance value for safe keeping.
[ ... ] until-empty Keep doing something to a sequence until it's empty. This is shorter than having to call dup, empty?, drop, and the like.
[ ] group-by values Group the runs of a sequence. This is shorter than [ = ] monotonic-split.
[ rest ] map-concat Cut the runs and glue 'em back together.
[ 1 + ] dip Increment the cut resistance value.

Vyxal, 7 bytes
≬ĠvṪfİL

Try it Online!
     İL # Count number of times until the result repeats
≬----   # By applying the following
 Ġ      # Group consecutive elements
  vṪ    # Remove the last item from each group
    f   # Flatten

Python 2, 63 bytes
Takes in a string \$ s \$, a space-separated string of the numbers. It returns False for the empty case.
f=lambda s:s>''and-~f(re.sub(r'(\d+ )(\1*)',r'\2',s))
import re

Try it online!

Python 2, 65 bytes
Same as above, but returns 0 for the empty case, in case returning false is disallowed.
f=lambda s:len(s)and-~f(re.sub(r'(\d+ )(\1*)',r'\2',s))
import re

Try it online!

Retina 0.8.2, 27 25 bytes
-2 bytes thanks to @Neil
1`\b
=
}`(\d+ )(\1*)
$2
=

Try it online!
Similar to @Xcali's Perl solution, and similarly requires a trailing space in the input.
1`\b
=

Replace the first word-boundary with a = character. The first time this is executed, it will match the start of the input. On subsequent runs, it will match the empty space between a = and the first number in the list
(\d+ )(\1*)
$2

Replace sequences of repeated integers with everything but the first integer.
}`

Execute the preceding two stages in a loop until the input stops changing
=

Count the number of ='s

25 bytes.

@Neil Thanks :) I didn't even know that shorthand existed

Factor, 109 bytes
: f ( s -- n ) 0 swap [ dup empty? ]
[ [ = ] monotonic-split [ 1 tail ] map concat
[ 1 + ] dip ] until drop ;

Try it online!

Jelly,  9  8 bytes
-1 by golfing a 9-byter suggested by clapp
EƝTịµƬL’

Try it online!

alternative 9-byte port of the K answer: =2\TịµƬL’

@clapp - thanks, that makes a save of 1 byte possible

I knew there had to be something shorter than 2\, nice

C (gcc), 118 \$\cdots\$ 94 92 bytes
Saved 2 5 7 bytes thanks to ceilingcat!!!
*t;k;c;f(l,n)int*l;{for(k=0;n;++k)for(t=l,c=-1;t-l<n;)c-*t?c=*t,wmemcpy(t,t+1,n--):++t;c=k;}

Try it online!
Commented code
*t;k;c;f(l,n)int*l;{
  for(k=0;                  /* initialise cut operation counter */
    n;                      /* loop until there're no numbers left
                                     in array */
    ++k)                    /* increment cut operation counter after
                                     every loop */
     for(t=l,               /* save the pointer to the start of
                                     the array */
       c=-1;                /* initialise c to something that won't
                                     match first number */
       t-l<n;)              /* loop until our temp pointer is past
                                     the end of the array */
        c-*t?               /* should we do a cut operation? */
         c=*t,              /* save our cuurent number so we do a cut
                                     operation at next different number */
         wmemcpy(t,t+1,n--) /* shift array down one element
                                     and reduce number of elements by 1 */
         :++t;              /* if we're not doing a cut operation
                                     bump pointer */
  c=k;                      /* return k */
}

Clojure, 72 bytes
#(count(take-while seq(iterate(fn[c](mapcat rest(partition-by + c)))%)))

Damn these function names are long :D

Charcoal, 22 bytes
Ｗθ«≔Φθ∧λ⁻κ§θ⊖λθ⊞υω»ＩＬυ

Try it online! Link is to verbose version of code. Explanation:
Ｗθ«

Repeat until the input list is empty.
≔Φθ∧λ⁻κ§θ⊖λθ

Filter out the first term and any terms equal to their predecessor, i.e. the first of every run.
⊞υω

Keep track of the number of iterations.
»ＩＬυ

Output the number of iterations.

Japt, 9 bytes
Ê©ÒßUòÎcÅ

Try it
Ê©ÒßUòÎcÅ     :Implicit input of array U
Ê             :Length
 ©            :Logical AND with
  Ò           :Negate the bitwise NOT of
   ß          :Recursive call with argument
    Uò        :  Partition U between elements where
      Î       :    The sign of their difference is truthy (not 0)
       c      :  Flatten after
        Å     :    Slicing off the first element of each partition

Husk, 8 7 bytes
←LU¡mhg

Try it online!
A different method, which is slightly longer now the same length.
Explanation
←LU¡mhg
      g group runs of consecutive equal elements
   ¡    apply function infinitely, collecting it's results
    mh  drop the last element from each run
  U     cut at fixed point
←L      Get length, decrement.

Ruby -nl, 43 bytes
Similar to the other regex-based answers.
$.+=1while gsub(/(\d+ )(\1*)/,'\2')!=""
p$.

Attempt This Online!

all_licenses: Creative Commons - Attribution Share-Alike - https://creativecommons.org/licenses/by-sa/4.0/


text:
Pass The ⚽ Around The Circle
The ball game is a game in which a number of players sit together in a circle. Each player is first assigned a number \$ n \$, either 1, 2, or 3. The game begins with any starting player, and proceeds clockwise around the circle. The current player with the ball throws it to the next player. Who the next player is solely depends on the number \$ n \$ the current player was assigned.
If \$ n = 1 \$, the next player will be the one sat directly adjacent (one space away), traveling in the current direction.
If \$ n = 2 \$, the next player will be the one sat two spaces away, traveling in the current direction.
If \$ n = 3 \$, the direction of play is first switched (clockwise to counter-clockwise, and vice-versa). The next player will then be the one sat directly adjacent, traveling in the new direction.
Task
You are given a list of numbers \$ l \$ all in the range \$ [1 - 3] \$, denoting the numbers each player was assigned. The elements in \$ l \$ are given in clockwise order, and such that the last element of \$ l \$ is adjacent to the first element. Your task is to determine the number of players who have touched the ball, before it reaches a player who previously already touched the ball.
Example
The starting player is at the first index. X represents a visited index, O represents an index visited twice.
[1, 2, 1, 1, 2, 2, 3] ->
[X, 2, 1, 1, 2, 2, 3] ->
[X, X, 1, 1, 2, 2, 3] ->
[X, X, 1, X, 2, 2, 3] ->
[X, X, 1, X, X, 2, 3] ->
[X, X, 1, X, X, 2, X] ->
[X, X, 1, X, X, X, X] ->
[X, X, 1, O, X, X, X]
The answer is 6.

Clarifications

\$ l \$ can be inputted in any reasonable format, but the numbers 1, 2, and 3 must not change
The starting player does not have to be at the first index, but please specify where it would start
This is code-golf, so the shortest code in bytes wins!

Test Cases
Input (start is index 0) -> Output
[1] -> 1
[2] -> 1
[3] -> 1
[3, 2] -> 2
[2, 1] -> 1
[2, 2, 3] -> 3
[1, 1, 1, 1, 1] -> 5
[2, 2, 2, 2, 2] -> 5
[3, 3, 3, 3, 3] -> 2
[1, 3, 2, 1, 2, 3] -> 2
[1, 2, 1, 1, 2, 2, 3] -> 6

You'll want to fix before the it reaches at the end of the Task paragraph.

You have instructions, state, and an interpreter... is this game turing complete?

Python 3, 67 55 bytes
f=lambda x,*l:x and-~f(*l[x%3-1::1-x//3*2],0,*l[x%2:1])

Try it online!
Use @Leo's idea of keeping the current player at a fixed index and transforming the list instead. Be sure to check out and upvote his answer!
If the current list is [x, a, b, c, d], where x is the move of the current player, then we want to rotate the list appropriately, and replace x with 0:

If x == 1, the new list is [a, b, c, d, 0]
If x == 2, the new list is [b, c, d, 0, a]
If x == 3, the new list is [d, c, b, a, 0]
If x == 0, this player has already been visited, thus we stop the algorithm.

Python 2, 90 85 bytes
f=lambda l,d=1,p=0,*t:(p in t)^1and-~f(l,[d,-d][l[p]>2],(-~l[p]%4*d-d+p)%len(l),p,*t)

Try it online!
Not every elegant, but does the job.
This recursive function keeps track of the current direction d, the position p, and the list of seen position t.

Husk, 16 bytes
←LU¡Γ!Moëṙ1ṙ2↔IΘ

Try it online!
Takes a list of numbers where the ball is currently at the first element and keeps rotating/reflecting the list to keep the ball in the first position while setting any visited element to 0. Returns how many different lists were created this way (-1).
For a more detailed explanation of the code (hard to do, but I'll try):
←LU¡Γ!Moëṙ1ṙ2↔IΘ
        ëṙ1ṙ2↔I  List of four functions [rotate by 1, rotate by 2, reflect, do nothing]
      Mo       Θ Make each of these functions prepend a 0 to the list before doing anything

    Γ!           Use the first element of the input list as an index into the list
                 of functions, and apply that function to the rest of the input list.
                 Note that indexing with a 0 returns the last function (do nothing)

   ¡             Iterate this process and keep all the results produced
  U              Discard all results after the first repeated one
←L               Return the number of results minus one

I'm a bit sad for all the bytes I'm taking to do ←LU, but I couldn't find a shorter alternative (damn 1-based indexing!)

C (gcc), 94 92 91 bytes
Saved a byte thanks to ceilingcat!!!
c;s;i;d;p;f(l,n)int*l,n;{p=1;for(s=i=0;c=l[i];l[i]=!++s,i=(i+p*d+n)%n)d=c>2?p*=-1,1:c;p=s;}

Try it online!
Inputs an array of int along with its length and returns the number of players touched before a repetitive touch.
How?
Passes the ball around setting the last place the ball was to \$0\$ and incrementing the counter \$s\$. When we arrive back at a \$0\$ we're done and return \$s\$.
Commented code
c;s;i;d;p;f(l,n)int*l,n;{
    p=1;                /* polarity is initialised to 1 forwards.
                           -1 is backwards */
    for(s=i=0;          /* initialise counter and index */
        c=l[i];         /* stop looping when we hit a zero
                           and cache the current value in c */
        ++s,            /* after loop bump our count */
        l[i]=0,         /* and zero last player */
        i=(i+p*d+n)%n)  /* and move index d places
                           in polarity p direction
                           adding n so we never go negative
                           when we make it all mod n */
       d=c>2?p*=-1,1:c; /* if number is 3 reverse polarity p
                           and set d to 1 otherwise set d to
                           the number */
    p=s;                /* return s */
}

@ceilingcat Recycling positive ints as zeros, nice one - thanks! :D

JavaScript (ES6), 67 bytes
a=>(d=1,g=x=>(n=a[x%=w=a.length])&&!(a[x]=0)+g(x+=n-3?n*d:d=w-d))``

Try it online!
Commented
a => (                    // a[] = input array
  d = 1,                  // d = direction
  g = x =>                // g is a recursive function taking the current position x
    ( n =                 //   n is the player number at the current position
      a[                  //
        x %=              //   take the wrap-around into account by computing
          w = a.length    //   x modulo w, where w is the length of a[]
      ]                   //
    ) &&                  //   stop the recursion if a[x] is equal to 0
    !(a[x] = 0) +         //   otherwise, set it to 0 and increment the final result
    g(                    //   do a recursive call:
      x +=                //     update the position:
        n - 3 ? n * d     //       add n * d to x if n is not equal to 3
              : d = w - d //       otherwise, reverse the direction and add it to x
    )                     //   end of recursive call
)``                       // initial call to g with a zero'ish value for x

R, 97 87 85 bytes
function(p,i=1)sum(while(m<-p[i]){p[i]=0
i=(i+`if`(m>2,T<--T,m*T)-1)%%sum(p|1)+1},!p)

Try it online!
Edit: -10 bytes by tracking direction of travel (instead of flipping list of players)
Edit 2: re-arranged to cull 2 more characters
touches=function(p,         # p=player numbers
    i=1)                    # i=current player
    sum(
        while(m<-p[i]){     # while m=number of current player is non-zero
            p[i]=0          # set his number to zero
            i=i+            # calculate new player:
            `if`(m>2,       # if m is 3...
                T<--T,      # flip the direction of travel T and move by this
                m*T)        # otherwise move ahead by T*m
            -1)%%sum(p|1)+1 # -1, modulo number of players, +1
        }                   # (R indices are 1-based)
        !p)                 # sum of null (the return value of 'while')
                            # and the final number of zeros

05AB1E, 22 21 bytes
[ć©_#0š„RÀS¤ºª®è.V¼}¾

Try it online or verify all test cases.
Explanation:
[                # Loop indefintely:
 ć               #  Extract head; pop and push remainder-list and first item separately
  ©              #  Store this first item in variable `®` (without popping)
   _             #  Check if its 0
    #            #   And if it is, stop the infinite loop
 0š              #  Prepend a 0 at the start of the list
   „RÀ           #  Push string "RÀ"
      S          #  Convert it to a list of characters: ["R","À"]
                 #  (NOTE: The `ª` already implicitly converts it to a list of
                 #   characters, so this usually isn't necessary; but without it the
                 #  `„RÀ¤` would act like a dictionary string and become "R cry")
       ¤         #  Push its last item (without popping): "À"
        º        #  Double it by mirroring: "ÀÀ"
         ª       #  Append it to the list: ["R","À","ÀÀ"]
          ®è     #  Index `®` into this list (0-based and with wraparound,
                 #  so the 3 will index into the first item "R")
            .V   #  Execute it as 05AB1E code
                 #   "R": Reverse the list
                 #   "À": Rotate the list once towards the left
                 #   "ÀÀ": Rotate the list twice towards the left
              ¼  #  Increase the counter_variable by 1
}¾               # After the infinite loop: push the counter_variable
                 # (after which it is output implicitly as result)

J, 39 bytes
1-~&#[:({.(|.*1*@+2*[)0,}.)^:a:]+_4*=&3

Try it online!
I solved this independently before looking at other answers, but have stumbled on the same high-level approach used by Leo and Surculose Sputum.
A few details appear to be different:

]+_4*=&3 I start by transforming all 3s in the list into -1s, so that I don't have to special case any code.  J automatically treats a negative rotation as the reverse direction.
Instead of reversing the list when I hit a -1, I multiply all numbers on the list by -1.  I avoid special casing here too by always multiplying all numbers on the list by the following quantity: "double current value, add 1, take sign num" 1*@+2*[.  The quantity will be 1 for all values greater than 0, and -1 when the value is -1.

Perl 5 -pa, 66 bytes
while($_=$F[$i]){$F[$i]=0;$.*=-1if/3/;$i+=$.+$.*/2/;$i%=@F;$\++}}{

Try it online!

JavaScript (Node.js), 81 bytes
s=>s.map(_=>(t=s[r=p%q])?s[p+=t>2?w*=-1:t*w,r]=!++n:0,q=s.length,n=0,p=3*q,w=1)|n

Try it online!

Charcoal, 30 bytes
Ｗ⊟θ≔⁺⎇⁼ι²⟦⊟θ⁰⟧⟦⁰⟧⎇⁼ι³⮌θθθＩ⊕№θ⁰

Try it online! Link is to verbose version of code. Based on @Leo's idea explained better by @SurculoseSputum. Takes input in reverse order. Explanation:
Ｗ⊟θ

Let the current list be [e, d, c, b, a]. Remove the last element a, which represents the current player. Then if it is non-zero, meaning that they have not played yet:
≔⁺⎇⁼ι²⟦⊟θ⁰⟧⟦⁰⟧⎇⁼ι³⮌θθθ

Create two lists depending on the value of a:

[0] and the rest of the list [e, d, c, b]
[b, 0] (obtained by popping b from the list) and the rest of the list [e, d, c]
[0] and the reverse of the rest of the list [b, c, d, e]

Concatenate them together to create the new state of the list.
Ｉ⊕№θ⁰

Print 1 more than the number of 0s in the list (because we popped a so that's no longer in the list).

perl -M5.010 -a, 84 bytes
$:=1;{($;=$F[$,])?do{$:=-$:,$;=1 if$;==3;$"++;$F[$,]=0;$,=($,+$;*$:)%@F;redo}:say$"}

Try it online!
This just follows the rule of the game, labelling any visited player as 0, and terminating when it encounters a player labelled 0. Steps are counted and printed at the end.
The code in the header section of TIO is just there to make it work on multiple inputs; leave it off for a single line of input.

Ruby, 63 bytes
f=->a,d=1{x,*a=a;x ?1+f[[p,*a].rotate((2-x%2)*d*=1-x/3*2),d]:0}

Try it online!


text:
Brainfuck movement
A normal Brainfuck program starts out with an array of cells, all containing the number 0. This program, on the other hand, must be able to run starting with any number x in the leftmost cell. The only rule is that at the end of program execution, the "pointed-to" cell must be cell x. (The leftmost cell is cell 0).

Each cell contains a number from 0 to 255, and every cell but the leftmost one starts out with a value of 0.
There are an infinite number of cells, but none to the left of the leftmost cell.

This is code golf, so the submission using the fewest bytes wins. Good luck!

I'm not sure I understand what the challenge actually is... perhaps some time on the Sandbox would do it some good?

Okay, it took me a while to read correctly, but I guess this seems fine. Maybe tag it [tag:atomic-code-golf] and include a little description of Brainfuck just for the sake of self-containedness. I'm leaving my close vote in place just in case someone else finds a problem that needs to be fixed, though.

...well, being self-contained, and defining the behavior of trying to go left from the leftmost cell. Brainfuck has enough variants none of which are standard that we can't really make assumptions here. VTC for being unclear.

In addition to those restictions what happens when + is executed on a 255, or - on a zero?  Do they wrap around or just do nothing?

10 bytes
[[->+<]>-]

Try it online!
Move x one cell to the right, and decrease it by 1. Repeat until x is 0.

20 bytes
[->>[>]+[<]<]>>[>]<<

Try it online.
This could be made shorter if there were a single cell to the left of cell 0.


text:
Get a middle-ish string
You are the first engineer in a hot new startup, Lexographical. Our company is making waves by digitizing and indexing all words, real or not. The core of our product... Our secret sauce... Well, that's why we hired you :)
Because we're bleeding edge, we're enforcing the latest standards. Even the latest ones aren't good enough. We need the ones that haven't been made yet. No language in existence is good enough for us, so you need to use Lexo to write your code (funnily enough, it looks identical to your favorite programming language).
Your mission...
Given 2 strings of arbitrary length and composition, return a string that is lexicographically between the 2 strings (i.e. input_a < output < input_b). Please use standard lexicographical comparisons.
We want ALL THE STRINGS, so the output doesn't have to resemble either of the inputs (though it might, and that's fine).
Because of legal hot water we're in, we can't actually process inputs longer than 10 characters. The output can be as long as we need it to be.
Inputs will always be printable ASCII characters and may not be in lexicographical order. They are gUaranteed to not be identical strings. Sample inputs and outputs (using Python string comparison):
Lexo, Rules => M
qwerty, qwertyuiop => qwertyu
"123", "124" => "1230"
Oh yeah, we're running this on very very small computers... we're on a tight budget. So the smaller you can make the program, the better!

Are we guaranteed that the two input strings will be in lexicographical order (ie. is the first input always lexicographically smaller than the second)?

Hi and welcome to Code Golf! This appears to be a fairly solid post, but is missing a couple requirements. You definitely need to specify what will appear as input (I'd recommend printable ASCII), and what  the comparison method used should be (I'd recommend usual lexicographical comparisons). Please note we do have a sandbox where you can post challenges for review before posting to main. Good luck!

@mathjunkie nope, only that they’re not identical.

I feel like this challenge can be made trivial by repeatedly appending the input with the lexicographically smallest character (which I assume is a space ' ').

Yes, it does like this challenge can just be "append a space to the smaller string"

Since you're looking for a string strictly in between, I guess the two inputs need to be not consecutive in lexicographic order?

If the two strings are "abc" and "abc " (where there's a space, or whatever the lowest character lexicographically is, at the end of the second string), what should the output be?  There is no string in between them lexicographically.  (Edit: I see this is the same as the last question @xnor asked.)

JavaScript, 8 bytes
Appends a space to the first string.
a=>a+' '

Keg, -pn, 5 bytes
᠀᠀⑭^,

Probably the only valid answer. Same as the 0 byter but it sorts the stack lexically.
Keg and 05AB1E, 0 bytes

This is a port of the Javascript answer, but instead of appending a space to the string, it appends a newline. A 0-byte Keg program is simply a cat program on the first input, meaning that it takes the first word and prints it, followed by a newline.
Try it online!

Polyglot with 05AB1E, which does the same.

Actually, based on the response on the very first comment, I think both answers are invalid, for assuming the first input is always the smaller one in lexicographical order.

@KevinCruijssen it should be a valid answer now.


text:
Bubbler numbers
Background
My user ID is 78410, or \$1 0 0 1 1 0 0 1 0 0 1 0 0 1 0 1 0_2\$. One interesting property of this number is that, in binary,

it doesn't have three consecutive identical digits, and yet
it has a substring \$100100100\$ which is three copies of \$100\$.

So, I define a Bubbler number as a positive integer whose binary representation satisfies the following:

it doesn't have three consecutive identical digits (so it is a member of A063037), and
it contains a substring which is three consecutive copies of some nonempty string (so it is NOT a member of A286262).

Task
Given a positive integer as input, determine if it is a Bubbler number.
You can use truthy/falsy values in your language or two distinct values to indicate true/false respectively.
There are 55 Bubbler numbers under 1000:
42 84 85 106 149 169 170 171 212 213 292 298 299 338 339 340 341 342 362 365
405 425 426 427 438 585 596 597 598 618 658 661 676 677 678 681 682 683 684 685
724 725 730 731 804 810 811 850 851 852 853 854 874 876 877

Standard code-golf rules apply. The shortest code in bytes wins.

Ugh. User ids all over again.

A tribute to yourself!? That's hilarous.

Brachylog, 10 bytes
ḃsᶠ~j₃ˢlⁿ1

Try it online!
This was meant to only be a partial solution, but it turns out that ⁿ fails on empty inputs.
  ᶠ           Find every
 s            substring of
ḃ             the input's binary digits,
      ˢ       then for each substring
   ~j₃        map it to the string which it is three copies of
      ˢ       (ignoring and discarding it if there is no such string).
        ⁿ     For none of those strings (of which there is at least one)
       l      is its length
         1    1.

05AB1E, 15 12 bytes
bŒʒ3ä1ìË}{нË

Outputs 0 for truthy and 1 for falsey.
Try it online or verify some more test cases.
Explanation:
b         # Convert the (implicit) input to a binary-string
 Œ        # Take all substrings of the binary-string
  ʒ       # Filter it by:
   3ä     #  Split the substring into 3 equal-sized parts
     1ì   #  Prepend a 1 to each part
       Ë  #  Check that the three parts are equal
          #  (the prepend 1 is necessary, because ["01","01","1"] would be truthy,
          #   since strings and integers are interchangeable in 05AB1E)
  }{      # After the filter: sort all remaining substrings
          # (this causes any "000" and/or "111" to be leading)
    н     # Pop and push the first substring (or "" if none are left)
     Ë    # Check if all characters in this string are equal (also truthy for "")
          # (after which the result is output implicitly)

Python 2, 64 63 61 58 bytes
f=lambda n,i=2:[n>i>0<f(n,i+1),i>3][3*bin(i)[3:]in bin(n)]

Try it online!
A recursive function that returns True if the number is a Bubbler number, and False otherwise.
Generates all possible binary string, then for each binary string checks if n contains 3 consecutive copies of that string.
The binary strings are generated by evaluating bin(i)[3:] for \$i\$ from \$2\$ to \$n-1\$. The slice [3:] gets rid of the first 3 characters in the binary representation of \$i\$, which are always 0b1. This allows us to generate binary strings that has leading 0.

JavaScript (ES6),  54  49 bytes
Saved 5 bytes thanks to @l4m2!
n=>/^(?!.*(.)\1\1).*(.+)\2\2/.test(n.toString(2))

Try it online!
Regular expression
/^(?!.*(.)\1\1).*(.+)\2\2/
 ^                         // match the beginning of the string
  (?!         )            // must NOT be followed by:
       (.)                 //   a single character
     .*                    //   appearing anywhere
          \1\1             //   immediately followed by 2 copies of itself
                           // must also match:
                 (.+)      //   a string
               .*          //   appearing anywhere
                     \2\2  //   immediately followed by 2 copies of itself

JavaScript (ES6), 55 bytes
This version uses a helper function to test /(.+.)\1{2}/ and /(.)\1{2}/ separately.
n=>(g=p=>n.toString(2).match(p+".)\\1{2}"))`(.+`&&!g`(`

Try it online!

49

Jelly, 10 bytes
BẆẋ3ẇɗƇ$ḢḊ

A monadic Link accepting a positive integer which yields a list - in Jelly an empty list (non-Bubbler) is falsey while a non-empty list is truthy (Bubbler).
Try it online! Or see the test-suite (identifying all Bubbler numbers in \$[1..1000]\$).
How?
BẆẋ3ẇɗƇ$ḢḊ - Link: positive integer, n
B          - convert n to binary (say b)
       $   - last two links as a monad - f(b):
 Ẇ         -   all sublists (say s) - Note these are sorted from shortest to longest
      Ƈ    -   filter keep those x of s which are truthy under:
     ɗ     -     last three links as a dyad - f(x, b):
   3       -       three
  ẋ        -       repeat (e.g. [1,0] -> [1,0,1,0,1,0])
    ẇ      -       is a sublist of (b)?
        Ḣ  - head (given an empty list this yields 0)
         Ḋ - dequeue

K (ngn/k), 46 bytes
{(*/0<3!+/+3':x)>*/^a?,/'3#','a:,/,\'|',\x}@2\

Try it online!
2\ binary encode
{ }@ apply function
first condition:

,/,\'|',\x all substrings of the argument, i.e. prefixes (,\), reverse each (|'), prefixes each (,\'), raze (,/)

a: assign to a

,/'3#',' triplicate each, i.e. enlist each (,'), 3-reshape each (3#'), raze each (,/')

a? find - indices in a, or nulls (0N) for not found

^ is null?

*/ all

second condition:

3': sliding window of size 3

+ transpose

+/ sum

3! mod 3

0< positive?

*/ all

> and not (between the two conditions)

APL (Dyalog 18.0), 34 33 bytes
⊃1<∘⍸⊢{∨/⍺⍷⍨∊3/⊂1↓⍵}Ö(2∘⊥⍣¯1)¨2↓⍳

Try it online!
This uses ⎕IO←0 and the Over operator (⍥, which was added in 18.0). The current version on TIO is 17.1, so it's been implemented manually (thanks Bubbler!). I think this does well for a non-regex answer.
Explanation
                              2↓⍳     ⍝ The range 2 to n-1
     ⊢               (2∘⊥⍣¯1)         ⍝ Convert this range and the input to base 2
      {∨/⍺⍷⍨∊3/⊂1↓⍵}Ö        ¨        ⍝ Before applying the function to each
                1↓⍵                   ⍝ Drop the first 1 of the binary number
           ∊3/⊂                      ⍝ Repeat the list 3 times and flatten
       ∨/⍺⍷⍨                          ⍝ Is this sublist in the binary input?
⊃   ⍸                                 ⍝ Is the index of the first element
 1<∘                                  ⍝ Greater than one?

Something like this would be more helpful to test the code and check the score.

@Bubbler Thanks! I always forget how to declare the function outside the code section

I'd love to see an explanation of this answer.

@Razetime Added explanation

Bash + Unix utilities, 44 bytes
dc -e2o?n|egrep -v 000\|111|egrep '(.+)\1\1'

Try it online!
Input is on stdin, and the output is the exit code (0 for truthy, 1 for falsey, as usual with shell scripts).
Or verify the Bubbler numbers under 1000.

perl -M5.010 -n, 45 bytes
$_=sprintf"%b",$_;say!/(.)\1\1/&&!!/(.+)\1\1/

Try it online!
Turns the input into a string with the binary representation of the number, applies regexes to test the requirements, then prints 1 or an empty string accordingly. Two bytes (the !!) could be saved if there wasn't a restriction for two distinct values -- without them, for bubbly numbers, it prints the thrice repeated string.

What about "truthy/falsy values in your language"? I know Perl can be weird, but if you're using !! it ought to already be truthy or falsy, right? ...That is an honest question.

@UnrelatedString Sure, and anything which isn't 0, the empty string, the string "0" or undefined is true. Without the !!, you'll get true values, but you'll get different values for different inputs (and returning two distinct values is a requirement -- I take that 1 specific value for false, and 1 specific value for true). In Perl && returns the value of the left operand if it's false, and value of the right operand if the left operand is true. And the value of /(.+)\1\1/ will be what is matched by the (.+) (because it is in list context, due to the say).

If you accept 0 and 1 as output you could swap the matches and use & instead for -3. Try it online!

Husk, 9 bytes
tṠḟ·€*3Qḋ

Returns a list, which is nonempty iff the input is a Bubbler number. In Husk, empty lists are falsy and nonempty lists are truthy.
Try it online!
Explanation
tṠḟ·€*3Qḋ   Implicit input: a number, say n=84.
        ḋ   Binary representation: [1,0,1,0,1,0,0]
       Q    Sublists: [[1],[0],[1,0], …, [1,0,1,0,1,0,0]]
  ḟ         Find the first one that satisfies this (or an empty list if none do):
              Example list: [1,0]
     *3       Repeat three times: [1,0,1,0,1,0]
 Ṡ ·€         It occurs in the list of sublists: yes.
            Result: [1,0]
t           Remove the first element: [0], truthy.

The correctness of this program relies on the fact that Q enumerates the sublists in a "good" order (all sub-sublists of a sublist occur in the result before the sublist itself) and ḟ returns the first match it finds.
If 000 occurs in the binary representation, then [0] is listed before any longer triply repeated sublist (unless that sublist consists of only 1s, in which case [1] is listed before it).

Jelly, 14 13 12 11 10 bytes
BẆẋ3eɗƇ`ḢṖ

Try it online!
The third -1 takes some inspiration from Kevin Cruijssen's 05AB1E solution.
Fourth -1 thanks to Jonathan Allan reminding me of Jelly's truthiness semantics.
Outputs truthy or falsy.
 Ẇ            Every substring of
B             the input's binary digits.
      Ƈ       Filter them by
  ẋ3 ɗ        repeated three times
    e         membership in
BẆ     `      every substring of the input's binary digits.
         Ṗ    Is there more than one element to remove from
        Ḣ     the first (i.e., shortest) of the filtered substrings?

Oh, just saw how similar mine is to yours - I hadn't even noticed an existing Jelly answer.

Using the empty-list being falsey and the fact that taking the head of an empty list yields 0  you can replace LỊ with Ḋ (as I did in mine) or Ṗ which gets your one down to 10 bytes too.

Retina 0.8.2, 44 bytes
.+
$*
+`(1+)\1
$+0
01
1
A`000|111
1`(.+)\1\1

Try it online! Link includes test cases. Explanation:
.+
$*

Convert to unary
+`(1+)\1
$+0
01
1

Convert to binary.
A`000|111

Delete the string if it contains 000 or 111 (A`(.)\1\1 also works for the same byte count).
1`(.+)\1\1

Check whether there are three consecutive substrings.

T-SQL, 258 bytes
Added some line changes to make it readable
DECLARE @ char(99)=''
WHILE @i>0
SELECT @=left(@i%2,1)+@,@i/=2;
WITH C as(SELECT number+1n FROM spt_values WHERE'P'=type)
SELECT count(*)FROM C,C D
WHERE not(@ like'%000%'or @ like'%111%'or len(@)<D.n*3+C.n+2)
and @ like'%'+replicate(substring(@,C.n,D.n+1),3)+'%'

Returns 1 or more for true, 0 for false
Try it online

Charcoal, 28 bytes
≔⍘Ｎ²θ¿⬤01¬№θ×ι³⊙θΦκ№θ×³✂θλ⊕κ

Try it online! Link is to verbose version of code. Outputs a Charcoal boolean, i.e. - for true, nothing for false. Explanation:
≔⍘Ｎ²θ

Input the number and convert it to base 2 as a string.
¿⬤01¬№θ×ι³

Test whether neither digit 0 nor 1 appears triplicated in the string.
⊙θΦκ№θ×³✂θλ⊕κ

Check whether any nontrivial substring appears triplicated. (I use Φ instead of a second ⊙ since Charcoal doesn't currently accept an implicit range there, but the effect is the same.)

C (gcc), 124 120 114 113 bytes
b,c,i,j;f(a){for(b=c=1;a;a/=2)for(b&=a&7^7&&a&7,i=~3,j=1;++j<11;i*=2)c&=~i&(a>>j^a|a>>j*2^a)||!(a>>j*3-1);b&=!c;}

Try it online!
-4 -5 bytes: ceilingcat

Raku, 37 bytes
{.base(2)~~/(.+)$0$0/&none /(.)$0$0/}

Try it online!
This matches the base-2 representation of the input number against the junction
/(.+)$0$0/ & none /(.)$0$0/

...which succeeds if it matches the first pattern, but not the second one.

Can't you just replace none with !?

@JoKing That seems to work, but I have no idea why.  I would expect /foo/ & ! /bar/ to be a junction of a regex and a boolean.

Well, the whole thing is boolified by the ~~, and when a regex is boolified, it evaluates with $_.

Vyxal, 11 bytes
bṅÞS'3/≈;h≈

Try it Online! or get all the Bubbler numbers up to a given number.
Port of Kevin Cruijssen's 05AB1E answer. Outputs \$0\$ for truthy and \$1\$ for falsy.
Explanation
bṅÞS'3/≈;h≈   # Implicit input
bṅ            # Input as a binary string
  ÞS          # All substrings
    '   ;     # Filtered by:
     3/       #  Split into three parts
       ≈      #  All equal?
         h    # First item
          ≈   # All equal?
              # Implicit output

Ruby, 42 bytes
->n{n.to_s(2)=~/^(?!.*(.)\1\1).*(.+)\2\2/}

Try it online!
How it works:
->n{
  n.to_s(2)      # convert to binary representation string
  =~ /           # check if it matches regex
  ^(?!.*(.)\1\1) # (from the start) assert that there are no 3
                 # repeated characters anywhere in the string
  .*             # skip any number of characters
  (.+)\2\2/      # check that there is a sequence of 1 or more
                 # characters repeated 3 times (note that there
                 # are no 3 repetitions of a single character so
                 # the 1 case is irrelevant (equivalent to ..+))
}

squints eyes"regex..."

PowerShell Core, 74 bytes
($s=[Convert]::ToString("$args",2))-notmatch'(.)\1\1'*($s-match'(.+)\1\1')

Try it online!


text:
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.
Task
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.)
Scoring
This is code-golf!
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 Nice improvement! I suggest you post it as an answer which would qualify for  my outgolfing bounty.

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!

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.

@SteveBennett I didn't notice it was explicitly allowed. Thanks!

Python 2, 52 bytes
lambda s:sum(3-"MCNSAO_WUZ_V".find(c)*4%7for c in s)

Try it online!
Improvement on @xnor's answer.
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

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

C(GCC) 84 73 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!

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
                                 ⍝ Index is 1-based, not found gives 4
     4|  ⍝ Modulo 4; convert 4 to 0
  1⊥¨    ⍝ Sum of each row
-/       ⍝ Alternating sum; (ANM score)-(CSO score)+(VZW score)-(U score)

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 thanks a lot, this really looks like it shouldn't work ;)

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
--
T`VAUC`##-
+`\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
--
T`VAUC`##-

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!

@mathjunkie I had tried transliteration stages but not in that exact combination, thanks!

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

@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: https://codegolf.stackexchange.com/a/35872/95505 Do you familiar with this?

By the way... I saw people using Header and Footer in TIO, what is it exactly?

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

@mathjunkie I saw people using it for printing on stdout and leaving only the function in the code. Is it legit? Thanks for everything :)

Yeah. On this site, function submissions are generally allowed as an alternative to a full program

As an alternative to x=>{foo;bar;return z} you can usually do x=>(foo,bar,z)

Instead of Array.from(d) you can write [...d]. Also, in this case it's acceptable to take as input an array of characters, so you can write d

@SteveBennett Thanks!

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.

@SteveBennett Hi, I heard that but couldn't seem to get the right values using -~, even when only using floats.

~~ is equivalent to Math.floor for positive numbers

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

Ah, thanks - I did try that, but couldn't find a result that was actually shorter.

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.

Java (JDK), 56 bytes
s->s.map(c->("XXAVNZMWOOSSCU".indexOf(c)/2+3)%7-3).sum()

Try it online!

Stax, 20 19 bytes
ô≈X╙R┤╠Φyf∙H¡»₧ßc≡╡

Run and debug it

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.

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.

05AB1E, 39 38 36 34 33 28 bytes
lε.•3и½:∍äaŠ•sk>4%„Ý§uyåi(]O

Try it online! or Verify all test cases (as arranged by @sporeball)
-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?.

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.

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

Perl 5 -p, 48 bytes
$_=y/AV//+2*y/NZ//+3*y/MW//-y/CUSO//-y/SO//-y;O;

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!

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!

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.

156 bytes

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

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]
				-1+4*7%3$_ ; Push 3 - ((r - 1) * 4 % 7)				STACK = [-1]

Perl 5 + -pF, 39 bytes
$\+=y/OSUBANMCBVZW/0-62-6/&&$_-3for@F}{

Try it online!
Explanation
For each character in the input (stored in @F via -F) we add to $\ (defaults to '', implicitly output as the last argument for calls to print, which is triggered as part of -p) the value of $_-3, if we have translated (y/// is a synonym for tr///) any char in $_ (the current char taken from for@F) using OSUBANM to 0-6 and CBVZW to 2-6.

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