text:
Mathematics is fact. Programming is not
In mathematics an exclamation mark ! often means factorial and it comes after the argument.
In programming an exclamation mark ! often means negation and it comes before the argument.
For this challenge we'll only apply these operations to zero and one.
Factorial
0! = 1
1! = 1

Negation
!0 = 1
!1 = 0

Take a string of zero or more !'s, followed by 0 or 1, followed by zero or more !'s (/!*[01]!*/).
For example, the input may be !!!0!!!! or !!!1 or !0!! or 0! or 1.
The !'s before the 0 or 1 are negations and the !'s after are factorials.
Factorial has higher precedence than negation so factorials are always applied first.
For example, !!!0!!!! truly means !!!(0!!!!), or better yet !(!(!((((0!)!)!)!))).
Output the resultant application of all the factorials and negations. The output will always be 0 or 1.
Test Cases
0 -> 0
1 -> 1
0! -> 1
1! -> 1
!0 -> 1
!1 -> 0
!0! -> 0
!1! -> 0
0!! -> 1
1!! -> 1
!!0 -> 0
!!1 -> 1
!0!! -> 0
!!!1 -> 0
!!!0!!!! -> 0
!!!1!!!! -> 0

The shortest code in bytes wins.

But 0!=1!, so what's the point of handling multiple factorials?

@boboquack Because that's the challenge.

What type can the output have? (String, char, integer, boolean, float...)

<?='1'; ... correct 75% of the time in php.
@Zgarb The type doesn't matter as long as it would be represented as plain 0 or 1 (not 0.0, not True, etc.)

I may be wrong here but can't any number with any factorials after it simply be removed and replaced with 1? Like 0!!!! = 1!! = 0!!!!!!!! = 1!!! = 1! = 0! = 1 etc

@AlbertRenshaw That is correct.

@AlbertRenshaw - my answer is just one (humble) example of how and why this works for this challenge.

Do we have to handle invalid inputs in any way?

@theonlygusti since he didn't say anything about invalid inputs, you don't have to worry about them (that's the standart rules of codegolf).

You need to talk to Alan Turing about the title.

You could take this one step further and require !! to mean the double factorial etc.

I finally get it.

@TobiasKienzler That wouldn't change the results.

@MartinEnder It sure would: 5!! = 5•3•1 = 15 while (5!)! = 120! > 6.6e198.

@TobiasKienzler You don't ever get a value other than 0 or 1 as an argument to the (multi-)factorial though.

@MartinEnder Ah yes, of course. The difference only matters when any integer is permitted. And in contrast to the recursive factorial the double, triple etc. factorial would not overflow so quickly :)

Can we output True for 1 and False for 0?

Mathematica, 25 17 bytes
Input[]/.!x_:>1-x

Takes input from a user prompt. Assumes Mathematica's notebook environment for implicit printing. To make it a command-line script, wrap it in Print[...] or to make it an argumentless function (which then takes input from the prompt), append &.
Mathematica has both of the required operators (with the required precedence), so we can just "eval" the input (which is done automatically by Input[]), but the logical negation operator doesn't work on integers (so it will remain unevaluated). If there's a !x left in the result, we replace it with 1-x.
A couple of fun facts about the evaluation:

Mathematica actually also has the double factorial operator !!, which computes n*(n-2)*(n-4)*..., but applied to 0 or 1 it still gives 1, so it doesn't matter that 0!!!!! will actually be parsed as ((0!!)!!)!.
Even though Mathematica leaves !0 and !1 unevaluated, it does know that ! is self-inverse, so it will automatically cancel all pairs of leading !. After the ToExpression we're always left with one of 0, 1, !0, !1.

Since when was a REPL snippet allowed by default?

@LegionMammal978 Apparently since December 2015, but I keep forgetting about it. To be fair, it's not a "snippet" in that it doesn't assume that the input is already stored somewhere in memory. And assuming the notebook environment is then not very different from having a language with implicit output.

Just curious, could a meta link be provided? (Trying to find information there is stressful, yet another problem of the SE Q&A format...)

@LegionMammal978 it's already in the answer.

Pure ksh solution x=${x/[01]!*/1};echo $(($x)) - not allowed to post a proper answer :(

Bash + Unix utilities, 21 17 bytes
sed s/.!!*$/1/|bc

Verify the test cases online!
Added a TIO link to the test suite.
This must be saved in a file and run as a program.  If you try to enter the command directly from the command line, it won't work because !! is expanded due to history substitution being enabled in bash's interactive mode.  (Alternatively, you can turn history substitution off with set +H.)

The old version works, this one doesn't

I used the TIO link

@KritixiLithos It worked fine when I tried it on my Linux box. The problem was apparently that TIO requires a newline at the end of the simulated input line.  It's a confusing situation, so I took out the TIO link.  If you want to try it out there, here's the link again (but be sure to include a newline at the end of the input if you change the input to test it out): https://tio.run/nexus/bash#@1@cmqJQrK@nqKilom@oX5OU/P@/oqKiIRArcn3Ny9dNTkzOSAUA

But what if someone has run mkdir -p 's/.!!'{bunch,of,different,directories}\$/1?  Then you'll get Pathname Expansion and Sed will be attempting to read directories as though they were files, instead of reading standard input, and it won't output anything!  :)

@Wildcard Yes, that's true, of course. You can avoid this issue by writing \* instead of * in the regex, at the cost of 1 byte, but it seems to be considered acceptable on PPCG to assume that the program is being run in a directory without strangely-named files that expressions like s/.!!*$/1/ would expand to.

Of course.  :)  Just commenting.  In general, and in Production scripts, I wish people would play a little less fast and loose with assumptions relating to glob characters...but it's perfectly appropriate for code golf.  Consider my comment a token of my wish that some reader will actually learn when quotes should be used in the shell.

@Wildcard  I agree completely.  In production scripts, I always use quotes in situations like this.  (In this case, I would actually put double-quotes around the argument to sed, rather than just escaping the *.  It's easier to read than using backslashes, and it avoids the possibility of missing some special character.)

@MitchellSpector, ha—I always use single quotes unless I specifically need parameter expansion or command substitution.  Or unless I'm nesting the command inside find ... -exec sh -c 'for f; do ... ; done' sh {} + or similar.  Notably, history expansion is not prevented by double quotes.  :)  (Note: I'm surprised to see you don't have an account on Unix & Linux SE!)

@Wildcard  You're right -- this one would actually go in single quotes.  I glanced at it quickly, saw the $, and commented as if it used parameter expansion, but it doesn't.

Retina, 20 15 14 bytes
Thanks to Leo for saving 1 byte.
0!
1
!!

^1|!0

Try it online!
Explanation
0!
1

Turn 0! into 1. We don't care about any other trailing !s, the resulting number is the same as if we had applied all factorials.
!!

Cancel pairs of negations. This may also cancel some factorials, but that's irrelevant.
^1|!0

Count the number of matches of this regex, which is either 1 or 0 and gives the desired result.

Alternate solution for same bytecount: \d.+...

@KritixiLithos Found a way to avoid that altogether.

You can remove the ^ before !0

Grime, 14 12 9 bytes
e`\0!~\!_

Try it online!
Explanation
This matches the input against a pattern, printing 1 for match and 0 for no match.
e`\0!~\!_
e`         Match entire input against this pattern:
    !      not
  \0       a sole 0
     ~     xor
      \!   exclamation mark
        _  followed by this pattern matched recursively.

The idea is this.
If the input begins with a digit, then the recursive part \!_ always fails, and \0! succeeds unless we have a single 0.
Their xor succeeds unless the input is a single 0.
If the input begins with a !, then \0! always succeeds, and \!_ succeeds if the recursive match succeeds.
Their xor succeeds exactly when the recursive match fails, thus negating it.

Brainfuck, 85 72 (84) bytes
,[>-[-----<->]<++[>++++[-<++++>]+<[[+],[[-]>-<]]]>[<<+[-->]>[<],>-]<]<+.

to return numerically, or
,[>-[-----<->]<++[>++++[-<++++>]+<[[+],[[-]>-<]]]>[<<+[-->]>[<],>-]<]-[-----<+>]<--.

for ASCII text. > may also be prefixed to avoid memory wrapping.
Try it online!

Loops over the input.
On 1, ends.
On "!", toggles bool a stored as 0 or 255.
On "0", toggles if there is no trailing bit, then ends.

Memory labels  | BOOL | INPUT | FLAG |

,                   first input
[                     # loop on INPUT
  >-[-----<->]<++     subtract 49 == "1"

  [                     # case not "1"
    >++++[-<++++>]      add 16 since 49 take 16 == "!"

    +                   set FLAG
    <                   move to INPUT
    [                     # case "0"
      [+],                clear and new INPUT
      [                     # case "0!"
        [-]>-<              clear INPUT and FLAG
      ]
    ]
  ]

  >                   move to FLAG
  [                     # case "!" or "0" without tail
    <<+[-->]>[<]        not the BOOL
    ,                   take new input
    >-                  clear FLAG
  ]
  <                   move to INPUT
]

+.                    return 0 or 1

Or for text response, replace the last line with
-[-----<+>]<--.       add 49 for "0" or "1" conversion and return

Brainfuck - way to many bytes (232 bytes)
Clearly the wrong language for winning in code golf. Mainly I noticed a lack of anyone using this esolang. There is a good online interpreter bf interpeter or you can actually watch what the program does using this bf visualizer.
>>>>>,[>+++[<---------------->-]<<<<<<[-]+>[-]>>>>[-[<<[>+<<<<->>>[<<+>>-] ]<<[>>+<<-]<[>>+<<[-]]>>>>>[-]]<<<<<[>>>++<<<-]>+>>>>[-]]<<<<-[>>+<<[-]]>>>>,]<<->[<[-]+>[-]]<<[<[-]>>[<<+>>[-]]+<<[->>-<<]>-]>>[-]+++[<++++++++++++++++>-]<.

You crazy man!!

Love it, can you do that in malbolge? XD

Information: There are too much shorter solutions below.

Python, -44- 42 bytes
Saved 2 bytes thanks to Zgarb!
lambda x:(x[-1]=='0')^len(x.rstrip('!'))%2

Step by step:

x[-1]!='0'
if x ends with 1 or ! ⇔ x doesn't end with 0, the factorial portion must have value 1, else 0
^len(x.rstrip('!'))%2
exploit xor's property as a "conditional not". The condition in this case is if the length of initial !s is odd. However, .rstrip doesn't remove the number from the string so the length calculated is offset by 1, therefore the condition is inverted
The offset by 1 in step 2 is corrected by changing != to == in step 1. Zgarb suggested using a difference comparison operator rather than applying another inversion, saving 2 bytes.

Try it online!

Fails on an input of !!0; it is currently returning 1.

@ValueInk should be working now

lambda x:(x[-1]=='0')^len(x.rstrip('!'))%2 avoids the extra inversion.

@Zgarb Cool, thanks!

crossed out 44 is still regular 44

@EasterlyIrk What do you mean?

I think he's stating that a crossed out 44 in the font used doesn't look crossed out... :) The cross out portion overlaps with the horizontal portion of the 4s.

JavaScript (ES6), 43 41 29 bytes
s=>+eval(s.replace(/.!+$/,1))

Non-regex method (41 31 bytes)
Below is my initial approach. It's slightly more interesting, but significantly longer still a bit longer even after a significant optimization by Neil (10 bytes saved).
f=([c,...s])=>1/c?c|s>'':1-f(s)

Test cases

let f =

s=>+eval(s.replace(/.!+$/,1))

;[
  "0", "1", "0!", "1!", "!0", "!1", "!0!", "!1!", "0!!",
  "1!!", "!!0", "!!1", "!0!!", "!!!1", "!!!0!!!!", "!!!1!!!!"
].map(
  s => console.log(s, '=>', f(s))
)

I can only save 10 bytes from your non-regex method, so it's still too long: f=([c,...s])=>1/c?c|s>'':1-f(s).

@Neil Since it's much better than my 1st attempt anyway, I took the liberty to include your suggestion.

Ha, I had the same idea but you golfed it better. :)

Retina, 13 bytes
A somewhat weird approach, but it's short and it works.
0$
!1
!!

^\d

With the first two lines we replace an ending 0 with !1: with this replacement we now know that the part of our string from the digit onwards is equal to 1.
Next two lines, remove pairs of !: double negation erases itself, and we already accounted for factorial with the previous step.
Last line, match a digit at the start of the string and return the number of matches: if the negations have all been eliminated we'll find a match (and as we said before we know this is equal to 1), if there's still a negation this won't match.
Try it online!

Wouldn't the final digit necessarily always be a 1? In that case, you could use 1 rather than \d.

@ais523 no, because the first part will only replace an ending 0, so for example the input 0! will stay unchanged until the last line

Really lovely solution, nice work! :)

Jelly, 5 bytes
VeMḂ$

Try it online!
Monadic function expecting a string. Inputs with leading !s cause a 1 to be printed to STDOUT along the way, so the TIO link I give is a test harness that prints the input-output pairs beneath the first line of output.
How?
VeMḂ$ - Monadic link: string
V     - eval the string
          - the implicit input of 0 causes !...! to evaluate to 1 (which gets printed),
          - the result is the evaluation of the rest: "0"=0; "0!"=1; "1"=1; "1!"=1; ...
 e    - exists in?
    $ - last two links as a monad:
  M   -     Maximal indexes - the "0" and "1" characters are greater than "!",
                            - so this results in a list of one item [i] where
                            - i is the 1-based index of the 0 or 1 character.
   Ḃ  -     %2 (vectorises) - [i%2], so a 0 if we need to logically negate and a 1 if not
                            - hence we check equality with e rather than inequality.

05AB1E, 9 bytes
Code:
.V¹'!ÜgG_

Uses the CP-1252 encoding. Try it online! or Verify all test cases!
Explanation:
.V         # Evaluate the input as 05AB1E code. This computes the factorial part.
   '!Ü     # Remove trailing exclamation marks..
  ¹        # ..from the first input
      g    # Get the length of the resulting string
       G   # Do the following length - 1 times:
        _  #   Negate the number

Ruby, 12+1 = 39 24 15 13 bytes
Uses the -n flag. Thanks to @GB for -9 bytes!
p~/!*$|0$/%2

Since you only check the length, you can delete the trailing zero, instead of checking "!0" first and a single zero after that.

@GB that's a wonderful idea! However, I found a solution that's even shorter by modifying my regex to look for the position of 0 or end-of-line

Then you could just check for trailing '!' or zero or end of line: p~/!+$|0$|$/%2 is just 14 bytes.

And then "0$|$" could become "0?$" to save another byte.

Better yet !*$ is shorter by two!

Cool. Are you sure it works for all test cases? I can't check right now.

@GB yes, it works. It's just using the same principle as what your suggestion was, but instead of 0 being the optional one, ! is.

Perl, 20 bytes
19 bytes of code + -p flag.
s/\d!+/1/;$_=0+eval

Try it online!
Perl's negation returns undef or 1, so I use 0+ to numerify the result 0+undef returns 0. Besides that, not much to say about the code.

Just wrote exactly this. Have a +1.

@primo Glad to see that for once I'm not 20 bytes behind you! Thanks :)

C, 68 62 61 53 bytes
c;e(char*a){for(c=1;*a<34;a++)c^=1;c=a[1]?c:*a&1^!c;}

Squeezed out a few more bytes with some abuse
Try it online!

I think you can remove the int from the function and you can change the *a==33 to *a<34.

Alas *a%2 is shorter than *a-48

Thanks for the tip. I was also able to eliminate another character by removing the brackets around the return and assigning it.

I am pretty sure for(;*a<34;a++) can be shortened to for(;*a++<34;) saving 1 byte

Unfortunately not, as the conditional statement it will always execute and so will push the pointer too far ahead for the return dereference.

-1 byte: c;e(char*a){for(c=1;*a++<34;)c^=1;c=*a?c:*--a&1^!c;}

49 bytes

Haskell, 27 bytes
f('!':b)=1-f b
f"0"=0
f _=1

Try it online!
Each leading ! complements the output for the rest of the expression, done as 1-. We keep flipping until we hit a digit. If the remaining is just "0", the result is 0. Otherwise, it's a 1 or is followed by one or more !, so the result is 1.

Haskell, 39 bytes
f('!':b)="10"!!read[f b]
f[a]=a
f _='1'

Defines a function f, which takes a string and returns a character.
Try it online!
Explanation
There are three cases: input begins with !, input has length 1, and everything else.
f('!':b)=    -- If input has head '!' and tail b,
 "10"!!      -- we index into the string "10"
  read[f b]  -- using f b converted to int. This essentially inverts f b.
f[a]=        -- If input has only one character, we know it's a digit,
 a           -- so we can just return it.
f _=         -- In all other cases, we know the input is a digit followed by !s,
 '1'         -- so we can return '1'.

Switch from String to Integer as the return type: f('!':b)=[1,0]!!f b;f"0"=0;f _=1.

Perl 6, 32 28 23 bytes
{m/(\!)*(1|0.)*/.sum%2}

How it works
{                     }  # A lambda.
{m/            /      }  # Match the lambda argument against the regex:
   (\!)*                 #   Zero or more `!`.
                         #     (First capture will be an array with one element per negation).
        (1|0.)*          #   A `1`, or a `0` and another character, zero or more times.
                         #     (Second capture will be a one-element array if the factorial
                         #     part evaluates to 1, and an empty array otherwise.)
                .sum     # Add the lengths of the two captures,
                    %2   # and return that sum modulo 2.

Befunge, 24 bytes
~"!"-:#v_$1+
*+2%!.@>0~`

Try it online!
This starts by counting the number of ! characters read from stdin. The first character that isn't a ! will either be a 0 or 1, but in the process of testing for ! we will have subtracted 33, making it either 15 or 16. We then read one more character, that will either be an ! or EOF, and compare if that is less than 0 (i.e. EOF).
Taking those three data points - the exclamation count (c), the digit value, (d), and the end-of-file condition (e) - we can calculate the result as follows:
!((c + d*e) % 2)

Multiplying the digit value by the end-of-file condition means it will be converted to zero if the digit was followed by a !, thus giving it the same modulo 2 value as a 1 (which remember has been converted to 16). But before applying the modulo 2, we add the initial exclamation count, which effectively toggles the modulo 2 result as many times as their were ! prefixes. And finally we not the result since our baseline values for 0 and 1 are the opposite of what we need.
Looking at the code in more detail:
~                Read a character from stdin.
 "!"-            Subtract 33 (ASCII for '!').
     :  _        Make a duplicate and check if zero (i.e. is it a '!').
         $1+     If so, drop the duplicate, increment a counter, and repeat.
       v         Otherwise move to the second line, leaving the digit value on the stack.
       >0~`      Read one more character and check if less than 0 (i.e. EOF).
*                Multiple by the digit value, making it zero if not followed by EOF.
 +               Add to the exclamation count.
  2%             Modulo 2 the result.
    !            Then not that value.
     .@          And finally write to stdout and exit.

Ruby, 22 21 20 bytes
->s{(s=~/!*$|0$/)%2}

Explanation:

First case, I got some '!' at the end, remove them, get length modulo 2.
Second case, no '!', if last character is zero then remove it, get length modulo 2
If the last character is 1, back to the first case

(-1 byte stealing @Value Ink's idea)

Awesome, I looked at this puzzle for 10 minutes but had not much time and then forgot about it. Now spotted it again in active questions and was delighted to see such a nice approach.

Python, 38 bytes
lambda s:(s[1::2]>s[::2])^ord(s[-1])%2

TryItOnline!
An unnamed function taking an input string s and returning an integer 0 or 1.
s[1::2] is a slice of the input string that starts at index 1 and has a step size of two:
'Like this' -> 'ieti'
s[::2] is similar but starts at the default index of 0:
'Like this' -> 'Lk hs'
The test (s[1::2]>s[::2]) checks if the 0-based index of the '0' or '1' is odd, i.e. if we need to complement.
This works because the ordering of strings is checked lexicographically with any non-empty string greater than the empty string, and with ASCII ordering, so '1'>'0'>'!'. This is a byte shorter than the simpler s.index(max(s))%2.
The ord(s[-1])%2 checks to see if the last character is not a '0' (for valid input), and results in an integer (whereas the same length (s[-1]!='0') would return a boolean).
This works because the last character of the input, s[-1], will be a '0', '1', or '!' which have ASCII code points 48, 49, and 33 respectively, which are 0, 1, and 1 modulo 2.
The ^ then performs a bitwise exclusive or operation on the two above values, returning an integer since one input, the right one, is an integer. If the left is True the complement of the right is returned, if the left is False the right is returned, as required.

APL (Dyalog Extended), 21 bytes
⍎⌽'!(\d.*)'⎕R'\1~'⍣≡⍞

Repeatedly shift any !s to the right with the fixpoint ⍣≡
!!!!0! -> !!!0!~ -> !!0!~~ -> !0!~~~ -> 0!~~~~
Then reverse with ⌽, and execute with ⍎
Try it online!

Your regex can be a byte shorter.

@user41805 how?

I think ^!(.*) works

///, 27 bytes
/0!/1//!!///!1/0//!0/1//!//

Try it online!

I love me a /// answer. Here's some -16 bytes!

@AviFS Thanks, I don't know why I thought that part was neccesary!

You can remove the /1!/1/ and clear the !s at the end instead

@JoKing That's a great point; totally slipped me by as well!

Jelly, 8 bytes
œr”!LḂ=V

Try it online!
This is a function (monadic link) that takes one argument and returns via its return value. (It also often writes junk to standard output as a side effect, but we don't care about that.)
Explanation
œr”!LḂ=V
œr”!      Take {the input}, with all trailing ! deleted
    L     Take the length of this
     Ḃ    Take the parity of that length
      =   Return 0 if unequal, 1 if equal to:
       V    the value of {the input} when eval'ed as a niladic Jelly program

First, note that as the input always consists of some number of !, followed by a digit, followed by more !, that if we delete the trailing ! and take the length, we'll end up with one plus the number of leading ! in the program. Taking the parity of this will return 0 if there were an odd number of !, or 1 if there were an even number of !. Comparing to 0 is a "not" function, whereas comparing to 1 is the identity function; thus œr”!LḂ= effectively implements the "treat leading ! as NOT operators" part of the question.
As for the second half, handling factorials, ! is a factorial operation in Jelly, so if the program has no leading !, we can solve the problem directly with a simple eval (V). If the program does have leading !, those will be interpreted as taking the factorial of 0 (possibly multiple times), producing a return value of 1, which will be printed to standard output and discarded once a digit is seen; thus, they have no impact on the return value of the function that's my submission to the question.

Very nice and great explanation.

Java 7, 105 82 81 bytes
int a(char[]a){int b=0,c=0;for(;a[b++]<34;c^=1);return(b<a.length?1:a[b-1]&1)^c;}

Try it online!
Old regex-ish solution
int a(String a){a=a.replace("0!","1").replaceAll("1.*","1");int b=a.length()-1;return b%2^a.charAt(b)&1;}

c^=1 is super clever. That's an unused operator if ever I've seen one.

Haskell, 67 65 bytes
f s|foldr(\_->not)(last s`elem`"1!")$fst.span(<'0')$s="1"|1<3="0"

Try it online! Usage: f "!!!0!!!!"
Saved two bytes thanks to @nimi.

CJam, 12 11 bytes
r_W='0=!\~;

Try it online! Test suite (prints a 1 for each correct test case).
r      e# Read input.
_W='0= e# Duplicate and check whether the string ends in '0'. This is the
       e# only case in which the factorial part results in 0.
!      e# Negate this to get the actual result of the factorial part.
\      e# Swap with the input.
~      e# Evalute the input as CJam code. The leading `!` will apply the logical
       e# negations to the factorial result. The 0 or 1 will then push a junk value
       e# which is potentially negated a few times as well, by the factorials.
;      e# Discard the junk value.

sed, 36 33 31 bytes
Pure sed, no bc / shell utils. Works on GNU sed < 4.3; 33 bytes on BSD and GNU 4.3+.
s/.!!*$/1/
:
s/!0/1/
s/!1/0/
t

Straightforward enough if you're familiar with sed; commented for those who aren't:
# Since 0! == 1! == 1 and factorial has precedence, just collapse any trailing "!"
s/.!!*$/1/
# Define an anonymous label
:
# Invert 0 if needed
s/!0/1/
# Invert 1 if needed
s/!1/0/
# If a change was made, go back to the anonymous label.
t

Test:
% cat 109248.sed
s/.!!*$/1/
:l
s/!0/1/
s/!1/0/
tl
% wc -c 109248.sed
      33 109248.sed
% cat cases
0
1
0!
1!
!0
!1
!0!
!1!
0!!
1!!
!!0
!!1
!0!!
!!!1
!!!0!!!!
!!!1!!!!
% sed -f 109248.sed cases
0
1
1
1
1
0
0
0
1
1
0
1
0
0
0
0
% gsed -f 109248.sed cases
0
1
1
1
1
0
0
0
1
1
0
1
0
0
0
0
%

IIRC some (all?) versions of sed allow you to use the null string as a label name. If you can get that to work here, it'd save you two bytes. Actually, I'm not sure the label is even needed; unless I've missed something, the first line is idempotent, so you might be able to jump back to the start of the program rather than needing a label.

@ais523 I thought so too, but evidently it doesn't work in BSD versions. The man page says "If no label is specified,
branch to the end of the script," and even that wasn't working when I tried.

GNU sed does allow a label to be just : (more of a bug taken as feature), in which case both the t and b! commands jump to the position of the label. Plus, a sed code must work for at least one version of sed, similar to other languages, so you don't need to create code that works for BSD as well.

Brainfuck, 115 bytes
>,[->++++[<-------->-]<[--------------->,[<[-]+>-]<<[->-[>+<+]>[-<+>]<<]>>++++++[-<++++++++>]<.>>+<]>-[<<+>,>[-]]<]

Try it online!
Ungolfed:
% 0: inverter count
% 1: result
% 2: if/else flag; tmpspace in inner loop 0

>1,[
    ->2++++[<-------->-]<1 subtract 33 (!)
    [
        % we've reached the number
        ---------------
        % now it's either 0 or 1

        % check next char; If it's not 0 then it's '!'
        % 0! = 1! = 1!...! so we only need to determine if at least one ! exists
        >2,
                [<[-]+>-]<1

        % apply inversions
        <0
        [->1
            % invert cell 1 once each iteration
                       % cell 1 is 0 or 1
            -          % cell 1 is 255 or 1
            [>+<+]     % cell 1 is 0; cell 2 is 1 iff cell 1 should be 1
            >2[-<+>]<1 % cell 1 is 1 or 0
        <0]

        % print result
        >1>++++++[-<++++++++>]<1.

        >>2+< % tape={0 r 0 1}
    ]
    >2-[ % we haven't seen the number yet
        <<0+>1,>2 % add to inverter count
        [-]
    ]<1
]

Vyxal, 16 bytes
`\d!+`1øṙ\!\¬VṘĖ

Try it Online!
We do a little regex.
Explained
`\d!+`1øṙ\!\¬VṘĖ
`\d!+`            # The string "\\d!+" ( a regex that matches the digit followed by !)
      1øṙ         # and replace it with "1" (because 0! = 1! = 1)
         \!\¬V    # replace any remaining "!" with "¬" (not)
              ṘĖ  # then reverse and evaluate

><>, 18 bytes
ib%:?\
+2%n;\i+3=l

Try it online!
Solved as part of LYAL CMC.
The basic idea is to process from left to right, doing the following:

If the current char is ! (boolean negation), keep it on the stack so that stack height can be used for negation count.
If the current char is 0 or 1, take one more char and test if there's a factorial (modifying the number to 1 if there is). Apply negation, print as number, and halt.

Optimizations:

To get the final number, we can simply do 2% at the end, which means we can just add the stack height + 1 (because the final stack height would be one more than the number of negations).

To discriminate between 48/49 and 33, we can do b% (modulo 11, idea by Jo King).

The (digit, factorial-or-EOF) combinations to handle are:
(48, 33) -> 1
(49, 33) -> 1
(48, -1) -> 0
(49, -1) -> 1

We can use 48%11 = 4 and 49%11 = 5. Then a simple way to handle these combinations is to check if the sum of two numbers is 3 or not. We could use "is not 3" for the condition, but it turns out that we can negate it and remove the extra 1+.

PHP 7.1, 58 55 54 37 35 bytes
Note: uses IBM-850 encoding
echo!!$argn[-1]^strspn($argn,~Ì)%2;

Run like this:
echo '!!!0!!!!' | php -nR 'echo!!$argn[-1]^strspn($argn,~Ì)%2;';echo
> 0

Explanation
echo
  strspn($a=$argv[1],~Ì) # Count the number of leading exclamation marks.
  % 2                    # Make 0 (even) or 1 (odd).
  ^ !!$a[-1];            # Negate with factorial part (truthy value of the
                         # last char):
                         # - "0" is considered falsy.
                         # - "1" or "!" is considered truthy.

Tweaks

Saved 3 bytes by using IBM-850 encoding
Saved a byte by changing the regex slightly
Saved 17 bytes, new version without long function names and return
Saved 2 bytes by using -R (which makes $argn available)

Batch, 62 bytes
@set/ps=
@set s=%s:0!=1%
@set s=%s:!!=%
@cmd/cset/a%s:1!=1%

Takes input on STDIN. Batch actually understands leading !s correctly for this challenge, but the trailing !s need to be dealt with, which takes three steps:

Change 0! to 1
Delete pairs of !! (this is safe for the !!s before the digit too)
Delete any remaining trailing ! (which by now can only be after a 1)

Pyth, 11 bytes
s.v:z"0!"\1

Try it online!
Explanation
s.v:z"0!"\1
   :         Replace...
    z        in the input...
     "0!"    the string "0!"...
         \1  with the string "1".
 .v          Evaluate the result. Since only the first expression is evaluated,
             anything after the number will be ignored.
s            Convert the result to an integer and implicitly print it.
             This is necessary because ! returns True/False, not 0/1.

Japt, 11 bytes
+OxUe/\d!/1

Try it online!
How it works
+OxUe/\d!/1

   Ue        Apply recursive replace to input string...
     /\d!/1    which replaces digit+! to 1
 Ox          Eval the result as vanilla JS
+            Cast the result (String or Boolean) to Number

The idea is similar to Arnauld's JS answer though I came up with it independently.

You could use the -N flag to get rid of the +.

Scala, 65 63 47 bytes
s=>s.takeWhile(34>).length%2^(s.last-48).abs%14

Try it online!
Many thanks to user for -16 chars!!!

Great answer. You can also use ascii values to get 59 bytes

47 bytes, actually

Python, 183 159 125 bytes
-34 bytes thanks to @wasif
Probably not going to beat anyone, but it was fun.
g=lambda x:x<2or x*f(~-x);from re import*;a,b,c=split("(\d+)",input())
exec("b=g(int(b));"*len(c)+"b=not b;"*len(a));print(b)

Try it online!

You can replace n=factorial(n) with just n=1 (and therefore remove the math import). Similarly, n=not n with n=1-n and remove the int part later. The first for loop could be an ir statement (or some fancy logic in the initialisation of n) and the import is shorter if you just do import re

125 bytes! (outputting True for 1 and False for 0 is allowed) golfing it more will make it identical to other answers

IBM/Lotus Notes Formula - 77 bytes
@Eval(@Left(a;@If(@Like(a;"%1%");"1";"0"))+@If(@Ends(a;"!");"1";@Right(a;1)))

There is no TIO for Notes Formula so a screenshot of all test cases is shown below:

How it works
@Eval() evaluates a string as an expression
First we check if the input string in field (input) a contains 1 or 0 and take all characters to the left of whichever it is which will be a string of ! characters. We don't care how many. @Eval() will take care of that.
Next we look to see if there is a ! at the end of the string. If there is we append 1 to the ! string (0! and 1! are both 1 - it doesn't matter how many ! characters there are at the end) otherwise we append the last character unchanged because it is not a ! and could be either a 1 or a 0.
We now have a string containing the leading inversions plus a number defined by whether there are any factorial characters so we can feed this to @Eval() and get the results above.

Bean, 24 bytes
Hexdump:
00000000 26 4a c1 53 a0 17 53 d0 80 a0 5d 20 80 0a a1 80  &JÁS .SÐ. ] ..¡.
00000010 81 00 25 3a ae a1 ab 24                          ..%:®¡«$
00000018

Equivalent JavaScript:
+eval(a.replace(/.!+$/,1))

Sorry for stepping on your toes, Arnauld.
Explanation:
Takes first line of input as unformatted string in a, and replaces any digit followed by one or more ! with 1, so that the rest can be eval'd by JavaScript.
Try the demo, or the test suite

C#, 88 84 bytes
Saved 4 bytes thanks to TheLethalCoder.
s=>{var c=s.Replace("!","")[0];int b=s.IndexOf(c);return(s.Length>++b?b:b+c-49)%2;};

Previous version:
s=>{var c=s.Replace("!","")[0];int b=s.IndexOf(c),n=s.Length-b>1?1:c-48;return(n+b)%2;};

Full program with commented method and test cases:
using System;

class MathIsFactProgrammingIsNot
{
    static void Main()
    {
        Func<string, int> f =
        s=>
        {
            // removes all the exclamation marks and extracts the 0 or 1 digit
            var c = s.Replace("!","")[0];

            // number of exclamation marks before the digit
            int b = s.IndexOf(c),

            // the number of exclamation marks after the digit increased by 1 (because of the digit)
                n = s.Length - b > 1 ? 1 : c-48;
            // if no exclamation marks are present, converts the digit from the string to an integer

            return (n + b) % 2;   // applies binary negation
        };

        // test cases:
        Console.WriteLine(f("0"));  // 0
        Console.WriteLine(f("1"));  // 1
        Console.WriteLine(f("0!")); // 1
        Console.WriteLine(f("1!")); // 1
        Console.WriteLine(f("!0")); // 1
        Console.WriteLine(f("!1")); // 0
        Console.WriteLine(f("!0!"));    // 0
        Console.WriteLine(f("!1!"));    // 0
        Console.WriteLine(f("0!!"));    // 1
        Console.WriteLine(f("1!!"));    // 1
        Console.WriteLine(f("!!0"));    // 0
        Console.WriteLine(f("!!1"));    // 1
        Console.WriteLine(f("!0!!"));   // 0
        Console.WriteLine(f("!!!1"));   // 0
        Console.WriteLine(f("!!!0!!!!"));   // 0
        Console.WriteLine(f("!!!1!!!!"));   // 0
    }
}

n is only used in the return so you can remove declaring it.

PowerShell, 28 bytes
+($args-replace'\d!+',1|iex)

Try it online!
Explanation
Replace any digit followed by 1 or more ! with 1 (to solve the factorials), then just execute the remaining string which is valid code. The result will be boolean so I use unary + to convert it to a number.

F#, 69 48 Bytes
let rec f=function|'!'::t->1-f t|'0'::[]->0|_->1

Try it online!
Tests
let test (input,expected) =
  let result = input |> Seq.toList |> f
  if result = expected then
    printfn "%s = %d" input result
  else
    printfn "Error at %s" input

[
  ("0", 0)
  ("1", 1)
  ("0!", 1)
  ("1!", 1)
  ("!0", 1)
  ("!1", 0)
  ("!0!", 0)
  ("!1!", 0)
  ("0!!", 1)
  ("1!!", 1)
  ("!!0", 0)
  ("!!1", 1)
  ("!0!!", 0)
  ("!!!1", 0)
  ("!!!0!!!!", 0)
  ("!!!1!!!!", 0)
] |> Seq.iter test

You're allowed to take a string as a list of characters, meaning that r is a valid solution by itself. That should save you quite some bytes.

Oh, thanks! That indeed saved a lot of bytes

Befunge-98 (PyFunge), 22 20 bytes
Golfed off 2 bytes because I realized that if I used k instead of j to do the logical notting to account for a 0 without a factorial, I didn't have to mod the ASCII value of 0 or 1 by 2. I did have to move the code because removing the 2% left a hole between the $~ and #< previously.
-kv$!~:'!
#<k!.@.$$~

Try it online!
Explanation
The first line:
-kv$         Does nothing because the top of the stack is 0
    !        Nots the top, so we start with a value of 1
     ~       Gets a character from input
-     :'!    Pushes (that character - the value of '!')
 kv          If that value is not 0 (the character was not '!'), go to the next line
   $!        If it is, Throw away the extra '!' and not the number below it (originally 1)
             Repeat from the ~

Now that we're on the second line, we have notted 1 for each ! before the number. If there is a ! after the number, we can just print this value, but if there isn't we need to adjust for whether or not the number is 0.
The second line:
 <            Directs the IP left.
#             Doesn't skip anything because it's at the beginning of a line
         ~    If we reached EOF (no factorials), the ~ will reverse the IP's direction
       $$     No EOF: Drop the '!' we just read along with the number
     @.               Print (1 notted the appropriate amount of times) and end
#<            EOF: Wrap around and skip the arrow
  k!               Not the top n + 1 times, where n is the ASCII value of 0 or 1 (48 or 49)
                       If the number is 1, it will be notted 50 times (even),
                       yielding no change.
                       If the number is 0, we not it 49 times (odd), which makes up for
                       starting with a 1 at the beginning.
    .@             Print this value and exit

Can you save a couple of bytes by outputting via exit code?

Vim + bc, 18 bytes
:s/\d!\+/1␊V!bc␊

␊ is a literal newline
Explanation

:s/\d!\+/1␊  Replace any digit followed by factorials with 1 ("0!!!" -> "1"; "1!!" -> "1")
V!bc␊        Evaluate the not-operators using the `bc` command, similarly to this answer

Vim, 36 bytes
:s/\d!\+/1␊:s/!!//g␊:s/!1/0␊:s/!0/1␊

␊ is a literal newline
Explanation
:s/\d!\+/1␊  Replace any digit followed by factorials with 1 ("0!!!" -> "1"; "1!!" -> "1")
:s/!!//g␊    Remove all double nots ("!!!!!" -> "!"; "!!!!!!" -> "")
:s/!1/0␊     Replace !1 with 0
:s/!0/1␊     Replace !0 with 1

C (gcc), 42 bytes
c;e(char*a){c=*a<34?!e(a+1):*a-48|1[a]%2;}

Try it online!

R, 41 bytes
+eval(parse(t=gsub("\\d!+",1,scan(,""))))

Try it online!
Two and a half years later, finally fixed the bug pointed out by JayCe, by switching * in the regex to a +...
The + is just to coerce the logical to numeric.

For some reason it's not working when I test it. What about this?

@JayCe did you somehow reassign + to something else? ;-) I'm not having any trouble testing it (on TIO)...do you have a particular example? That approach is, I think different enough that it can be its own answer.

You're right, my bad! Will post separately as you're suggesting. That said, we both return 1 for the input 0 so I need to fix this first...

@JayCe goood point. In that case, I need to fix my answer, so I'll be deleting it for the moment.

GolfScript, 11 bytes
Blatant port (not copying the whole algorithm) of Martin Ender's CJam answer. At least I got the same length.
.)\;48=!\~;

Try it online!
Explanation
.           # Add a fresh copy of the input
 )          # Generate the input without the last item
            # and the last item of the input
  \         # Swap so that the first output of ) is on top
   ;        # Remove so that there is only the last item
    48=     # Is the item equal to 48 (ASCII '0') ?
       !    # If not (0), there are !'s before either 0 or 1
            # and the result is obviously 1 (0 negated)
            # If yes (1), the last item is going to be 0,
            # and the result is 0 (1 negated).
        \   # Take the fresh unused copy of the input to the top
         ~  # Apply the input to the top
          ; # (Due to postfix evaluation the trailing
            # !'s are applied to the number.)
            # Discard the applied number, which is garbage

# Implicitly output the result
```

Rust macros, 69 bytes
macro_rules!f{(!$($x:tt)*)=>{1-f!($($x)*)};(0)=>{0};($($x:tt)*)=>{1}}

Defined a macro f that takes a sequence of tokens (including !, 0 and 1) as input and produces a token sequence that evaluates to the expected result. Port of a Haskell answer.
try it online
Explanation
macro_rules! f {
    (! $($x:tt)*) => {  // match an exclamation mark followed by any number of token trees
        1 - f!($($x)*)  // expand to 1- and call the macro recursively
    };
    (0) => {            // match a 0 that's not followed by anything
        0               // replace with a 0
    };
    ($($x:tt)*) => {    // match any number of token trees
        1               // replace with 1
    }
}

Python 2, 37 bytes
f=lambda s:s<"0"and 1-f(s[1:])or"0"<s

Try it online!
Sometimes returns True/False instead of 0/1, which is allowed I think.
How

The factorial part can be evaluated simply as "0"<s. This is 0 only when s is 0, and 1 if s is anything else (e.g 1 or 0!!).
The negation part is evaluated using recursion. If s starts with ! (s<"0"), recurs as 1-f(s[1:]).

You can shorten the and part to s<"0">1>f(s[1:]) if you don't mind it always being True/False

Vim, 29 bytes
:s/\d!!*\|1
A<C-r>=col('.')%2
<esc>d|

Try it online!
Explanation
If we normalize the number-and-factorial part, we can get the answer by counting the number of characters on the line mod 2.
:s/\d!!*\|1//<cr>

Substitute either 1) a digit followed by one or more !s or 2) a 1 digit with the empty string. (In the golfed version, the two final /s can be omitted.) This leaves a 0 if there was a lone 0 to begin with, and otherwise deletes the digit and everything after it.
A

Go to the end of the line and append something. The cursor is now positioned one column after the last character on the line.
<C-r>=col('.')%2<cr>

Calculate the column number mod 2 and insert that result (either 0 or 1).
<esc>d|

Leave insert mode; the cursor is now on the just-inserted character. Delete everything to the left of the cursor.

CLC-INTERCAL, 182 bytes.
The following source must be in Latin-1 or ISO-8859-1 encoding:
DO;1<-#1DO.9<-.9/;1SUB#1DOCOMEFROM:9DO.8<-"¥'.8¢:9'"~#1DOWRITEIN;1(1)DO:9<-.9~#16DO.7<-.9~#1DOWRITEIN;1DO.9<-"V'"'¥"'"&'"'¥"#1¢'.9~#4'"'~#1"¢.7'"~#1'¢.8"'~#1"¢#100'"~#5461DOREADOUT;1

Assumed I/O

Uses standard input/output.
Input: a line of the text. Does not matter trailing LF.

How to try it online
Try it online! Because of encoding issue, I recommend uploading and submitting the source rather than copying and pasting the code to the form.
Also it may take very long time until the program ends.
How to test
Use the following shellscript:
#!/bin/sh
set -eu
cat<<'.' | awk 'BEGIN{ORS=""}$0=(NR>1?"":RS)$0' | iconv -f UTF-8 -t ISO-8859-1 >.i
DO;1<-#1
DO.9<-.9/;1SUB#1
DOCOMEFROM:9
DO.8<-"¥'.8¢:9'"~#1
DOWRITEIN;1
(1)DO:9<-.9~#16

DO.7<-.9~#1
DOWRITEIN;1

DONOTE remove the following long line to uncomment four sentences that begin with "DONOT.9<-" because it will result in timeout because of stupidly long expression.
DONOTE don't worry because they work equivalently.
DO.9<-"V'"'¥"'"&'"'¥"#1¢'.9~#4'"'~#1"¢.7'"~#1'¢.8"'~#1"¢#100'"~#5461

DONOT.9<-.9~#4
DONOT.9<-#1¢.9
DONOT.9<-.¥9~#1
DONOT.9<-"V'"'¥"'"&'".9"¢.7'"~#1'¢.8"'~#1"¢#100'"~#5461

DOREADOUT;1

.
wc -c .i >&2
sick .i
: >o
while read p _ q; do
   o="$(
      printf %s "$p" |
      ./.io |
      tee -a o
      printf X
   )"
   [ "X$o" = "X${q}X" ] &&
      printf "%s: Passed\n" "$p" >&2 ||
      printf "%s: Failed\n" "$p" >&2
done <<'.'
0 -> 0
1 -> 1
0! -> 1
1! -> 1
!0 -> 1
!1 -> 0
!0! -> 0
!1! -> 0
0!! -> 1
1!! -> 1
!!0 -> 0
!!1 -> 1
!0!! -> 0
!!!1 -> 0
!!!0!!!! -> 0
!!!1!!!! -> 0
.
cat o

How it works
DONOTE ;1SUB#1 shall be these:
   '!' is #116
   '0' is #101
   '1' is #100
DONOTE actually first 16 bits are random bits
DONOTE that has at least a 1 bit
DONOTE when WRITEIN is done
DONOTE then second 16 bits are the values above
DONOTE EOF is really #0
DONOTE otherwise at least 2^16

DO;1<-#1 DONOTE for i/o
DO.9<-.9/;1SUB#1 DONOTE replaces every .9 with ;1SUB#1

DOCOMEFROM:9 DONOTE it is from label 1
DO.8<-"¥'.8¢:9'"~#1 DONOTE for fact
DOWRITEIN;1
(1)DO:9<-.9~#16 DONOTE '!' is #116 others are #101 or #100

DO.7<-.9~#1 DONOTE '0': 1, '1': 0

DOWRITEIN;1 DONOTE has trailing '!'?
DONOTE at this point .9 is '!': #116, EOF: #0, LF: #89

DONOTE explaining long expression

.9~#4                 #1 if .9&0x04==0x04 else #0
'¥"#1¢'...'"'~#1      ^#1
"&'"..."¢.7'"~#1      &.7
'¥"'...'¢.8"'~#1      ^.8
"V'"..."¢#100'"~#5461 |#100

DO.9<-"V'"'¥"'"&'"'¥"#1¢'.9~#4'"'~#1"¢.7'"~#1'¢.8"'~#1"¢#100'"~#5461

DONOTE finally
DOREADOUT;1
```

Swift, 58 bytes
let f={($0.last=="0" ?0:1)^($0+"").prefix{$0<"0"}.count%2}

Basically ended up being a port of @busukxuan's Python answer.

X bytes? did you forget to add the byte count?

@noodleman Wow, yes I did. I’ll just go ahead and fix that…

RProgN, 31 bytes
~'(!*1?)0?(!*)'{L`x=L2%x+0>1*}R

Explained
~'(!*1?)0?(!*)'{L`x=L2%x+0>1*}R
~                               # Zero Space Segment
 '(!*1?)0?(!*)'                 # A pattern string, matching any number of !'s with optionally a 1, optionally an uncaptured 0, and any number of !'s
               {             }  # An anonymous function, which takes two arguments. The last !'s and the optional 1 with the first 1's.
                L               # Get the length of the last !'s
                 `x=            # Set 'x' to equal it.
                    L2%         # Get the length of the first !'s with the optional 1, mod 2, giving us the boolean portion.
                       x+       # Add x
                         0>1*   # If the total is larger than 0, converted to a number. If there are any leading !'s, this will always be 1, otherwise, it will be the boolean of the left handside.
                              R # Replace the input string via the function matching the first pattern.

Try it online!

JavaX, 77 74 bytes
!7p{print(repeatMultiReplace3(args[0],splitAtSpace("0! 1 1! 1 !0 1 !1 0";}

Run with, e.g.: " java -jar x30.jar 1006862 '!0' "

C, 56 bytes
c=1;f(char*a){c=-c;*a&16?c+=1-*a-1[a],c&=2,c/=2:f(a+1);}

Hint: only the last two bits count.
The basic idea was to use least significant bit for storing result of factorial, and next bit for storing the negation, then xoring the two.
c=0;
for(;*a<34;a++)c^=2; // invert the 2nd bit at each negation
while(*a)c|=*a++; // '0' ends with bits 00, '1' and '!' ends with bits 01, so this OR will let the first bit to resut of factorial (LSB) and leave the 2nd bit unchanged
c=((c>>1)^c)&1; // apply the negation (2nd bit) on the factorial (1st bit)

But it makes our intentions too clear. First, we don't need a loop for the factorial, and we can allways take 2 char, the 2nd being eventually a NULL terminator will have neutral 00 end bits. This is much like the answer Mathematics is fact. Programming is not from Ahemone, but longer and less elegant so far.
c=0;
while(*a++<34)c^=2; // invert the 2nd bit at each negation
c|=*a,c|=*--a; // '0' and NULL ends with bits 00, '1' and '!' ends with bits 01, so this OR will let the first bit to resut of factorial (LSB) and leave the 2nd bit unchanged
c=((c>>1)^c)&1; // apply the negation (2nd bit) on the factorial (1st bit)

C isn't going to win anyway, so let's trade some golf for some obfuscation: replace the last expression with something else, assuming 2-complement: -x == (~x+1) and observe how the last two bits evolve
- ...00 -> ...11+1 -> ...00
- ...01 -> ...10+1 -> ...11
- ...10 -> ...01+1 -> ...10
- ...11 -> ...00+1 -> ...01

We see that the LSB is unchanged via c=-c, and the 2nd bit becomes the xor of last two bits. So we can just pick this second bit with c>>=1,c&=1 or c&=2,c/=2;
Of course, the bit inversion ~x is useless, just adding+1 has the same effect.
But there is a reason behind it:
what if we would replace the XOR flip/flop with negated op?
at each neg -...01 becomes ...11 et vice et versa
If we then subtract 1, we have either ...00 or ...10 at the end of the loop.
We are back to our original solution.
c=1;
while(*a++<34)c=-c;
c-=1;
c|=*a,c|=*--a;
c=-c;
c&=2,c/=2;

And let's see what happens if we add the factorial bit instead of ORing:
...00 becomes 00 or 01 or 10 in case of '0' , '0!'||'1' , '1!'.
...10 becomes 10 or 11 or 00.
So using + gives the same parity than | on last two bits, even if we accidentally add a bit twice du to '1!' case.
Now we just have to roll the final c-=c inside the loop, and replace the + by - for getting our obfuscated solution.
Ah and also use recursion to take a functional style disguise, but of course with non reentrant, ugly static variable assignment side effect, else there would be no "advantage" to code in C ;)

Haskell, 42
There must be a better way to do this...
f(h:t)|h=='!'=1-f t|h=='1'=1|t==[]=0|1<2=1

Pyth, 13 bytes
Code
s.v:z"\d!+""1

There may be a way to shave off a couple of bytes, but alas.
Explanation
s                # Cast to an integer (Python's int()).
 .v              # Evaluate (Python's eval()). This handles the negations.
   :             # Regex substitution. The following three expressions are its arguments.
    z            # Argument 1: what to replace in. This is equal to the (unevaluated) input string.
     "\d!+"      # Argument 2: what to replace. This is a regex that matches a number followed by one or more !'s.
           "1    # Argument 3: what to replace to. The string "1" (ending quote not needed in Pyth).

You can check it out here or run the test suite here. I have no earthly idea how to (or if one actually can) use the test suite feature to run tests as opposed to just evaluating a bunch of inputs at once, but if someone else knows, I'm all ears.

SmileBASIC, 63 bytes
INPUT S$WHILE"#">S$[0]N=!N
S$[0]="
WEND?(VAL(S$)||LEN(S$)>1)!=N

I don't think this is the best way...

Java (OpenJDK 8), 109 bytes
s->{int l=s.length(),n=(l>1?s.split("[01]")[0]:s).length();return l<2?s:(l-n<2&&s.charAt(n)<'1')==n%2>0?1:0;}

Try it online!

Cubically, 42 bytes
UD3(L2~:7=3)6+13=7!6{<0%6&}~:7=3?6L2-6=0%6

Try it online!
For some unknown reason it exits with an error when the digit is 1, but it outputs the correct result regardless. The following is my basic algorithm:

Start with 0
Invert for each !
If digit is 1 invert again and output (any number of trailing ! are irrelevant), ending the program
If there is a ! after the digit, invert
Output (any further ! are irrelevant)

And a more thorough explanation:
UD3                            Sets RIGHT to 33 and LEFT to 15

(L2~:7=3)6                     Flips TOP between 0 and 15 each time the input is ASCII 33 '!'

+13=7!6                        If the next character is NOT ASCII 15+33 '0':
       {<0%6&}                   output 1 if TOP is 15, 0 otherwise, then exit

~:7=3?6                        If the next character is ASCII 33 '!':
       L2                        flip TOP

-6=0%6                         Output 1 if TOP is 0, 0 otherwise

Each %6 can be shortened to % thanks to language updates.

Deorst, 42 bytes
o1:ER'[01]!+'gs''p@
'(!!)+'gst!0gso0@t!1gs

Try it online!
Regex based solution. Input must be quoted ('0')
How it works
Example input: '!!!0!!!!'
o1                     - Push '1';      STACK = ['!!!0!!!!', '1']
  :                    - Duplicate;     STACK = ['!!!0!!!!', '1', '1']
   ER                  - Reverse;       STACK = ['1', '1', '!!!0!!!!']
     '[01]!+'          - Push '[01]!+'; STACK = ['1', '1', '!!!0!!!!', '[01]!+']
             gs        - Regez replace; STACK = ['1', '!!!1']
               ''p     - Push '';       STACK = ['1', '!!!1', '']
                  @    - Swap;          STACK = ['1', '', '!!!1']
'(!!)+'                - Push '(!!)+';  STACK = ['1', '', '!!!1', '(!!)+']
       gs              - Regex replace; STACK = ['1', '!1']
         t!0           - Push '!0';     STACK = ['1', '!1', '!0']
            gs         - Regex replace; STACK = ['!1']
              o0       - Push '0';      STACK = ['!1', '0']
                @      - Swap;          STACK = ['0', '!1']
                 t!1   - Push '!1';     STACK = ['0', '!1', '!1']
                    gs - Regex replace; STACK = ['0']

Alternative, 27 bytes
t!1@t0$gs''p@
t!!gs'^\d'gcB

Try it online!
Requires input to be quoted ('0'). Based on Leo's Retina answer
How it works
t!1           - Push '!1';         STACK = ['!!!0!!!!', '!1']
   @          - Swap;              STACK = ['!1', '!!!0!!!!']
    t0$       - Push '0$';         STACK = ['!1', '!!!0!!!!', '0$']
       gs     - Regex replace;     STACK = ['!!!0!!!!']
         ''p  - Push empty string; STACK = ['!!!0!!!!', '']
            @ - Swap;              STACK = ['', '!!!0!!!!']
t!!           - Push '!!';         STACK = ['', '!!!0!!!!', '!!']
   gs         - Regex replace;     STACK = ['!0']
     '^\d'    - Push '^\d';        STACK = ['!0', '^\d']
          gcB - Count occurrences; STACK = [0]

Wren, 50 bytes
Fn.new{|a|(a[-1]=="0"?1:0)^a.trimEnd("!").count%2}

Try it online!

Julia 0.7, 28 bytes
~s=s[1]<'0'?!~s[2:end]:s>"0"

Try it online!
called with ~"!!0!", returns true or false
ungolfed:
function f(s)
    if s[1] == '!'
        return !f(s[2:end])
    else
        return s>"0"
    end
end

s>"0" is false only for s=="0"

Lexurgy, 47 bytes
f:
{\0,\1} \!+=>\1
e:
\!\!=>*
g:
\! {\0,\1}=>* {\1,\0}

Explanation
# strip factorials
f:
{\0,\1} \!+=>\1

# !!X = X
e:
\!\!=>*

# apply !
g:
\! {\0,\1}=>* {\1,\0}

YASEPL, 81 bytes
=1'=s®1=i`1!m¥i,1ſ""}7,exclamation,2!i+}2,s`2!1±m!3¥1,1®3}2,1,3!m$`3!3¥0,1®3+m%<

Could you provide a link to the language, or an online interpreter where people could test this?

Whoopsies! my mistake, I'll link it. I'm working on an online interpreter right now for it too! https://github.com/madeforlosers/YASEPL-esolang/


text:
Find the submatrix with the smallest mean, 2.0
You're given a n-by-m matrix of integers, where n,m > 3. Your task is to find the 3-by-3 sub-matrix that has the lowest mean, and output this value.
Rules and clarifications:

The integers will be non-negative
Optional input and output format
The output must be accurate up to at least 2 decimal poins (if it's non-integer)
The submatrices can be made up of arbitrary columns and rows

Test cases:
1   0   4   0   1   0
1   0   4   0   1   0
4   3   4   3   4   3
1   0   4   0   1   0

Minimum mean: 0   (We have chosen columns 2,4,6 and rows 1,2,4 (1-indexed)
-----------------------------
4    8    9    7
5   10    1    5
8    5    2    4
8    3    5   10
6    6    3    4

Minimum mean: 4.2222
-----------------------------
1   0   0   0   0
0   2   0   0   0
0   0   3   0   0
0   0   0   4   0
0   0   0   0   5

Minimum mean: 0.11111
-----------------------------
371   565   361   625   879   504   113   104
943   544   157   799   726   832   228   405
743   114   171   506   943   181   823   454
503   410   333   735   554   227   423   662
629   439   191   707    52   751   506   924

Minimum mean: 309.56

What makes this different from the first version of this challenge?

@KritixiLithos It uses the more general definition of "submatrix" where a submatrix is any matrix you can obtain from deleting any number of rows and columns from the original (so the remaining rows/columns don't have to be adjacent).

Mathematica, 77 50 bytes
±x_:=x~Subsets~{3}
Min[Mean/@Mean/@±#&/@±#]&

 is Mathematica's transposition operator (and is rendered as a superscript T in Mathematica).
This answer first defines a helper operator ± which returns all 3-element subsets of a list, and then evaluates to an unnamed function which uses this operator to solve the problem.
This is done by first computing all 3-element subsets of the matrix's rows. Then for each such subset, we transpose it and compute its 3-element subset of rows. This gives us all possible 3x3 submatrices (although they are transposed). We then compute the mean on all of them and find the overall minimum.

Jelly, 15 12 bytes
œc3S€Zµ⁺FṂ÷9

Try it online!
How it works
œc3S€Zµ⁺FṂ÷9  Main link. Argument: M (matrix)

œc3           Yield all combinations of 3 rows.
   S€         Map column-wise sum over the combinations.
     Z        Zip, transposing rows and columns.
      µ       Combine all links to the left into a chain.
       ⁺      Duplicate the chain, executing it twice.
        F     Flatten.
         Ṃ    Take the minimum.
          ÷9  Divide it by 9.

œc3S€µ⁺€FṂ÷9 is what I got... EDIT - hah and just like that you do the same :D

Ninja'd by 17 seconds. :P Thanks anyway. :)

I can't help but think there is a way to get rid of the 9 by dividing by 3 inside the repeated chain, but is it possible to get 3 as the right argument such that it's possible in 11?

Not in one byte, and that's what it would take to save one. You can't place 3 outside the chain (both because it's monadic and you'd have to group it to use ⁺), and inside the chain you either have to specify 3 explicitly or group it with ÷.

05AB1E, 21 16 bytes
2Fvyæ3ùO})ø}˜9/W

Try it online!
Explanation

For each row, get the sum of each ordered subset of size 3
Transpose the resulting matrix
For each row, get the sum of each ordered subset of size 3
Flatten the resulting matrix
Divide by 9
Get the minimum

Haskell, 90 bytes
import Data.List
t r=[a+b+c|[a,b,c]<-subsequences r]
s=(/9).minimum.(t=<<).transpose.map t

Try it online!

concatMap t can be shortened to (>>=t)

Bean, 198 bytes
Hexdump:
00000000 bc 81 bd a0 65 40 a0 5d dd a0 68 50 80 a0 77 20  ¼.½ e@ ]Ý hP. w
00000010 80 01 dd a0 66 25 3b 52 cc cb c0 50 84 a0 5d 20  ..Ý f%;RÌËÀP. ]
00000020 66 87 4c cc a0 68 8b 20 66 8c 25 3b cd d0 84 a0  f.LÌ h. f.%;ÍÐ.
00000030 5d 20 66 80 4e a0 66 81 4c d3 a0 65 a0 5d a0 68  ] f.N f.LÓ e ] h
00000040 4c a0 66 8c 25 3a 8b 25 3a 50 84 a0 5d 20 66 bd  L f.%:.%:P. ] f½
00000050 a0 6e 43 a5 39 a5 3a a5 3b 00 bd a0 5f 43 cf 20   nC¥9¥:¥;.½ _CÏ
00000060 6e 00 3d a0 69 20 12 b6 a7 36 a7 26 4d a0 69 80  n.= i .¶§6§&M i.
00000070 53 d0 80 a0 1f 20 80 45 a0 69 53 d0 80 a0 6e 20  SÐ. . .E iSÐ. n
00000080 80 8b 40 a0 6f a0 75 4c a0 6f 8b 53 d0 80 a0 5f  ..@ o uL o.SÐ. _
00000090 20 80 8b 40 a0 6f a0 74 4c a0 6f 8b 50 84 d0 84   ..@ o tL o.P.Ð.
000000a0 a0 77 20 75 20 74 4c d3 a0 65 a0 5f 50 80 a0 43   w u tLÓ e _P. C
000000b0 20 80 01 81 25 3b 4c d3 a0 65 20 6e 81 25 3b 26   ...%;LÓ e n.%;&
000000c0 4c a0 69 8e 25 42                                L i.%B
000000c6

Equivalent JavaScript:
// indices array increment function
var i=(a,l=$.length,j=2)=>++a[j]>=l+j-2?a[j]=j&&i(a,l,j-1)+1:a[j],
// row indices
    r=[0,1,2],
// column indices
    c=[...r],
// minimum sum
    m=Infinity;
do{
  do{
// calculate sum of current row/column indices and keep minimum
    m=Math.min(m,
      (r.reduce((s,y)=>s+c.reduce((s,x)=>s+$[y][x])))
    )
// until column indices loop
  }while(i(c,A.length)!=2)
// until row indices loop
}while(i(r)!=2)
// output mean
m/9

Try demo here

source: Stack Exchange

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

sort: votes


text:
You have a n day streak!
Write a program that keeps track of the number of consecutive days it has been run.
The day resets at midnight (the local time of the computer on which the program is run).
If there is a day that the program is not run, the counter is reset to zero.
Every time the program is run, the counter should go up by one day if it is the first time the program is run that day. This means that the first time the program is run, it should say, "You have a 1 day streak!"
The output of the program is in the form: You have a [streak_length] day streak!
You are allowed to store data externally or modify the source code––your choice.
This is code-golf, so shortest code in bytes wins!

Does it have to be local time? Or can it just be any time zone?

@EasterlyIrk, the day resets at midnight local time.

So it has to be local from where the computer is? Or at least waht timezone the comptuer is on. It can't use UTC regardless?

@EasterlyIrk, it has to be the time zone of the computer

Ah okay. Thanks for clarifying.

Is there specific behavior required if the program is run a second time on the same day?

Bash, 92, 90, 82 bytes
Golfed
grep `date -d-1day -I` h||>h
date -I>>h
echo You have a `uniq h|wc -l` day streak!

EDITS

Truncate file, instead of removing it, -8 bytes;
Replaced -daystart with ! -newermt to save 2 bytes.

How It Works !
Each time you launch it will append a line with the current date to the file h, e.g:
2017-02-03
2017-02-04
2017-02-05
2017-02-05

It will then use uniq to filter out duplicates (i.e. multiple launches, within the same day), and count the lines to get the streak length.
uniq h|wc -l

To reset a streak, it will grep for 'yesterday' in h, and truncate it if not found.
grep `date -d-1day -I` h||>h

Bash, 102 bytes
find ! -newerat 0-1day -delete
touch -a a
echo You have a $((1+(`stat -c %X-%Y a`)/86400)) day streak!

Warning, do not run in any folder you care about. It deletes any file not accessed in the last day in the working directory.
Uses a file a to store data, using the accessed/modified timestamps.

Goruby, 85 Bytes
Run with the interpreter flag -rdate.
c,t=0,Dae.y
op t.ts,?w
dw{c+=1;t=t.p;Fil.f t.ts}
s"You have a #{c} day streak!"

It works by storing a new file for each day on which it's invoked, then counts the number of consecutive files backwards to obtain the length of the streak. It doesn't ever delete files, so it will, eventually, after a very, very, very, very, very long time, fill your hard drive, a handful of bytes at a time.
Here's an ungolfed version of it:
streak, current_date = 0, Date.today
open(current_date.to_s, 'w')
while File.file?(current_date.to_s)
    streak += 1
    current_date = current_date.prev_day;
end
puts "You have a #{streak} day streak!"

Bash + coreutils, 120 97 bytes
read d n<f;c=`date -d0 +%s`;echo $c $[c>d?c>d+86399?n=1:++n:n]>f;echo You have a $n day streak!

The bash line above has 95 bytes in it.
There's a second file called f that just contains a single character:
0

(The program writes to f.)
So I think the total number of bytes should be scored as 97 (95 bytes for the contents of the bash file, 1 byte for the contents of the external file, and 1 byte because 1 file other than the program is used).  This is based on Counting bytes for multi-file programs.
Note: Thanks to @orlp for pointing out that an earlier answer to this that I posted was nonsense; I had misread the problem completely. (It was posted as a different answer, which I've deleted.)

Python 3, 213 bytes
import time
t=time.time()
try:
 r=open("a").read().split(":")
 open("a","w").write((str(int(r[0])+1)+":"+t)if(t>float(r[1])+86400)and(t<float(r[1])+172800)else("1:"+str(t)))
except:open("a","w").write("1:"+str(t))

PowerShell, 95 bytes
(date -f 'd')>>z
gc z|gu|%{$c=(1,++$c)[($d=date $_)-eq$n]
$n=$d+1D}
"You have a $c day streak!"

Try it online!
Explanation
I start by writing the current date (in short date format) out to a file (named z). >> works as usual; appends, but creates if it doesn't exist.
Next, I read the contents of the file line by line with Get-Content (gc), pipe through Get-Unique (gu) since there could be multiple entries from the same date, then pipe through ForEach-Object (%).
In the loop, I create a 2 element array with the value 0 in the first element, and the current value of $c (+1) in the second element. Using ++$c allows me to avoid wrapping something like $c+1 in parentheses.
Then I index into the two element array with a boolean comparison, which will get coalesced to 0 for $false or 1 for $true. Within the comparison, I'm assigning to $d a [datetime] object created from the date read from the current line in the file. That object gets compared to $n, which on the first run hasn't been assigned yet, so it will never match, and $c will be initialized to 1, since the value of the indexing is being assigned to $c.
Next, $n is populated with the next expected date, by adding 1 to the current datetime object. The key here is that 1D means it's a [decimal] literal. For some reason when you add integer numbers to a [datetime], it's interpreted as ticks, but when you add floating point numbers, they are interpreted as days. So this populates $n for the next iteration.
The effect is that the counter gets reset every time the current date doesn't match the "next" date (which is the previous date plus 1 day).
Finally the message is output.

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


text:
Detect a Symmetric polynomial
A symmetric polynomial is a polynomial which is unchanged under permutation of its variables.
In other words, a polynomial f(x,y) is symmetric if and only if f(x,y) = f(y,x); a polynomial g(x,y,z) is symmetric iff g(x,y,z) = g(x,z,y) = g(y,x,z) = etc.
For example, x^2+2xy+y^2, xy and x^3+x^2y+xy^2+y^3 are symmetric polynomials, where 2x+y and x^2+y are not.

The challenge
You will be given a polynomial, and your program should output truthy/falsy values, depending on if the given polynomial is a symmetric polynomial.
The input format is allowed in two ways. A string, and an array, like ["x^2","2xy","y^2"], where the polynomial is the sum of each elements.

Example
x^2+2xy+y^2 => true
xy => true
xy+yz+xz-3xyz => true
(x+y)(x-y) => false
2x+y => false
x^2+y => false
x+2y+3 => false

Specs
The operation has orders, just like in normal math. the order is like this:
() => ^ => * => +-

code-golf rules apply.
All the characters in the alphabet (a~z) are accepted as variables, everything else are numbers.
The given polynomial will have 2 or more variables.
Multiplication does not require the * operator, You only need to detect juxtaposition. (detecting by juxtaposition is not neccessary, use the better option)

Is a function a valid input?

@Pavel Write the input format on your answer and I'll consider if it's valid.

I'm fairly certain that, due to Richardson's Theorem, this problem is undecidable.

@Mego Mathematica would like to disagree.

@Mego Of course it's decidable; you can expand each permutation to a sum of terms of the form a^n1 b^n2 c^n3 etc.

Please specify the allowed inputs exactly and include test cases that cover the space well.

I don´t think its fair to ask someone to spend time writing an answer and then you decide if it´s valid. So I agree with @xnor. Also you indicated () => ^ => */ => +- but your examples do not show all these. I would have imagined we could expect - but not /. As you have mentioned () are we expected to handle in the format (-1+x)(-y-3)?

/ is not a valid operation for polynomials, unless applied on constants. Similarly, ^ can only be used to raised variables to integer powers.

Edited! Now you might be easier to golf!

Will there ever be a negative sign in the input? For the array format, I assume you mean something like '+'.join(inputlist), so for a negative one item will start with -, making x^2+-x+1?

@EasterlyIrk Yes.

This is still unclear. Can arbitrary degree polynomials be multiplied? Can operations be done to exponents? Where can minus signs appear?

What's the allowed syntax for variables? Also, how do we figure out which variables are involved? 2x+y is symmetrical if you exchange w and z!

Example (x+y)(x-y) => true seems wrong to me

@Leo Thanks! Didn't see that.

@ais513 I said two of the given variables. Where the variables are all the alphabetic letters given in the polynomial

"The input format is allowed in two ways" but they give two very different challenges, and a lot of the "spec" section is irrelevant if the second one if chosen.

Your comments allowing functions as input contradicts the spec. Also, one of the answers uses * for multiplication, which seems to be forbidden by the spec, but you haven't left a comment on that answer.

Maxima, 40 bytes
f(p):=listp(tpartpol(p,showratvars(p)));

Try It Online!
A function that takes a polynomial as input and returns true if it is symmetric  else returns false

Mathematica, 43 bytes
Last@SymmetricReduction[#,Variables@#]===0&

Unnamed function taking as input a polynomial in the given format (except that juxtaposed variables must be separated by a space) and returning True or False. Variables@# detects the variables appearing in the input (and thus the input can contain all kinds of weird variable names, not just single letters). SymmetricReduction returns an ordered pair of polynomials, where the first one is symmetric and the two sum to the original polynomial; therefore we can detect whether the input is symmetric by seeing whether the second polynomial is identically 0.

According to OP, you take input as a function, which could maybe let you golf that more.

TI-Basic, 46 bytes
Basically, how this works is the polynomial is entered into a (two-byte) variable type that is dynamically evaluated. Then, we swap the X and Y values enough times to see if the function is symmetric.
Prompt u
For(I,0,8
rand->X
Ans->W
rand->Y
u->Z
Y->X
W->Y
If u=W
End
Ans=9

I know the spec is shaky, so: this program takes input as a string, works with either juxtaposition or the * operator, and outputs 1 for true and 0 for false.

Sure, that should be enough to detect one.


text:
Find the submatrix with the smallest mean
You're given a n-by-m matrix of integers, where n,m > 3. Your task is to find the 3-by-3 sub-matrix that has the lowest mean, and output this value.
Rules and clarifications:

The integers will be non-negative
Optional input and output format
The output must be accurate up to at least 2 decimal poins (if it's non-integer)
The submatrices must be made up of consecutive rows and columns

Test cases:
35    1    6   26   19   24
 3   32    7   21   23   25
31    9    2   22   27   20
 8   28   33   17   10   15
30    5   34   12   14   16
 4   36   29   13   18   11

Minimum mean: 14

100    65     2    93
  3    11    31    89
 93    15    95    65
 77    96    72    34

Minimum mean: 46.111

1   1   1   1   1   1   1   1
1   1   1   1   1   1   1   1
1   1   1   1   1   1   1   1
1   1   1   1   1   1   1   1

Minimum mean: 1

4   0   0   5   4
4   5   8   4   1
1   4   9   3   1
0   0   1   3   9
0   3   2   4   8
4   9   5   9   6
1   8   7   2   7
2   1   3   7   9

Minimum mean: 2.2222

This is code-golf so the shortest code in each language wins. I encourage people to post answers in languages that are already used, even if it's not shorter than the first one.

It would also be interesting to have a challenge with not necessarily contiguous rows and columns

No, go ahead yourself :-)

Do you mean integers in the mathematical or data type sense, i.e., can we take a matrix of integral floats?

Mathematical sense. Is it one thing I've learned here, it is that you can make assumptions about data types in various languages...

Sweet, that saves a byte. Thanks for clarifying.

@Luis done. :-)

@StewieGriffin Nice title! I'll give it a try later if I have some time

Jelly, 11 9 bytes
+3\⁺€F÷9Ṃ

Saved 2 bytes thanks to @Dennis.
Try it online!
Explanation
+3\⁺€F÷9Ṃ  Main link. Input: 2d matrix
+3\        Reduce overlapping sublists of size 3 by addition
   ⁺€      Repeat previous except over each row
     F     Flatten
      ÷9   Divide by 9
        Ṃ  Minimum

Oh, >_< of course :D

I would be interested in an ungolfed version of jelly, since it has so many useful functions.

+3\⁺€F÷9Ṃ saves a couple of bytes.

@Dennis Wow, is that really processing +3\ first and the duplicate as +3\€? Did not expect that to happen

The parser is essentially stack-based; \ pops 3 and + and pushes the quicklink +3\, ⁺ pops the the quicklink and pushes two copies, then € pops the topmost copy and pushes a mapping version.

Octave, 30 bytes
@(A)min(mean(im2col(A,[3,3])))

Try it online!

Octave, 38 bytes
@(M)min(conv2(M,ones(3)/9,'valid')(:))

MATL, 13 9 bytes
3thYCYmX<

Port of @rahnema1's answer.
Try it online!
How it works
Consider input
[100 65  2 93;
   3 11 31 89;
  93 15 95 65;
  77 96 72 34]

as an example.
3th   % Push [3 3]
      % STACK: [3 3]
YC    % Input matrix implicitly. Convert 3x3 sliding blocks into columns
      % STACK: [100   3  65  11;
                  3  93  11  15;
                 93  77  15  96;
                 65  11   2  31;
                 11  15  31  95;
                 15  96  95  72;
                  2  31  93  89;
                 31  95  89  65;
                 95  72  65  34]
Ym    % Mean of each column
      % STACK: [46.1111 54.7778 51.7778 56.4444]
X<    % Minimum of vector. Display implicitly
      % STACK: [46.1111]

Mathematica, 37 35 bytes
Thanks @MartinEnder for 2 bytes!
Min@BlockMap[Mean@*Mean,#,{3,3},1]&

Explanation
Min@BlockMap[Mean@*Mean,#,{3,3},1]&
    BlockMap[                    ]&  (* BlockMap function *)
                        #            (* Divide the input *)
                          {3,3}      (* Into 3x3 matrices *)
                                1    (* With offset 1 *)
             Mean@*Mean              (* And apply the Mean function twice to
                                        each submatrix *)
Min                                  (* Find the minimum value *)

Very very slick!

Python 2, 93 81 80 79 bytes
f=lambda M:M[2:]and min(sum(sum(zip(*M[:3])[:3],()))/9,f(M[1:]),f(zip(*M)[1:]))

Try it online!
How it works
f is a recursive function that takes a list of tuples (or any other indexable 2D iterable that represents a matrix M) and recursively computes the minimum of the mean of the 3×3 submatrix in the upper left corner and f applied recursively to M without its first row and M without its first column.
f(M) does the following.

If M has less than three rows, M[2:] is an empty list, which f returns.
Note that, since n > 3 in the first run, the initial cannot cannot return an empty list.
If M has three rows or more, M[2:] is non-empty and thus truthy, so the code to the right of and gets executed, returning the minimum of the three following values.
min(sum(sum(zip(*M[:3])[:3],()))/9

M[:3] yields the first three rows of M, zip(*...) transposes rows and columns (yielding a list of tuples), sum(...,()) concatenates all tuples (this works because + is concatenation), and sum(...)/9 computes the mean of the resulting list of nine integers.
f(M[1:])

recursively applies f to M with its first row removed.
f(zip(*M)[1:])

transposes rows and columns, removes the first row of the result (so the first column of M, and recursively applies f to the result.

Note that the previously removed layer in a recursive call will always be a row, so testing if M has enough rows will always be sufficient..
Finally, one may expect that some recursive calls returning [] would be a problem. However, in Python 2, whenever n is a number and A is an iterable, the comparison n < A returns True, so computing the minimum of one or more numbers and one or more iterables will always return the lowest number.

J, 21 bytes
[:<./@,9%~3+/\3+/\"1]

Try it online!
The proper way to operate on subarrays in J is to use the third (_3) form of cut ;. where x (u;._3) y means to apply verb u on each full subarray of size x of array y. A solution using that requires only 1 more byte but will be much more efficient on larger arrays.
[:<./@,9%~3 3+/@,;._3]

Try it online!
Explanation
[:<./@,9%~3+/\3+/\"1]  Input: 2d array M
                    ]  Identity. Get M
                  "1   For each row
              3  \       For each overlapping sublist of size 3
               +/          Reduce by addition
          3  \         For each overlapping 2d array of height 3
           +/            Reduce by addition
       9%~             Divide by 9
[:    ,                Flatten it
  <./@                 Reduce by minimum

I like how the [] look like they’re matched, but they’re really not.

@Lynn Wait a second, that's not right. J is supposed to distract viewers with multiple unbalanced brackets. Should have used a [ or | :)

Jelly, 18 bytes
Missed the trick, as used by miles in their answer, of using an n-wise cumulative reduce of addition - the whole first line can be replaced with +3\ for 11.
ẆµL=3µÐfS€
ÇÇ€FṂ÷9

Try it online!
Traverses all contiguous sublists, filters to keep only those of length 3 and sums (which vectorises) then repeats for each resulting list, to get the sums of all 3 by 3 sub-matrices and finally flattens those into one list, takes the minimum and divides by 9 (the number of elements making this minimal sum).

I like the filtering sublists idea. Useful if that sublist size depended on a computed value.

Pyth, 19 bytes
chSsMsMs.:R3C.:R3Q9

A program that takes input of a list of lists and prints the result.
Test suite
How it works
[Explanation coming later]

Haskell, 87 bytes
import Data.List
t(a:b:c:d)=a+b+c:t(b:c:d);t _=[]
s=(/9).minimum.(t=<<).transpose.map t

Try it online!

Uiua, 14 bytes
÷9/↧♭⍥(/+⍉◫3)2

Try it!
÷9/↧♭⍥(/+⍉◫3)2    input: a matrix of shape RxC
      ⍥(      )2    repeat twice:
             ◫3      windows of 3; shape (R-2)x3xC
           ⍉        transpose (rotate axes once); shape 3xCx(R-2)
         /+          sum over the length-3 axis; shape Cx(R-2)
             ◫3      windows of 3; shape (C-2)x3x(R-2)
           ⍉        transpose (rotate axes once); shape 3x(R-2)x(C-2)
         /+          sum over the length-3 axis; shape (R-2)x(C-2)
÷9/↧♭               minimum of all elements divided by 9

Uiua, 14 bytes
÷9/↧♭/+/+◫-2△.

Try it!
÷9/↧♭/+/+◫-2△.    input: a matrix of shape RxC
           -2△     subtract 2 from the shape; [R-2, C-2]
          ◫   .    2D windows of that size; gives 3x3x(R-2)x(C-2) array
      /+/+          sum over the first two axes
÷9/↧♭              minimum of all elements divided by 9

JavaScript (ES6), 107 98 96 bytes
A function that computes the sums of triplets over the rows and then calls itself to do the same thing over the columns, keeping track of the minimum value M.
f=m=>m.map((r,y)=>r.map((v,x)=>M=(z[x<<9|y]=v+=r[x+1]+r[x+2])<M?v:M),z=[M=1/0])&&m[1]?f([z]):M/9

JS is a bit verbose for that kind of stuff and lacks a native zip() method. It took me quite a lot of time to save just a dozen bytes over a more naive approach. (Yet, a shorter method probably exists.)
Non-recursive version, 103 bytes
Saved 2 bytes with the help of Neil
m=>m.map((r,y)=>y>1?r.map((v,x)=>[..."12345678"].map(i=>v+=m[y-i%3][x+i/3|0])&&(M=v<M?v:M)):M=1/0)&&M/9

Test cases

f=m=>m.map((r,y)=>r.map((v,x)=>M=(z[x<<9|y]=v+=r[x+1]+r[x+2])<M?v:M),z=[M=1/0])&&m[1]?f([z]):M/9

console.log(f([
  [ 35,  1,  6, 26, 19, 24 ],
  [  3, 32,  7, 21, 23, 25 ],
  [ 31,  9,  2, 22, 27, 20 ],
  [  8, 28, 33, 17, 10, 15 ],
  [ 30,  5, 34, 12, 14, 16 ],
  [  4, 36, 29, 13, 18, 11 ]
]));

console.log(f([
  [ 100, 65,  2, 93 ],
  [   3, 11, 31, 89 ],
  [  93, 15, 95, 65 ],
  [  77, 96, 72, 34 ]
]));

console.log(f([
  [ 1, 1, 1, 1, 1, 1, 1, 1 ],
  [ 1, 1, 1, 1, 1, 1, 1, 1 ],
  [ 1, 1, 1, 1, 1, 1, 1, 1 ],
  [ 1, 1, 1, 1, 1, 1, 1, 1 ]
]));

console.log(f([
  [ 4, 0, 0, 5, 4 ],
  [ 4, 5, 8, 4, 1 ],
  [ 1, 4, 9, 3, 1 ],
  [ 0, 0, 1, 3, 9 ],
  [ 0, 3, 2, 4, 8 ],
  [ 4, 9, 5, 9, 6 ],
  [ 1, 8, 7, 2, 7 ],
  [ 2, 1, 3, 7, 9 ]
]));

I'm somewhat interested in your so-called naïve approach, since the best I could do with a reasonably pure approach was 113 bytes: (a,b=a.map(g=a=>a.slice(2).map((e,i)=>a[i]+a[i+1]+e)))=>eval(\Math.min(${b[0].map((_,i)=>g(b.map(a=>a[i])))})`)/9`

@Neil I think it was something close to m=>m.map((r,y)=>r.map((v,x)=>[..."12345678"].map(i=>v+=(m[y+i/3|0]||[])[x+i%3])&&(M=v<M?v:M)),M=1/0)&&M/9, although I think my first attempt was actually larger than that.

Nice, although I was able to shave off a byte: m=>m.map((r,y)=>y>1&&r.map((v,x)=>[..."12345678"].map(i=>v+=m[y-i%3][x+i/3|0])&&(M=v<M?v:M)),M=1/0)&&M/9.

@Neil Cool. This allows to save one more byte with m=>m.map((r,y)=>y>1?r.map((v,x)=>[..."12345678"].map(i=>v+=m[y-i%3][x+i/3|0])&&(M=v<M?v:M)):M=1/0)&&M/9

Python 3, 111 103 bytes
lambda m,r=range:min(sum(sum(m[y+i][x:x+3])for i in r(3))/9for x in r(len(m[0])-3)for y in r(len(m)-3))

Try it online!

Python 2, 96 bytes
h=lambda a:[map(sum,zip(*s))for s in zip(a,a[1:],a[2:])]
lambda a:min(map(min,h(zip(*h(a)))))/9.

Test cases at Repl.it
An unnamed function taking a list of lists, a - the rows of the matrix.
The helper function h zips through three adjacent slices, and maps the sum function across the transpose, zip(*s), of each. This results in summing all height three slices of single columns.
The unnamed function calls the helper function, transposes and calls the helper function again on the result, then finds the minimum of each and the minimum of the result, which it then divides by 9. to yield the average.

05AB1E, 21 16 bytes
2FvyŒ3ùO})ø}˜9/W

Try it online!
Explanation
2F         }       # 2 times do:
  v     }          # for each row in the matrix
   yŒ3ù            # get all sublists of size 3
       O           # reduce by addition
         )ø        # transpose matrix
            ˜      # flatten the matrix to a list
             9/    # divide each by 9
               W   # get the minimum

K (ngn/k), 21 20 17 bytes
Port of @miles' J solution
-3 thanks to @Bubbler
1%9%&//2(+3+/':)/

Try it online!

"Sum of 3-windows in 2D" can be done with 2(+3+/':)/, and "divide by 9" can be done with 1%9% (works fine when the answer is zero).

R, 78 bytes
\(m,l=nrow(m))min(mapply(\(x,y)mean(m[x-0:2,y-0:2]),3:l,y=rep(3:ncol(m),l-2)))

Try it online!

Vyxal, 10 bytes
2(3lvṁ∩)fg

Try it Online!
2(     )   # Twice
  3l       # Take sliding windows of length 3
    vṁ     # Take the mean of each window
      ∩    # Transpose
        fg # Flatten and take the minimum

Uiua, 17 16 bytes
÷9/↧♭≡≡(/+♭)◫3_3

Try it!
-1 thanks to alephalpha
÷9/↧♭≡≡(/+♭)◫3_3
            ◫3_3  # 3x3 windows
     ≡≡(   )      # map function to each matrix in 4D array
          ♭       # deshape
        /+        # sum
    ♭             # deshape
  /↧              # minimum
÷9                # divide by 9

-1 byte: ÷9/↧♭≑≊(/+♭)◫3_3

@alephalpha Thanks!

Nekomata, 10 bytes
2ᵑ{q3Lµ}aṁ

Attempt This Online!
2ᵑ{q3Lµ}aṁ
2ᵑ{    }        Apply the following function twice
   q                Choose a contiguous subsequence
    3L              of length 3
      µ             Take its mean
        aṁ      Minimum of all possible results

Scala, 142 bytes
Golfed version. Try it online!
M=>if(M.size<3||M(0).size<3)Double.MaxValue else List((0 to 2).flatMap(i=>(0 to 2).map(j=>M(i)(j))).sum/9.0,f(M.tail),f(M.transpose.tail)).min

Ungolfed version. Try it online!
object Main {

  def f(M: List[List[Int]]): Double = {
    if (M.length < 3 || M.head.length < 3) Double.MaxValue
    else {
      val minSubMatrix = (0 until 3).flatMap(i => (0 until 3).map(j => M(i)(j))).sum / 9.0
      List(minSubMatrix, f(M.tail), f(M.transpose.tail)).min
    }
  }

  def main(args: Array[String]): Unit = {
    val M = List(List(100, 65, 2, 93), List(3, 11, 31, 89), List(93, 15, 95, 65), List(77, 96, 72, 34))
    println(f(M))
  }
}

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