text:
Alice's First Code Review
Alice is an intern in a company that uses Brainfuck as a primary language for both client-side and server-side development. Alice just has written her first code and she's a bit nervous as she's getting ready for her first code review.
Alice wants to make her code properly formatted and look nice, but she has no time to read the 328-pages company code style guide, so she decided to format it as a perfect square. Alas, code length may be insufficient to form a square, so she decided to leave a rectangular gap in the middle. Gaps must be perfectly centered and as close to square as possible.
Examples
++++++         +++++         +++++         +++++         +++++         +++++
++++++         +++++         +++++         +  ++         ++ ++         +++++
++++++         ++ ++         +   +         +  ++         +   +
++++++         +++++         +++++         +++++         ++ ++         +++++
++++++         +++++         +++++         +++++         +++++         +++++

Perfect         OK         Acceptable   Unacceptable    No way!        Nope.

Write a program or a function to help Alice.
Given Alice's code as an input string, output properly formatted code as described below if possible.
If formatting is impossible, output crying emoji :~(.
This is code-golf so answers are scored in bytes with fewer bytes being the aim.
Constraints

Your program or function should take a single string as an input and output one or more lines of text (or return multi-line string or array of strings, if you implement a function).
Input string may contain any ASCII characters including whitespaces.
All whitespaces in input must be ignored. They shouldn't count for code length and they shouldn't be used in output.
Input string contains at least one non-whitespace character.
Formatted code must have the same non-whitespace characters in the same order as in input code.
Formatted code must be a perfect square, i.e. all lines should have same length, and number of lines should be equal to the lines' length.
Formatted code may contain a gap in the middle.
Only space characters (ASCII code 32) may be used in the gap.
Gap (if present) must be rectangular.
Every line of formatted code must contain at least one non-whitespace character, i.e. gap width must be strictly less than square width (gap 5x1 is unacceptable for 5x5 square).
Gap should be horizontal, i.e. gap width should be greater than or equal to gap height.
Gap must be perfectly centered.
Consequently gap width and height parity should be the same as square width parity (for example for 5x5 square gap may be 1x1, 3x1, or 3x3).
If possible, output square with no gap at all.
In case of multiple solutions, choose one with gap closest to square, i.e. difference between gap width and gap height is minimal (for example: gap 10x10 is more preferable than 8x6, and 8x6 is more preferable than 6x2).
If still there's a tie, choose solution with minimal gap area (for example gap 2x2 is more preferable than 4x4).
If it is impossible to format code at all, output :~(.
Newline after last line is optional.
[New] You may safely assume that any character with code below 33 is a white-space. I hope it will help you for golfing.

Tests
Input           Output      Code length     Comment

+++++++++       +++                   9     Alice is lucky,
                +++                         her code perfectly fits a square.
                +++

++++++++        +++                   8     Though code length isn't enough for a square,
                + +                         a small gap fixes it.
                +++

++++++          :~(                   6     No luck, code cannot be formatted.

Hello,          Hell                 12     Input may contain any ASCII characters,
World!          o  ,                        but whitespaces in input should be ignored.
                W  o
                rld!

+++++ + +++++   +++++                22     Gap is not required to be a square,
+++++ + +++++   +++++                       it can be a rectangle.
                +   +
                +++++
                +++++

+++ + +++       ++++++++             28     There exists another solution:
+++ + +++       +      +                    6x6 square with 4x2 gap,
+++ + +++       +      +                    but in Alice's opinion square gap
+++ + +++       +      +                    makes code more readable.
                +      +
                +      +
                +      +
                ++++++++

Tricky tests
This must be    Thism                24     7x7 with 5x5 gap looks good,
5x5 with 1x1    ustbe                       but 5x5 with 1x1 gap is better,
gap.            5x 5w                       because gap area is smaller.
                ith1x
                1gap.

+++ +++ +++     :~(                  18     In case you tried 5x5 square
+++ +++ +++                                 with 7x1 gap ;)

Resources
To save space you can find sample code and extra test cases on
tio.run
[New] You may take a look at the table of accepted solutions for inputs upto 100 characters. I swapped width and height because it seems to look more intuituve.
Inspired by: A square of text
Changes

Added 2 tests, fixed bug in sample code.
Added table of solutions upto 100, added white-space clarification.

"If formatting is impossible, output crying emoji" - unnecessary fluff, allowing any output would be better IMO.

@JonathanAllan, even better would be guaranteeing that formatting is always possible. Boo-urns to input validation!

@Jonathan Allan, any output is too broad and may be vulnerable to loopholes. I thought about letting to use any falsy values, but it would make output inconsistent between different languages. I also considered using empty string, but it would make running test cases less visual. I also thought about some other messages like "Error", but decided to make it even shorter to support Brainfuck developers all over the world.

@Shaggy, this is not just input validation. To find that there is no solution is important part of the puzzle: you need to find out when to stop searching.

@DaniilTutubalin it is very common to assure only certain inputs will be given here. Allowing any output on such "invalid" inputs is also a common approach, and avoids boilerplate code which has nothing to do with the core of the challenge. If you really want something unvarying as output in such cases I'd suggest the input itself. I am not suggesting you change it now though ...also this is a very nice challenge!

Is there a maximum width we should support? Like 100x100 as max? Or can the width/height be as wide as the integer-size of the used language in theory?

C (gcc), 354 348 bytes
-6 thanks to ceilingcat
h,i,j,k,l,m;g,v,w,d;f(char*s){for(j=l=0;i=s[j];d=g=l+=!isspace(s[j++]));for(;i++<l;)if(h=i*i-l)for(j=0;j<i-++j;h>0&h%k<1&k<=m&m<i&m+~k&m-k<g|m==k+g&(d-2*v)*(d-2*w)>h?w=i-m>>1,v=j,g=m-k,d=i:0)k=i-2*j,m=h/k;else g=0,v=w=d=i;if(g-l)for(i=j=1;*s;j+=!i++,k=0,i%=~d)for(putchar(i?v>=j|j>d-v|w>=i|i>d-w?k=1,*s:32:10);k;k*=isspace(*++s));else puts(":~(");}

Try it online!

I think a tab is "white-space", although I asked for clarification on what exactly counts.

I have updated post. You may replace isspace(x) with x<33.

Stax, 80 bytes
ö╦Çc▀╕╡ëé╓]µiÖ9♪`W|╣▐↑╖D┘↕♫╗╔äƒ■úφ■U`ÿ⌠%é┴☼vAú)☺@Ä±◄¬¶¢(îÉ┼6⌠D~♀└lfæA.I@º╤∟òîü╦(

Run and debug it
How does it work?

Consider all squares with rectangle cutouts.
Filter out program shapes that aren't the right size.
Filter out program shapes that don't have matching parity.
Filter out program shapes where the cutout is too big for the square.
If none are found, output failure and quit.
Find the shape that minimizes "rectangularity" of cutout, then by cutout size.
Replace each character in the program shape with the corresponding character in the program input.

Unpacked, ungolfed, and commented it looks like this.
                input e.g. "big socks"
L$j$            remove whitespace from input e.g. "bigsocks"
cr{~F           copy push each remaining character to input stack in reverse order
%c              get length L of flattened input and copy e.g. 8
^r3:$           create triples [a,b,c] in range 0..n e.g. [[0,0,0], [0,0,1], ... [8,8,8]]
{E*NsJ+n=f      filter triples: require a*a-b*c=L e.g. [[3,1,1], [4,1,8], ...]
{ET>f           filter triples: a>b and a>c e.g. [[3,1,1]]
{{2%m:u_0#+f    filter triples: a%2=b%2=c%2 or b*c=0 e.g. [[3,1,1]]
":~("s|c        if there are no triples, output ":~(" and terminate
{D:s_D:*\oh     sort by [abs(b-c),b*c]; keep the first e.g. [3,1,1]
X               store the chosen triple in the X register
E               explode onto stack e.g. 3, 1, 1
z)]*            using last two values, make a rectangle of zeroes e.g. 3, [[0]]
~               this will be the cutout, push it to the input stack
c'X*]*          make a square of "X" e.g. ["XXX", "XXX", "XXX"]
xEd-h           given the dimensions in x register, calculate the centered cutout coordinates
xEsd-h          e.g. ["XXX", "XXX", "XXX"], 1, 1
,||             embed the cutout grid at the specified coordinates e.g. ["XXX", "X X", "XXX"]
m'X{,]}R        for each line, regex replace "X" with a character from the input stack

Run this one

Could you please provide unpacked version as well?

@DaniilTutubalin I've added an explanation.

Thank you! Well done!

JavaScript (ES6),  284 ... 274  270 bytes
Saved 4 bytes thanks to @Shaggy
Returns an array of strings.
s=>(s=s.replace(/\s/g,''),n=s.length,o=[':~('],W=d=g=(x,w=0,h=p=0)=>4*~-x>n?o:h>w?++w>x-2?g(-~x):g(x,w):g(x,w,h+1,o=n-x*x+w*h|(w^x|h^x)&!!(w|h)|w-h>d|w-h==d&w>W?o:[...Array(x)].map((_,Y,a)=>a.map((_,X)=>2*X>=x-w&2*X<x+w&2*Y>=x-h&2*Y<x+h?' ':s[p++]).join``,d=w-h,W=w)))``

Try it online!

Well done! Though try this test: This must be 5x5 with 1x1 gap. (24 characters)

@DaniilTutubalin That's why I deleted my initial post (and suggested such a test case), but this is now supported correctly.

Can't [\s\n] just be \s?

@Shaggy ++w>x-2 is actually w++>=x-2 (w++>=x would fail for $42$, for instance). But \s does save 4 bytes. Thanks. :)

Charcoal, 120 bytes
≔Ｅ⮌⪫⪪Ｓ ωιθ≔⁰ηＦ⊕Ｌθ¿⁼Ｌθ×ιι«≔ιη≔ιζ≔ιε»Ｆ⊘ιＦ⊕κＦ∧⁼Ｌθ⁻×ιι×⁻ι⊗⊕κ⁻ι⊗⊕λ∨¬η›⁻ζε⁻κλ«≔ιη≔⊕κζ≔⊕λε»¿η«ＵＯη#ＪεζＦ›η⊗ζＵＯ⁻η⊗ε⁻η⊗ζψＵＭＫＡ⊟θ»:~(

Try it online! Link is to verbose version of code. Explanation:
≔Ｅ⮌⪫⪪Ｓ ωιθ

Strip the spaces from the input, then reverse it and split it into characters, so that we can loop over the characters more easily later.
≔⁰η

Start with a size of zero, indicating no result found (yet).
Ｆ⊕Ｌθ

Check all side lengths up to the length of the string. (Throwing a division in would of course make the code faster.)
¿⁼Ｌθ×ιι«≔ιη≔ιζ≔ιε»

If the result turns out to be a perfect square then save the square size and also set that as the border size.
Ｆ⊘ιＦ⊕κ

Loop over the possible border heights and widths (the border width is no greater than the border height, making the gap height no greater than the gap width.)
Ｆ∧⁼Ｌθ⁻×ιι×⁻ι⊗⊕κ⁻ι⊗⊕λ∨¬η›⁻ζε⁻κλ«≔ιη≔⊕κζ≔⊕λε»

If the size of the border is the desired length and we don't have a solution yet or it's not as square as this solution then update the solution with this square and border sizes.
¿η«

If we have a solution...
ＵＯη#

Draw a rectangle of an arbitrary character of the given square size.
ＪεζＦ›η⊗ζＵＯ⁻η⊗ε⁻η⊗ζψ

If the border is small enough to leave a gap then erase the gap. (The drawing command will draw upwards and leftwards for negative values and doesn't like zero values at all.)
ＵＭＫＡ⊟θ

Replace all (remaining) characters with characters from the input.
»:~(

Otherwise output :~(.

Jelly, 91 85 bytes
ÆDżU$<Ạa;ḂEɗʋƇṢƑƇ;€€
ḟ⁶ḟ⁷;©⁶L’²<¥Ðḟ²_ç;,`Wɗ¹?⁸ʋ€$Ẏạ/ÞḢµIH+""Rp"/ḟ@/ŒṬ€ĖP€Sị®Yµ“:~(”¹?

Try it online!
A monadic link that takes the input string as its argument and returns a string either with the formatted output or :~(.

Python 2, 287 281 279 bytes
c=list("".join(input().split()))
l=len(c)
p=[]
for q in range(l*l):x=q%l;y=q/l;s=(l+x*y)**.5;p+=[(abs(x-y)/2,int(s),-x)]*(s%1==0)*(x<s-1>y>=s%2==x%2==y%2or x<1)
if p:d,s,x=min(p);b=(s+x)/2;Y=0;exec"c[b:b]=' '*-x*(b+d<=Y<s-b-d);print''.join(c[:s]);c=c[s:];Y+=1;"*s
else:print':~('

Try it online!
Uses Python's lexicographic list comparison by using the same values for both choosing a solution and printing it. I'm pretty sure 10 4 2 or so bytes can still be shaved off.
Explanation
c=list("".join(input().split()))
l=len(c)
p=[]

Remove whitespaces by splitting with whitespace and joining with "", then convert input to a list for later. Also initialize l to length of actual code and p to list of valid possibilities.
for q in range(l*l):x=q%l;y=q/l;s=(l+x*y)**.5;

Loop through all possibilities of gap sizes from 0*0 to l*l. Compute the edge length of the square with l code chars and x*y spaces as s.
p+=[(abs(x-y)/2,int(s),-x)]*(s%1==0)*(x<s-1>y>=s%2==x%2==y%2or x<1)

Check if the following conditions match:

s % 1 == 0, i.e. a perfect square would be formed
x < s-1 > y, i.e. x and y are at most s-2 and fit inside the square
s % 2 == x % 2 == y % 2, i.e. both x and y match the edge's parity and can be centered
but if x < 1, i.e. x == 0, ignore all but the perfect square requirement

If the conditions matched, add the following items in a tuple to p to find the optimal one:

abs(x-y)/2; first find the minimal difference of x and y to get the most square gap. This is always even so we divide by 2.
int(s); next find the minimal side length. Since s is an integer and increases as the gap area x*y, this sorts by gap area.
-x; next find the maximal width to prefer horizontal gaps. This comes after the area due to how it was developed, but the area is the same for x*y and y*x so it works.

if p:d,s,x=min(p);b=(s+x)/2;Y=0

If we found any valid layouts, find the optimal one as described above. Compute the horizontal border b and initialize the line number Y to 0.
exec"c[b:b]=' '*-x*(b+d<=Y<s-b-d);print''.join(c[:s]);c=c[s:];Y+=1;"*s

If the line number Y is inside the gap (the vertical border is b+d, with d from the tuple), add the gap width's worth of spaces after the horizontal border in c. (The modification of c is why we need it to be a list.) Then print a line of the square and remove it from c. Repeat s times, incrementing the line number.
else:print':~('

If no layouts were found, fail.

Well done! Alas, it doesn't work well with multiline input. Also it seems it doesn't support rectangular gaps (see test with length 22).

@DaniilTutubalin You need to supply input as a Python string, multiline would be literally "some\ntext" as delimiters. (input() evaluates a line of input as Python code.) If this isn't acceptable, please let me know. Length 22 also works for me.

ouch, sorry. Probably I did something wrong with input.

Pyth, 99 98 bytes
#JscQ)=QshKh|oaFtNSf!|%hT1&eT|t{%R2TgeStThT+L@+lJ*Fd2^UJ2
":~("Am/-Qd2tKW=W}~hZrG-QGJj*eKdcJ]H<~>J

Try it online!
This uses the same algorithm as my Python answer, but many details are significantly changed to be shorter in Pyth.
Pyth shows its age here, since it hasn't been updated in ages and only uses the printable ASCII characters (for code, not data), wasting a lot of space.
Interestingly, if Pyth used the same kind of base-256 packing as Stax, this program could be ⌈98 log256 95⌉ = 81 bytes long, right next to Stax (80 bytes) and Jelly (85 bytes). I think this nicely shows how close golfing languages are even with their drastically different paradigms.
Explanation (only slightly less unreadable than the code)
# wraps everything in a while True: that suppresses the message and quits on error.
JscQ) chops input (Q) at whitespace, sums the parts together and saves the result in J.
^UJ2 makes the list of indices (U) of J and takes its 2nd Cartesian power (^), resulting in all pairs [h,w] with 0<=h<len(J) and 0<=w<len(J).
+L@+lJ*Fd: for all (L) such pairs d, adds (+) the square root (@…2) of (the length of J plus (+) the product (*F) of the pair d) to the left side of the pair, creating a triplet [side length, gap height, gap width].
f!|%hT1&eT|t{%R2TgeStThT: filter for triplets T where

neither of (!|):

side length (hT) modulo 1 (%…1) is nonzero
both of (&):

gap height (eT) is nonzero
either of (|):

each number in the triplet (R) modulo 2 (%…2), with duplicates ({) and the first unique (t) removed, is nonempty
the larger (eS) of gap height and gap width (tT) is greater-or-equal than side length (hT)

S sorts the triplets lexicographically (by side length, then by gap height). oaFtN then orders the triplets by the absolute difference between gap height and gap width (tN).
At this point, if we have no valid solutions, | evaluates its second argument, \n":~(", which prints and returns :~(. h takes the optimal solution (or ":" if none) and it is saved in K. Then h takes its side length (or ":" if none), s casts it to integer (or fails and quits if none) and it is saved (=) in Q.
Each of (m) [gap height, gap width] (tK) is then subtracted (-) from side length (Q) and the result divided by 2 (/…2). The results are Assigned to G and H.
Finally, we enter a While loop. Z starts at 0 and each iteration we increment it but use the old value (~hZ, think Z++ in C).

If (W) the old value is in ({) the range G to (side length - G) (-QG), assign (=) to J the following: chop J at position (]) H and join the halves with gap width eK times (*) a space (d). If the value was not in the range, just return J. If this result is empty, stop the loop.
Remove (>) the first Q characters from J and assign (~) the result to J. From the old value of J, take (<) the first Q characters and print them.

Finally, the # loop starts again, errors and quits because cQ) with Q containing a number is invalid.

To be honest, I like printable code more. Though binary code takes less space, it looks much less apealing (and i would prefer to see it as hex dump) and usually comparison is done between solutions in the same language. So shorter packed Stax solution compared to another packed Stax solution, will still be shorter after unpacking both. Packing makes sense for overall Kolmogorov complexity only.

@DaniilTutubalin Regardless of how comparison is usually done, it's still nice to beat another golflang ;)

05AB1E, 95 89 bytes
…
    мDg©Ý3ãʒćnsP-®Q}ʒć‹P}ʒÈËyß_~}Dg_i…:~(ëΣ¦DÆÄsP‚}н©¤_iнë¬Ućα¤®Ås<иs;R`X*+šXnª£®θð×ýX}ô»

A few bytes here and there can definitely be golfed.
The first three steps of the program are inspired by @recursive's Stax answer, so make sure to upvote him!
Try it online or verify all test cases.
Explanation:
Step 1: Remove all white-spaces:
…
                   # Push string " \n\t"
    м              # And remove those from the (implicit) input-string

Step 2: Create all possible triplets \$[a,b,c]\$, where \$a\$ is the dimension of the resulting \$a×a\$ square, and \$b×c\$ is the size of the gap. We do this by creating all possible triplets using integers in the range \$[0,a]\$. And then we filter these where all of the following is truthy for the triplet:

\$a^2-b×c=L\$, where \$L\$ is the length of the string
\$(a\gt b)\text{ and }(a\gt c)\$
\$(a\pmod2=b\pmod2=c\pmod2)\text{ or }(min(a,b)\neq0)\$

For example: \$L=28\$ will result in the triplets [[6,2,4],[6,4,2],[8,6,6]].
Dg                 # Get the length of this string (without popping by duplicating first)
  ©                # Store this length in variable `®` (without popping)
   Ý               # Create a list in the range [0,®]
    3ã             # Create all possible triplets by repeating the cartesian product 3 times
ʒ                  # Filter these triplets by:
 ć                 #  Extract head; pop and push remainder-list and head to the stack
  n                #  Square the head
   sP-             #  Take the product of the remainder, and subtract it from the squared head
      ®Q           #  And check if it's equal to the string length in variable `®`
}ʒ                 # Filter the remaining triplets further by:
  ć‹P              #  Where the first integer is larger than the other two
}ʒ      }          # And filter it a third time by:
  ÈË               #  Where all three are either odd or even
       ~           #  Or
    yß_            #  It does not contain any 0s

Step 3: Check if we still have any triplets left. If not, output ":~("; if we do, determine which one to use by sorting and only leaving the first. We do this by sorting the tuples \$(abs(b-c),b×c)\$.
For example: the triplets [[6,2,4],[6,4,2],[8,6,6]] will be sorted to [[8,6,6],[6,2,4],[6,4,2]], after which only [8,6,6] remains.
Dg_i               # If this completely filtered list is now empty:
    …:~(           #  Push string ":~("
ë                  # Else:
 Σ                 #  Sort the triplets by:
  ¦                #   Remove the first character
   DÆÄ             #   Get the absolute difference between the remaining two integers
   sP‚             #   And pair it with the product of the remaining two integers
      }н           #  After we're done sorting: only leave the first triplet

Step 4: Create a list on how we should split the string to insert the spaces. This is done like this:
Given \$[a,b,c]\$, create a list with:

As first item: \$\frac{a-b}{2}×a + \frac{a-c}{2}\$
As middle \$b-1\$ items: \${a-c}\$
As last item: \$a^2\$

For example: triplet [7,3,5] will result in the list [15,2,2,35].
©                  #  Store this triplet in variable `®` (without popping)
 ¤_i               #  If the last value is 0 (which means the string is a square without gap):
    н              #   Simply keep the first value of the triplet
   ë               #  Else:
    ¬U             #   Store the first value in variable `X` (without popping)
      ć            #   Extract the first value; pop and push remainder-list and head to the stack
       α           #   Get the absolute difference of this head with the other two values
        ¤          #   Push the last value (without popping the pair itself)
         ®Ås<и     #   And repeat it the middle element or `®` minus 1 amount of times
       s           #   Swap to get the difference pair again
        ;          #   Halve both values
         R`        #   Push them reversed to the stack
           X*      #   Multiple the first value by `X`
             +     #   And then add it to the second value
              š    #   Prepend this in front of the repeated list
               Xnª #   And also append the square of `X`

Step 5: And finally we split the string based on this list, join it back together with \$c\$ amount of spaces, split it into parts of size \$c\$, and join those together by newlines. For example:
String "Alongtesttoseeifitworksasintended." split according to list [15,2,2,35] will result in: ["Alongtesttoseei","fi","tw","orksasintended."]. This is then joined by \$c=5\$ amount of spaces to "Alongtesttoseei     fi     tw     orksasintended.". And then split into parts of size \$a=7\$ to this: ["Alongte","sttosee","i     f","i     t","w     o","rksasin","tended."]. Which is then joined by newlines to output.
    £              #   Then split the string into parts based on this list
     ®θð×          #   Push a string consisting of the last value of `®` amount of spaces
         ý         #   Join the list of substrings by this
          X        #   Push variable `X`
   }ô              #  After the if-else: split the string into parts of that size
     »             #  And then join these by newlines
                   # (after which the top of the stack is output implicitly as result)


text:
Tesco's Burger Relish Best Before End date number

Given a date between 2010-01-01 and 2099-12-31 as three integers (please state your order if not [year,month,day]), answer with Tesco's corresponding five-digit Burger Relish Best Before End date number.
The format is simply the last two digits of the year, followed immediately by three digits representing the day of that year, between 000 and 365.
Cookies if you can do this using only numeric operations and comparisons. You may put such code separately from your golfed code, if it isn't also the shortest code you have.
Test cases
[year,month,day] → BRBBED:
[2010,1,1] → 10000
[2019,7,5] → 19185
[2020,5,20] → 20140
[2096,12,31] → 96365
[2099,12,31] → 99364

"Cookies if you can do this using only numeric operations." Good luck handling dates without e.g. comparison operators...

@EriktheOutgolfer Addressed.

Closely related

@Arnauld But that's a do-X-without-Y with an input domain that makes things harder.

Oh indeed. I missed that rule in the linked challenge.

Huh, I always wondered how that number translates to a date

Must our output use 0-indexing for the days of the year or may we choose 1-indexing instead?

@Adám It's on many other products; not just this one! Why is the title so specific?

@wizzwizz4 It was in the news recently: https://www.independent.co.uk/life-style/tesco-expiry-date-20140-julian-calendar-relish-twitter-a8973931.html : The supermarket’s unusual labelling choice came to light on Sunday after customer Matthew Stock shared a photograph on Twitter featuring a burger relish that had an expiration date of “20140” and asked Tesco to “please explain” it.

@seventyeightist Oh. I thought it was one of those elitist things, and felt so pleased with myself for figuring it out after only 5 weeks and setting my laptop's clock to show the date.

@Shaggy It has to be that silly format.

PowerShell, 74 70 67 bytes
param($y,$m,$d)-join"$y 00$((date "$m/$d/$y"|% d*r)-1)"[2,3+-3..-1]

Try it online!
Exactly what it says on the tin (but formatted weirdly). Takes in $year, $month, $day, plucks out the last two digits of the $year, gets a .NET datetime object of the specified day, then gets the dayofyear (with |% d*r) thereof. Subtracts one to make it zero-indexed, then uses string formatting and slicing to make it three-padded (e.g., 000 instead of 0), and then -joins it all together into a single string with implicit output.
Note this is culture dependent due to the datetime formatting. This works in en-us, which is what TIO also uses.
-3 bytes thanks to mazzy

-1 byte

@mazzy Don't need the parens in -join(...) since it's a single string slice, then. So that's another -2. Thanks!

C# (Visual C# Interactive Compiler), 88 78 73 71 59 57 bytes
a=>a[0]%100*1000+new DateTime(a[0],a[1],a[2]).DayOfYear-1

Try it online!

(y,m,d)=>y%100*1000+new DateTime(y,m,d).DayOfYear-1 for 51 bytes?

@EdT well it depends on your interpretation of the rules, I was aware of it but didn't do it, can be 51 curried also

Ruby, 40 bytes
->d{Time.gm(*d).strftime('%y%j').to_i-1}

Try it online!
So close to being a built-in format, but %j is 1-indexed.
In a happier coincidence, though, Tesco Time (.gm) is one character shorter than local time (.new).

PowerShell, 48 45 39 bytes
Port of Expired Data's answer for C#
$args[0]*1E3+("$args"|date|% d*r)-2E6-1

Try it online!

PowerShell, 60 58 52 bytes
-2 bytes thanks @AdmBorkBork
-join"$args 00$(("$args"|date|% d*r)-1)"[2,3+-3..-1]

Try it online!
Explanation:

The script takes three integers [year,month,day]
"$args" makes a string like YYYY MM DD
"YYYY MM DD"|date converts string to [DateTime]. I don't know why it works. Can you explain?
("YYYY MM DD"|date|% d*r)-1 calculates dayOfYear
the script makes a result string like YYYY MM DD 00### where ### is dayOfYear
finally the script extracts chars from positions [2,3+-3..-1] of the result string and joins this chars to result

Clever use of $args.

Just like with my answer, you only have a single string slice, so you don't need parens in -join(...) for -2 bytes. Try it online!

Nice port! I knew there'd be a good way in PowerShell, but I didn't think it'd be that good

Python 2, 63 61 57 bytes
lambda y,m,d:y%100*1000+(153*m-2)/5+[m+1,1>y%4][2<m]+d-33

Try it online!
Returns the result as an integer.
Neil found that his Charcoal approach turned out to be lucky after some rearrangement.
Pure arithmetic and comparison solution (61 bytes):
lambda y,m,d:y%100*1000+30*m+m/2+(8<m<12)+~(0<y%4)*(2<m)+d-31

Seems to be a day off for January and February?

@Neil Oh LOL I just noticed I was subtracting the extra day February doesn't have even when that month hasn't passed yet. EDIT: should be fixed.

I ported my Charcoal solution, and then golfed it down to 57 bytes: y%100*1000+(153*m-2)/5+[m+1,1>y%4][2<m]+d-33. (Sadly this rearrangement has the same byte count in Charcoal.)

@Neil LOL Charcoal thinking beats Python thinking 10/10 I guess. :D I'll add it and see what I can further do, if I can.

Python 3, 87 85 74 bytes
lambda y,m,d:sum([y*1000-2000029+28*m,3,y%4==0,3,2,3,2,3,3,2,3,2,3][:m])+d

Try it online!
This is using only numeric operations and comparisons.

-5: y%4==0 -> 1>y%4 and splitting up the long array into ([y*1000-2000029+28*m,3,1>y%4]+[3,2,3,2,3]*2)

Jelly, 30 bytes
4ḍȯ2⁽¢wB+30¤_2¦ŻḣS+⁵’+³×ȷ¤_2ȷ6

A full program taking arguments y m d.
Jelly has no built-in date functionality.
Try it online!
How?
4ḍȯ2⁽¢wB+30¤_2¦ŻḣS+⁵’+³×ȷ¤_2ȷ6 - Main Link
           ¤                   - nilad followed by link(s) as a nilad:
    ⁽¢w                        -   1370
       B                       -   to binary = [1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0]
        +30                    -   add 30   = [31,30,31,30,31,30,31,31,30,31,30]
              ¦                - sparse application...
             2                 -   to indices: [2]
            _                  -   action: subtract...
4ḍ                             -     four divides y? -> 1 or 0
  ȯ2                           -     OR 2               1 or 2
               Ż               - prepend 0 = [0,31,29|28,31,30,31,30,31,31,30,31,30]
                ḣ              - head to index m
                 S             - sum
                  +⁵           - add d
                    ’          - subtract one
                         ¤     - nilad followed by link(s) as a nilad:
                      ³        -   y
                        ȷ      -   1000
                       ×       -   multiply
                     +         - add
                           2ȷ6 - 2000000
                          _    - subtract

Charcoal, 33 bytes
ＩΣ⟦×φ﹪θ¹⁰⁰⊖ζ÷⊕×¹⁵³⊖η⁵⎇›η²⊖⊖¬﹪θ⁴⊖η

Try it online! Link is to verbose version of code. Try it online! Link includes test suite. I don't think Charcoal has any date functions, so here's a mathematical solution. Explanation:
   ×φ﹪θ¹⁰⁰                          Year * 1000
          ⊖ζ                        Day - 1
            ÷⊕×¹⁵³⊖η⁵               Days in previous months
                     ⎇›η²            For months after Februrary
                         ⊖⊖¬﹪θ⁴     Adjust for leap years else
                               ⊖η   Adjust for short Februrary
 Σ⟦                                 Take the sum
Ｉ                                   Cast to string for implicit print

[R],  66 bytes
 function(a,b,c)as.numeric(strftime(paste(a,b,c,sep='-'),'%y%j'))-1

taking 3 integers and pasting them into a string strftime can recognize and extract the wanted parts. 0 indexing costs 14 bytes and another conversion to numeric

Ruby, 79 81 79 78 bytes
->y,m,d{(y%100*k=1000)+[d-1,l=31,y%4<1?29:28,l,s=30,l,s,l,l,s,l,s][0,m].sum%k}

Cookies version. Saved a few bytes by including the -1 and day in the array that gets summed, since those spaces were spare anyway.
EDIT: I realised that using "...%03d"%... wasn't really in the spirit of 'numerical and comparison operators only' so I've updated this answer (and gone up by 2 bytes).
EDIT-2: ... and recovered them by using the alternative sub-array index format [start,length], which in turn allowed me to combine the first two entries in the array.
EDIT-3: y%4==0 can be shortened to y%4<1

JavaScript, 70 57 bytes
(y,m,d,U=Date.UTC)=>y%100*1e3+(U(y,m-1,d)-U(y,0,1))/864e5

Try it online!

59 bytes?

@Arnauld y%100*1e3 works because they have the same precedence.

Heh, I was overcomplicating it by using string concatenation when simple arithmetic is all that was needed. Thanks @Arnauld and @Neil!

@darrylyeo something in the back of my head said -2000 was 1 byte too many but %100 was far too obvious!

Japt, 23 bytes
Takes input as 3 individual strings, assuming that's allowed.
¤+@ÐUTX Å¶ÐN Å}as ùT3 É

Try it
Very unhappy with this (there must be a better way to get the day of the year!) but it's been a long day. A port of darrylyeo's JS solution would be 2 bytes shorter but, for some reason, despite Japt being JavaScript, floating point inaccuracies abound.
¤+@ÐUTX Å¶ÐN Å}as ùT3 É     :Implicit input of strings U=y, V=d & W=d (N=[U,V,W])
¤                           :Slice the first 2 characters off U
 +                          :Append
  @                         :  Function taking an integer X as its argument
   ÐUTX                     :    Construct a Date object with U as the year, 0 as the month & X as the day
        Å                   :    Convert to date string ("yyyy-mm-dd")
         ¶                  :    Test for equality with
          ÐN Å              :    Construct a Date object from N and convert to a date string
              }             :  End function
               a            :  Return the first integer that returns true when passed through that function
                s           :  Convert to string
                  ùT3       :  Left pad with 0 to length 3
                      É     :Subtract 1

Red, 54 bytes
func[y m d][y % 100 * 1000 - 1 + pick to now[d m y]11]

Try it online!

8th, 77 71 bytes
Code
needs date/utils : f third -rot d:ymd> doy n:1- swap 100 mod 1000 * + ;

Return the result on TOS
Usage
ok> 2010 1 1 f .
10000
ok> 2019 7 5 f .
19185
ok> 2020 5 20 f .
20140
ok> 2096 12 31 f .
96365
ok> 2099 12 31 f .
99364

Perl 6, 45 bytes
{@_.[0]%100*1000-1+Date.new(|@_).day-of-year}

Try it online!

42 bytes

@joking I have no idea why .[0] was bombing in TIO when I did it. Good catch on 1e3, I'll update when I get to a computer

05AB1E, 29 26 23 bytes
¦¦₄*•ΘÏF•ºS₂+I<£O¹4ÖI<O

Try it online or verify all test cases.
Takes three loose inputs in the order \$year\$, \$month\$, \$day\$.
Explanation:
¦¦             # Remove the first two digits of the (implicit) input-year
               #  i.e. year=2024 → 24
  ₄*           # Multiply it by 1000
               #  → 24000
•ΘÏF•          # Push compressed integer 5254545
     º         # Mirror it vertically to 52545455454525
      S        # Convert it to a list of digits: [5,2,5,4,5,4,5,5,4,5,4,5,2,5]
       ₂+      # Add 26 to each: [31,28,31,30,31,30,31,31,30,31,30,31,28,31]
         I<    # Push the second input-month decremented by 1
           £   # Leave the first month-1 amount of values from the list
            O  # Sum this list
               #  i.e. month=4 → [31,28,31] → 90
¹              # Push the first input-year again
 4Ö            # Check if it's divisible by 4
               #  2024 → 1 (truthy)
I<             # Push the third input-days, decremented by 1
               #  i.e. days=3 → 2
O              # Sum everything on the stack together
               #  24000,90,1,2 → 24093
               # (which is output implicitly as result)

See this 05AB1E tip of mine (section How to compress large integers?) to understand why •ΘÏF• is 5254545.

Elm 0.19, 112 bytes
t y m d=1000*(y-2000)+List.sum(List.take m[d-1,31,if modBy 4 y<1 then 29 else 28,31,30,31,30,31,31,30,31,30,31])

Verify all test cases here.

source: Stack Exchange

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

sort: votes


text:
Jimmy needs your help!
It seems as of recent, there have been a lot a Jimmys falling to their death, as can be seen here, and here where you were asked to determine if Jimmy would fall. It is time we put a stop to this madness and try to save Jimmy.
Jimmy has three body parts /, o, and \ arranged like this
/o\

Platforms are represented with -.  Jimmy will fall off their platform iff they have two or more body parts that are not directly above a platform.
Some examples:
   /o\
- -------

Jimmy will balance since all their body parts are above a -.
   /o\
    ------   ---

Jimmy will balanced since two body parts are above -s.
 /o\
-- ----  --

Jimmy will balance even though they are split between two platforms
  /o\
   -

Jimmy will not balanced since two body parts are not above a platform.

Since my platform supply is running low, I only have platforms with a length of 5, and it is important we use as few as possible.
Your task is to take an input of Jimmys and output a string of platforms which will save all of the Jimmys in the input. Your output must use as few platforms as possible, but each platform must be 5 -'s wide.
Note that the rule of the platform being 5 - wide, means that each platform must have a space between them. ---------- is not valid in the output since it will be considered a platform of length 10 and not two platforms of length 5.
Test Case's
/o\  /o\/o\    // input
----- -----    // output

/o\           /o\
-----       -----

/o\           /o\      // same input as above
-----          -----   // this is also valid output

    /o\ /o\
     -----

Standard rules apply.
This is code-golf, may the shortest answer win!

Does board position matter if it could go multiple places or anywhere is fine as long as all Jimmys are saved? Is there always at least 1 Jimmy?

@Veskah as long as all Jimmys are saved using the fewest possible platforms, position does not matter, so there will be multiple valid outputs for each input. There is no condition on how many Jimmys there will be in the input, an empty string would be a valid input.

Test case 4 looks like a scary face

Jimmy's not happy with you.

@connectyourcharger I was just trying to help Jimmy!

I don't think Jimmy likes all this negative attention, but he appreciates the help.

All joking aside - can we output empty platforms without any Jimmys on top?

@connectyourcharger as I mentioned in the question, you must use as few platforms as possible, so outputting platforms without any Jimmys on them would likely break this rule.

@Quinn Ok, gotcha.

What are the rules for trailing whitespace in input/output?

@negativeseven Both inputs and outputs may contain trailing whitespace

Follow-up: place as many Jimmys as possible on platforms in such a way that they will not fall.

@val that is actually one of the linked questions - I followed up after that one :P

@Quinn That one tells to only count them. Why not actually calculate placement string like in this challenge?

@val ah, you are right - that could be another follow up!

I can see so many Jimmy follow-ups in the future.

Is my output allowed to contain the input demonstrating that all of the platforms correctly save all of the Jimmies?

@Neil Im torn on how to answer you, I keep going back and forth on it - but I think ill allow it

Charcoal, 17 15 bytes
Ｗ‹ⅈＬθ«×⁵№o\§θⅈ→

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

Repeat while the cursor position is less than the length of the input.
×⁵№o\§θⅈ

If the character at that position in the input is an o or a \ then print 5 -s.
→

Move to the next character, thus guaranteeing at least one space between platforms.
Previous 17-byte solution is IMHO more "Charcoal-y".
θ⸿Ｆθ«×⁵¬№ /⊟ＫＤ²↑→

Try it online! Link is to verbose version of code. Output includes input, thus demonstrating correctness of solution. Explanation:
θ⸿

Print the input and move to the start of the next line.
Ｆθ«

Loop over every character of the input to ensure that no Jimmy gets missed.
×⁵¬№ /⊟ＫＤ²↑

Look at the character above the cursor. If there is none, or if it is space or /, then do nothing, otherwise print 5 -s.
→

Move to the next character, thus guaranteeing at least one space between platforms.

Python 2, 70 67 bytes
lambda s:S('/',' ',S("\S.{5}","----- ",s+' '*5))
import re;S=re.sub

Try it online!
-3 bytes thanks to Kevin Cruijssen & Neil
Not the prettiest, not sure how to better handle those leftover slashes...
Unfortunately, we can't replace both ends of each platform with spaces using a single re.sub call, because in the case of 2 platforms being one space apart, the gap between them can't be matched more than once. A lookahead/lookbehind assertion won't help, because anything matched within those assertions doesn't get replaced.
Using a single re.sub reference:
Python 3.8 (pre-release), 78 bytes
lambda s:[s:=re.sub(".[^/ -].{5}"," ----- ",s+"  ",1)for c in s][-1]
import re

Try it online!

69 bytes by first replacing the / and then the [^ ].

@Neil's suggested golf on my Retina answer also works for you: 67 bytes.

JavaScript (ES6),  56 55  54 bytes
s=>[...s+1e4].map(c=>(s--?s:s=c>{}&&5)?'-':' ').join``

Try it online!
Or 47 bytes if returning an array of characters is acceptable.

Python 3, 158 164 160 bytes
a=input();r=""
for i,c in enumerate(a):
 try:r[i]
 except:
  if c in"/o":
   r+=(a[i+5<len(a)and i+5or len(a)-1]=="o"and" "or"")+"----- "
  else:r+=" "
print(r)

Try it online!
This is my first code golf answer, and I'm happy it's on a Jimmy question!
Explanation:

a=input();r="": Take in input and initialize a new string r.
for i,c in enumerate(a):: Enumerate over the input.
try:r[i] ... except:: See if r[i] exists - if not, process the except block.
if c in"/o":: Check if the current character is in Jimmy's first two body parts.
r+=(a[i+5<len(a)and i+5or len(a)-1]=="o"and" "or"")+"----- ": If so, add a new segment. Add a space before our new segment if another Jimmy head is present in five characters.
else:r+=" ": Otherwise, just add a space.
print(r): Print our final result.

Pretty close, but I think this actually doesn't works for all test cases. It should use as few platforms as possible and your last example uses 2 when it could be done with 1

@connectyourcharger "non-competing" doesn't validate an invalid solution. I recommend deleting it if you're not going to fix it for some time (e.g. the next few minutes).

@Quinn I fixed it now.

@connectyourcharger Nice!

Doesn't quite work for three Jimmies separated by 2 spaces each, though not far off.

Good catch, that's one dead Jimmy

@negativeseven Whoopsies. Ah well, I think this answer was doomed from the start. Sorry, Jimmy!

It should work alright if you always add a space before a platform.

Japt, 23 22 bytes
A port of Arnauld's JS solution that I'm just too exhausted to fully test. If it's invalid can a Diamond please delete?
+L² £=U´?U:X>M©5)?'-:S

Try it

Retina, 23 21 bytes
/

$
5*
\S.{5}
5*-
-2 bytes thanks to @Neil.
Contains a single trailing space on the second, fourth, and sixth lines.
Port of @negativeSeven's Python 2 answer, so make sure to upvote him!
Try it online.
Explanation:
Replace all "/" with a " ":
/

Append 5 trailing spaces:
$
5*
Replace all substrings of size six that do not start with a space by "----- ":
\S.{5}
5*-

Can you use \S instead of [^ ]?

@Neil Ah, of course. Thanks!

Jelly, 35 bytes
;3x5¤;0;ṛṫ6ɗ
;⁶x5¤e€⁾o\œṡ1ç/$ÐLị⁾-

Try it online!
A monadic link that takes the input as a string and returns a Jelly string with the platforms.
Takes inspiration from @negativeseven’s answer.

05AB1E, 25 24 bytes
ð5×«'/ð:DŒ6ùʒнðÊ}'-5×ð«:

Port of @negativeSeven's Python 2 answer, so make sure to upvote him!
Try it online or verify all test cases.
Explanation:
ð5×«                      # Append 5 trailing spaces to the (implicit) input-string
    '/ð:                 '# Replace all "/" with a space
        D                 # Duplicate the string
         Œ                # Get all substrings of this
          6ù              # Only leave those of length 6
            ʒ   }         # Filter it further by:
             нðÊ          #  Only keep those which do NOT start with a space
                 '-5×ð«  '# Push a string of 5 "-" appended with a space: "----- "
                       :  # Replace in the initially duplicated string all substrings
                          # remaining in the list with this "---- "

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


text:
Geiger counter prime generation
This challenge is quite simple to conceptualise, but likely a lot harder to execute.
In this challenge, the program will have to detect the number N of its characters that have been randomly changed (at least half of the program will always remain). Then, it will have to output the Nth prime number.
A 'change' can only be the replacement of a character. You can assume it will always be replaced by something within your own language's alphabet.
Rules

Only half of your program (rounded down to fewer changes, if necessary) will ever be hit by radiation.
Standard output formats apply.
The prime number must always be correct. Regarding the 'Nth' prime, N can be 1- or 0-indexed.
1 is not a prime number!
If nothing (or only 1 character, if you choose to make N 1-indexed) is changed, output 0/null/any falsey value.
This is code-golf, so shortest answer in bytes wins.
Standard loopholes apply.

What kind of changes may happen? Is it possible that the very first character is changed into something outside of language alphabet, making code unacceptable by compiler/interpreter?

@DaniilTutubalin see now.

All of your recent challenges have been either put on hold or been very poorly recieved. I strongly suggest using the Sandbox in the future to check community feedback and clear up obvious unintended loopholes before posting to main

I would suggest to change it this way: one (and exactly one) random character is changed. The program must find its index N and output Nth prime number. Or just output N (if its not a duplicate).

What counts as the language's alphabet?

Anything it can parse @SriotchilismOZaic

Closing as unclear because as the rules currently are, I could make a 1-character program that outputs 0 or null, and solutions longer than 3 characters seem impossible, which is probably not what the author intends.

GolfScript, 1 0 bytes
Rule 1 + Rule 5. Thanks to @Jo King for mentioning that empty string is falsey value.

Try it online!
GolfScript, 1 byte
0-indexed version which actually outputs a prime number (0th).
It is also JS and Python solution if we run it in interactive shell.
2

Try it online!

Unary, 0 bytes
Outputs nothing as a falsey value for no change. Note that Unary only has one symbol in its alphabet, so any program outputting a falsey value would be valid, since there are no possible changes to be made.

Brainfuck, 1 byte
Outputs zero byte. Zero byte is falsey in Brainfuck.
.

Try it online!


text:
Can Jimmy hang on his rope?
Yet another Jimmy challenge by his original father. See these other lovely challenges.

As you all know, recently we've been seeing challenges related to Jimmy on platforms. Now, Jimmy is an acrobat as I mentioned before, and he's got other tricks up his sleeve.
One of these tricks is hanging by ropes. Here's an example of a rope Jimmy could hang from:
            ||
            ||
            ||
            ||
            ||
            ||

When Jimmy hangs on a rope, it looks like this:
            ||
            ||
           /o\
            ||
            ||
            ||

He can hang on the left or the right of the rope, so this:
            ||
            ||
            ||
            /o\
            ||
            ||

is also valid. But he cannot hang by just one body part, so anything like this:
            ||
            ||
            ||
          /o\|
            ||
            ||

is invalid. Note that when he is hanging by one body part the other half of the rope is visible because Jimmy does not cover it up.
Also, Jimmy does not like hanging on the bottom of the rope - it scares him - so this:
            ||
            ||
            ||
            ||
            ||
            /o\

is invalid.
The challenge
Take input of a Jimmy situation like the ones above, and output whether Jimmy will hang on to the rope or not through a truthy or falsy value.
The specifics

Write a program that takes input. This can be through a function or any other appropriate input method.

The input should be a scene of one Jimmy and a rope as exemplified above.

The program should output a truthy or falsy value to the console based on whether Jimmy can hang on to the rope or if he would fall off the rope, respectively.
The criteria for Jimmy being able to hang on the rope:

Two of his body parts are on the rope.
He is not on the bottom of the rope.
He is not floating in mid-air.

You can assume the rope will be straight, made up of || segments, and will be longer than one character in height.
You can assume one whole rope and one singular Jimmy will be present in your scene, no more and no less.
You can assume there will be no trailing newlines at the bottom of the rope.
You must cover any amount of leading or trailing spaces before and after the rope.

Test cases
           ||
           ||
           ||                 TRUTHY
           /o\
           ||
           ||

            ||
            ||
          /o\|                FALSY
            ||
            ||

        ||
       /o\                    TRUTHY
        ||

            ||
           /o\                FALSY

         /o\
          ||                  TRUTHY

            ||
            ||
       /o\  ||                FALSY
            ||
            ||

Scoring
This is code-golf, so lowest score in bytes wins after about a week.
Leaderboard
You can view the leaderboard for this post by expanding the widget/snippet below. In order for your post to be included in the rankings, you need a header (# header text) with the following info:

The name of the language (end it with a comma , or dash -), followed by...
The byte count, as the last number to appear in your header.

For example, JavaScript (ES6), 72 bytes is valid, but Fortran, 143 bytes (8-bit) is invalid because the byte count is not the last number in the header (your answer will be recognized as 8 bytes - don't take advantage of this).

<!-- Run the snippet to see the leaderboard. Report any bugs to @ozewski on Github. -->    <iframe src="https://ozewski.github.io/ppcg-leaderboard/?id=187759" width="100%" height="100%" style="border:none;">Oops, your browser is too old to view this content! Please upgrade to a newer version of your browser that supports HTML5.</iframe><style>html,body{margin:0;padding:0;height:100%;overflow:hidden}</style>

Is the rope always going to have the same number of spaces before it, or can that vary?

@Maxwell That can vary.

Can we take the input as something like a list,  where each line is a separate string, or does it have to be one single string?

@Maxwell A list is fine.

Jimmy should really take a holiday

@LuisMendo Jimmy is dedicated to what he does!

"You can assume there will be no trailing newlines at the bottom of the rope." Can we assume that there will be exactly one?

@Doorknob Yeah, I really meant extra lines past the bottom of the rope when I said that.

Is there an implicit rule that prevents the input jimmy+blank line(s)+rope?

Taking a page right of of your book here @connectyourcharger, Jimmy's not happy with you.

Japt, 5 bytes
I think this is right; I've been working for 16 hours straight and barely know my own name so I wouldn't be surprised if it's not!
Õø|io

Try it
Õø|io     :Implicit input
Õ         :Transpose
 ø        :Contains?
  |io     :  "|" prepended with "o"

16 hours? Now that's dedication!

Wait so it took you 16 hours to write down 5 bytes? ;-)

Looks you like you need a break (same advice goes for little Jimmy!).

@connectyourcharger, no, that's my life! Dedication would be doing it without a break. Which I did!

@Cullub, not quite but it did take a hell of a lot longer than it should have given the state I was in!

@ihavenoidea, Pfft! I'll sleep when I'm dead!

Python 2 or 3,  33  30 bytes
-3 thanks to Maxwell
lambda l:'o'in map(max,l[:-1])

An unnamed function accepting a list of lines
Try it online!
How?
There needs to be a section of rope obscured by Jimmy that is not the bottom one.
lambda l:'o'in map(max,l[:-1])
lambda l:                      # a function taking l (the lines as strings)
                       l[:-1]  # strip off the last line
               map(max,      ) # maximum of each line (where '|'>'o'>'\'>'/'>' ')
         'o'in                 # was 'o' one of them? (hence Jimmy obscured all the rope)

Save three bytes: lambda l:'o'in map(max,l[:-1])

Oh, very astute - thanks!

That's a very clever use of ASCII values. Nice.

Python 2, 28 bytes
lambda x:"o', '|"in`zip(*x)`

Try it online!
How does it work? It takes input as a list of strings, and zip joins the string. Jimmy stays on the rope if there is a "|" below an "o", so this code joins all the lines and checks if there is an "o" followed by a "|".
Annotated code:
lambda x: # Creates an anonymous function that takes one argument
  "o', '|" # If this substring is in the zip object, then Jimmy's "o" is above a "|"
    in
    `    # Back quotes change the object into its string representation
    zip(*x)` # Joins the lines together

(Old Answer) Python 2 or 3, 39 bytes
lambda x:1-all("|"in i for i in x[:-1])

A function that takes input as a list of strings, each string being a different line.
-11 bytes thanks to xnor! -2 bytes thanks to Jonathan Allan!
Try it online! (If you want to try more test cases, just put a "." after each set of lines in the input box.)
How does this work? Well, if Jimmy is fully on the rope, then that line will not have any "|" characters. Therefore, we can check each line, and if we find any with no "|" characters, then we know that Jimmy can stay on the rope. However, Jimmy can not hang on to the bottom of the rope; therefore, we don't include the final line in our check. If the final line is just another part of the rope, than it won't matter, because we'll still find a valid row higher up, but if the final line is the one with Jimmy, then it won't find a line without "|" anywhere, and will return False.

Can you do it without the check that the line has a "/"?

@xnor Yup! -11 bytes

Save a couple like so: lambda x:1-all("|"in i for i in x[:-1])

Ouch, you was faster :)

What about the case where he's at the end of his rope, though? It seems you're solution would simply return a truthy result, which should be incorrect.

@KlaymenDK The function iterates through x[:-1], not x. x[:-1] is all the elements of the list except the final element, because in Python you can use negative indices. Therefore, it (correctly) returns a falsy result if Jimmy is at the bottom of the rope.

Jelly,  9 7  6 bytes
Ṗ<”|ṀẠ

A monadic Link accepting a list of lines
Try it online!
How?
There needs to be a section of rope obscured by Jimmy that is not the bottom one.
Ṗ<”|ṀẠ - Main Link: list of lines of characters
Ṗ      - remove last line
  ”|   - pipe character
 <     - less than? (vectorises) - Note that all other characters are
    Ṁ  - maximum
     Ạ - all?

I feel so weird writing these challenges about Jimmies. It starts to make you feel weird once you imagine multiple Jimmies hanging off of the same rope.

@connectyourcharger Foreshadowing?

@negativeseven Possibly. I have already considered a meta community wiki to index all of the Jimmy posts.

"Note that all other characters are [less than '|']"?

@EriktheOutgolfer yeah, implicitly.

Grime, 5 bytes
\o/\|

Try it online!
The right tool for the job.
\o        # match an "o"
  /       # above
   \|     # a "|"

05AB1E, 5 bytes
Çü%àθ

Try it online!
Ç         # convert the input to a 2D array of codepoints
 ü%       # pairwise modulo (vectorized)
   à      # maximum (*not* vectorized, returns a single number)
    θ     # tail (last digit)

The only characters that can appear in the input are \o/ |, with respective
codepoints 92, 111, 47, 32, 124 (there are no newlines, since we chose to take
input as an array of lines). The possible results by moduloing two of these
numbers are 0, 13, 15, 17, 19, 28, 30, 32, 45, 47, 92, 111. 111 is the largest
of those, and also the only one that ends with 1, so the code will output truthy
if and only if 111 is present in the list (only 1 is truthy in 05AB1E). 111
is 111 (o) % 124 (|), and so only occurs if there's an o above a | in the input.

Very nice with the pairwise modulo.

brainfuck, 79 64 bytes
>>+<<,[----------[<]>>[.-]+<[>-]+[---------<-->]<[<]>>[-]<[>]<,]

Try it online!
Outputs the 0x01 byte for truthy and nothing for falsy.
Z: 0
A: input
B: 0
C: has no | been found on this line?

>>+<<                       initialize C to 1
,[                          loop over each char

  ----------                set A to 0 if input was \n
  [<]>>                     move to C if input was \n; B otherwise
  [                         if input was \n and C is true
    .-                      output 1
  ]

  +                         this will reinitialize C to 1 if input was \n
                            but also clobber B with 1 if it wasn't
                            now we have { 0   0   0  (1)} if input was \n;
                                        { 0   _  (1)  _ } otherwise
  <[>-]                     clear own cell if the one to the left is positive
                            this yields { 0   0  (0)  1 } and
                                        { 0   _  (0)  _ } as desired

  +[---------<-->]          set A to 0 if input was |
  <[<]>>                    move to C if input was |; B otherwise
  [-]                       zero out current cell: clears C if input was |
  <[>]<                     realign pointer onto A

,]                          break on end of input

Stax, 6 bytes
é¿┤4╠q

Run and debug it
Transposes the input, then searches for "o|".

APL (Dyalog Unicode), 8 bytesSBCS
Anonymous tacit prefix function, taking a character matrix as argument.
1∊'o|'⍷⍉

Try it online!
⍉ transpose
'o|'⍷ mask for everywhere o is immediate followed by |
1∊ is one a member thereof?

Dyalog APL Extended, 14 13 11 9 bytes
3∊¯1+/⍤↓<

Try it online!

-2: 3∊¯1+/⍤↓<

Haskell, 19 bytes
any(all(<'|')).init

Try it online!

JavaScript, 39 33 bytes
Thanks @Daniil Tutubalin for golfing off 2 bytes
x=>!!x.match(/^( *)\/[^|]*\n/m)

This matches any line which is not the line where his left arm shows up and none of the rope shows.
Try it online!

It appears to fail if he is at the bottom of the rope

It should fail if he is at the bottom, right?  Because Jimmy falls if he is at the bottom fo the rope

I mean that when I put him at the bottom of the rope your function for some reason returned 1

Hm, nevermind then.

Minus 2 bytes: x=>!!x.match(/^( *)\/o\\ *\n\1 ?\|/m)
But basically !! is not required because valid object is truthy value and null is falsy value, so you can make even minus 4.

What about /^ *.o. *\n/?

///, 53 50 bytes
/~/\/\~/\/o\\/1~1 /1~ 1/1~|1~/1|~/|~/1.~/ ~/.~/
~.

Try it online!
Because there is no other way to take input in ///, it is hard-coded:
/~/\/\~/\/o\\/1~1 /1~ 1/1~|1~/1|~/|~/1.~/ ~/.~/
~<INPUT HERE>.

Explanation:
The general approach is to replace Jimmy with a unary 1, then remove him from all situations where he is in danger. If he survives, he is outputted. If he doesn't, then nothing is. The ~ in the code are a replacement for //, which allow the code to be shortened by 3 bytes here. They are omitted from the explanation.
/{/\//
/}/\/\//

          {The top two lines allow me to leave comments without disturbing the code.}

/\/o\\/1/ {Replace Jimmy with a 1.}
/1 /1/    {Get rid of any spaces in front of Jimmy. This moves Jimmy towards the rope from the left.}
/ 1/1/    {Get rid of any spaces after Jimmy. This moves Jimmy towards the rope from the right.}

/|1//     {If Jimmy is touching the rope, remove him and the rope.}
/1|//     {This is based on the observation that in all cases where Jimmy is safe, there is no visible rope on his line.}

/|//      {Remove any remaining rope. If Jimmy was touching a rope, it's already too late for him.}
/1.//     {This handles the case where Jimmy is at the bottom of the rope (hence the period at the end).}

/ //      {The remaining lines clean up the output.}
/.//
/
//

           ||
           ||
           ||
           /o\
           ||.

Try it online!

Elm 0.19, 68 60 bytes
List.any(not<<String.contains"|")<<List.drop 1<<List.reverse

Anonymous, points-free function. Takes input as a list of lines. Disregarding the last line, it checks whether there are any with no '|' in them – implying the rope is fully covered by Jimmy.
Thanks to Lynn for golfing 8 bytes.
Verify all test cases here.

List.any(not<<String.contains"|")<<List.drop 1<<List.reverse is a slightly shorter anonymous function.

Kotlin, 93 84 bytes
fun j(a:List<String>){print(a.count{!it.contains("|")}==1&&!a.last().contains("o"))}

Try it online!

Ruby 2.5.5, 22 bytes
->x{x.pop;!x.all? /\|/}

Expects an array of lines. Requires a minimum of version 2.5.5 because this is when Array#all?(pattern) was added.
Because of the constraints that the input will always be a valid scene of Jimmy and a rope, it boils down to whether any of the lines prior to the last line have the rope obscured.

Retina, 9 bytes
m`^[^|]+^

Try it online!
Now, I've never programmed in Retina before, but as far as I can tell this works. It is a regular expression that finds a string containing (1) the beginning of the input, (2) no "|" characters, and (3) a newline.
People more familiar with either regular expressions or Retina are encouraged to offer suggestions. -2 bytes thanks to Neil!

Two alternatives: 1) Remove the \n because a new line is not a | and so that will match anyway, and the final ^ suffices to ensure that you actually matched a newline. 2) Use a pilcrow ¶ instead of \n and remove the trailing ^ (because that's always true after a newline in multiline mode).

JavaScript, 38 37 bytes
r=>r.some(x=>!~x.search`\\|`,r.pop())

Try it online!

Befunge-98 (PyFunge), 26 24 bytes
]~:a-!#v_' `+
^_-3q#$<
@

Try it online!
Exits with return code 3 if \n is encountered and the last line contained 3 non-space characters, otherwise exits with return code 0 on EOF. Thus, this relies on the last line not containing a trailing newline.
Dissected
]~                     :a-!#v_                ' `+
 Read character,      Branch downwards if   Increment counter on
 branch up (and        equal to 10 ('\n')   the stack if greater
 loop around) if                            than 32 (' ') and
 end of stream                              implicitly loop

^_-3q#$<                    <
 Return with code 3
 if counter is equal to 3,
 otherwise reset counter
 and return to beginning

@
 Return with exit
 code 0

Perl 5 -p, 26 bytes
$\||=!/\|/;$;=/o/}{$\&&=!$

Try it online!

05AB1E (legacy), 6 bytes
ζJ„o|å

Port of @Shaggy's Japt answer.
Input as a list of lines.
Try it online.
Explanation:
ζ       # Zip/transpose the (implicit) strings in the input-list, with space as filler
        # (NOTE: zip/transpose doesn't work on string-list in the new version of 05AB1E,
        #  which is why we use the legacy version)
 J      # Join these zipped/transposed lines together to a single string
  „o|å  # And check if it contains the string "o|"
        # (after which the result is output implicitly)

5 bytes on non-legacy

PowerShell, 26 bytes
Port of Maxwell's answer for Retina.
"$args"-match'(?m)^[^|]+^'

Try it online!
Explanation:
true if:

there is a line in a scene that does not contain the character |
and after this line there is a start of a new line.

false otherwise.

Husk, 6 bytes
€'om▲h

Try it online! A port of Jonathan Allan's answer.

Vyxal, 6 bytes
Ḣ\|vOA

Try it Online!
Takes a list of lines, returns a 1 if Jimmy can't hang on, 0 if he can.

Pyth, 9 bytes
sPm!sqR\|

Test suite!

Pyret, 79 bytes
{(l):all2({(r,n):string-char-at(n,string-index-of(r,"o") == "|")},l,link(0,l))}

Expects an array of lines as strings. Makes a copy in link(0,l) where all rows shifted down by one. Goes through each row r and n where n is the row below r. Checks that if Jimmy's body "o" is at some position, then the row below it has a pipe there (i.e. Jimmy's hanging on the rope and isn't at the bottom).

SnakeEx, 7 bytes
j:<R>o|

Try it here!
Explanation
The only necessary and sufficient condition is that Jimmy's torso has to have a rope character below it.
j:       Define the main snake, j (for Jimmy)
  <R>    Pivot right (i.e. downward, since the snake starts out moving to the right)
     o   Match Jimmy's torso and move down
      |  Match a rope character

Rockstar, 147 146 137 bytes
Outputs 0 for false and 1 for true
listen to J
Y's 0
while J
cut J
listen to R
X's 0
while J
let S be R at X
let Y be+roll J's "o" and S's "|"
let X be+1

let J be R

say Y

Try it here (Code will need to be pasted in)
listen to J     :Read first line of input into variable J
Y's 0           :Initialise Y as 0
while J         :While J exists
cut J           :  Split J into an array
listen to R     :  Read next line of input into variable R
X's 0           :  Initialise X as 0
while J         :  While J is not empty
let S be        :    Assign to variable S
  R at X        :      The character in R at 0-based index S
let Y be+       :    Increment Y by
  roll J        :      Pop the first element from J
  's "o"        :      Eqaul to "o"?
  and           :      Logical AND with
  S's "|"       :      S is equal to "|"
let X be+1      :    Increment X
                :  End while loop
let J be R      :  Reassign R to J
                :End while loop
say Y           :Output Y


text:
Counting generalized polyominoes
This challenge will have you count pseudo-polyforms on the snub square tiling.
I think that this sequence does not yet exist on the OEIS, so this challenge exists to compute as many terms as possible for this sequence.
Update: this is now on the OEIS as A309159: Number of generalized polyforms on the snub square tiling with n cells.
Definitions
The snub square tiling is a semiregular tiling of the plane that consists of equilateral triangles and squares.

A pseudo-polyform on the snub square tiling is a plane figure constructed by joining together these triangles and squares along their shared sides, analogous to a polyomino. Here is an example of a six-cell and an eight-cell pseudo-polyform:

Examples
For n = 1 there are two 1-cell pseudo-polyforms, namely the square and the triangle:

For n = 2 there are two 2-cell pseudo-polyforms, namely a square with a triangle and two triangles.

For n = 3 there are four 3-cell pseudo-polyforms.

Challenge
The goal of this challenge is to compute as many terms as possible in this sequence, which begins 2, 2, 4, ... and where the n-th term is the number of n-cell pseudo-polyforms up to rotation and reflection.
Run your code for as long as you'd like. The winner of this challenge will be the user who posts the most terms of the sequence, along with their code. If two users post the same number of terms, then whoever posts their last term earliest wins.
(Once there are enough known terms to prove that this sequence does not already exist in the OEIS, I'll create an entry in the OEIS and list the contributor as a co-author if he or she desires.)

In a [tag:code-challenge] challenge, answers should be code, not terms of a sequence. Also, the number of terms one manages to discover before too much time passes in their opinion (or before the program overflows) isn't an objective winning criterion.

@EriktheOutgolfer, last time I used a [tag:code-challenge] for a similar-style problem, and it worked really well.

Hm... I disagree on the part that you can run the program for as long as you like, since different people have different levels of patience (e.g. person 1 might let it run for 7 days, while person 2 might let it run for 30 days), and so it's subjective, although others may consider it to be a "good subjective". Just saying that it's not really objective. Regarding the [tag:code-challenge] tag, I just wanted to make sure you're asking for code and not just a list of terms (Project Euler style). :P

Anyone want to confirm or dispute 2, 2, 4, 10, 28, 79, 235, 720, 2254, 7146, 22927, 74137, 241461, 790838, 2603210, 8604861?

@PeterTaylor I'm getting the same numbers

next is 28549166

This question might work well as a fastest code question similar to this question

I’ve got the same numbers up to n=10 with my far less efficient R code.

Nick, submit the R code as an answer. I’d be curious to see it.

Haskell
Now that not only the comments document that Peter Taylor was the first one to give enough terms to search on OEIS, I can give my results.
( 1 - 10) 2, 2, 4, 10, 28, 79, 235, 720, 2254, 7146,
(11 - 15) 22927, 74137, 241461, 790838, 2603210,
(16 - 18) 8604861, 28549166, 95027832,
(19 - 22) 317229779, 1061764660, 3562113987, 11976146355

Earlier, I counted hexagonal polyominoes. Except for some optimizations, what I'm doing here is very similar.
The elements of the tiling are represented like this:
You can go in an almost straight line from left to right
(in the first picture), alternating between squares and
rectangles. There are almost parallel further lines,
wiggling in opposite directions. Together, they miss
some triangles.
There are similar almost straight parallel lines from
bottom to top, containing the missing triangles.
Now ignore the wiggling and use a Cartesian coordinate
system, but only use odd numbers for the coordinates of the squares.
Then the triangles naturally get coordinate pairs with one even
and one odd coordinate. Pairs with both coordinates even
don't represent elements of the tiling.
(You could as well use even numbers for the coordinates
of the squares. I guess I decided this way because I thought
about reflection before rotation.)
Save the program in a file with a name like cgp.hs and compile with ghc -O2 -o cgp cgp.hs. It takes either one numeric command line argument and computes the number of polyominoes of that size, or none, in which case it computes values until stopped.
{-# LANGUAGE BangPatterns #-}

import Data.List(sort)
import qualified Data.Set as S
import System.Environment(getArgs)

data Point = P !Int !Int deriving (Eq,Ord)

start :: Point
start = P 1 1

redsq :: Point -> Bool
redsq (P x y) = (x+y) `mod` 4 == 2

neighs :: Point -> [Point]
neighs (P x y) =
  case (even x, even y) of
    (False,False) -> [P x (y+1), P (x+1) y, P x (y-1), P (x-1) y]
    (True, False) -> (P x (c y (x+y+1))) : opt [P (x-1) y, P (x+1) y]
    (False,True ) -> (P (c x (x+y-1)) y) : opt [P x (y-1), P x (y+1)]
  where
    opt = filter ok
    ok p = p>start || not (redsq p)
    c z m = if m `mod` 4 == 0 then z+2 else z-2

count :: S.Set Point -> S.Set Point -> [Point] -> Int -> Int -> Int -> Int -> Int
count use _    _            0 c r y =
  if check (S.toAscList use) (y==r)
    then c+1
    else c
count _   _    []           _ c _ _ = c
count use seen (p:possible) n c r y =
  let !c' = count use seen possible n c r y
      new = filter (`S.notMember` seen) $ neighs p
      !r' = if redsq p then r+1 else r
      !y' = if redsq (mirror p) then y+1 else y
      !n' = n-1
  in if r'+n' < y'
       then c'
       else count (S.insert p use) (foldr S.insert seen new) (new++possible)
                  n' c' r' y'

class Geom g where
  translate :: Int -> Int -> g -> g
  rot :: g -> g
  mirror :: g -> g

instance Geom Point where
  translate dx dy (P x y) = P (dx+x) (dy+y)
  rot (P x y) = P (2-y) x    -- rotate around (1,1)
  mirror (P x y) = P x (-y)

instance (Geom g, Ord g) => Geom [g] where
  translate x y = map $ translate x y
  rot = sort . map rot
  mirror = sort . map mirror

normalize :: [Point] -> [Point]
normalize pol = let (P x y) = head (filter redsq pol)
                in translate (1-x) (1-y) pol

check :: [Point] -> Bool -> Bool
check pol !cm = let rotated = take 4 $ iterate rot pol
                    mirrored = if cm then map mirror rotated else []
                    alts = map normalize (tail rotated ++ mirrored)
                in all (pol<=) alts

f :: Int -> Int
f 0 = 1; f 1 = 2; f 2 = 2
f n = count S.empty S.empty [start] n 0 0 0

output :: Int -> IO ()
output n = putStrLn $ show n ++ ": " ++ show (f n)

main = do args <- getArgs
          case args of
            []  -> mapM_ output [1..]
            [n] -> output (read n)

Try it online!

Looks like you have a better tile representation than me. Would you mind explaining how it works?

I hope my addition answers your question.

2, 2, 4, 10, 28, 79, 235, 720, 2254, 7146, 22927, 74137, 241461, 790838, 2603210, 8604861, 28549166, 95027832
I'm going to put a stake in the ground before Christian Sievers posts an answer for n=18. This is as far as I can go with the current code and 16GB of RAM. I've already had to sacrifice some speed to reduce the memory usage, and I'm going to have to do so even more. I have some ideas...
This snippet is the SVG from the first comment.

<svg xmlns="http://www.w3.org/2000/svg" width="130" height="130">
  <path style="stroke:none; fill:#f22" d="M 72,72 l -14.235,53.1259 -53.1259,-14.235 14.235,-53.1259 z" />  <!-- "Anticlockwise" square -->
  <path style="stroke:none; fill:#44f" d="M 72,72 l 53.1259,-14.235 -14.235,-53.1259 -53.1259,14.235 z" />  <!-- "Clockwise" square -->

  <path style="stroke:none; fill:#4f4" d="M 72,72 l 38.89,38.89 14.235,-53.1259 z" />  <!-- "NE" triangle -->
  <path style="stroke:none; fill:#ff4" d="M 72,72 l 38.89,38.89 -53.1259,14.235 z" />  <!-- "SW" triangle -->
  <path style="stroke:none; fill:#4ff" d="M 72,72 m -53.1259,-14.235 l 38.89,-38.89 -53.1259,-14.235 z" />  <!-- "NW" triangle -->

  <path style="stroke:#000; fill:none" d="M 72,72 m 38.89,38.89 l 14.235,-53.1259 -14.235,-53.1259 -53.1259,14.235 -53.1259,-14.235 14.235,53.1259 -14.235,53.1259 53.1259,14.235 53.1259,-14.235" />
</svg>

Code is C#. I ran it with .Net Core 2.2.6 under Linux.
#define SUPERLIGHT
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;

namespace Sandbox
{
    // https://codegolf.stackexchange.com/questions/187763/counting-generalized-polyominoes
    // Count polyominos on the snub square tiling.

    // We index the tiles using the following basic element, which tiles like a square:
    /*
        <?xml version="1.0" standalone="no"?>
        <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
        <svg xmlns="http://www.w3.org/2000/svg" width="130" height="130">
            <path style="stroke:none; fill:#f22" d="M 72,72 l -14.235,53.1259 -53.1259,-14.235 14.235,-53.1259 z" />  <!-- "Anticlockwise" square -->
            <path style="stroke:none; fill:#44f" d="M 72,72 l 53.1259,-14.235 -14.235,-53.1259 -53.1259,14.235 z" />  <!-- "Clockwise" square -->

            <path style="stroke:none; fill:#4f4" d="M 72,72 l 38.89,38.89 14.235,-53.1259 z" />  <!-- "NE" triangle -->
            <path style="stroke:none; fill:#ff4" d="M 72,72 l 38.89,38.89 -53.1259,14.235 z" />  <!-- "SW" triangle -->
            <path style="stroke:none; fill:#4ff" d="M 72,72 m -53.1259,-14.235 l 38.89,-38.89 -53.1259,-14.235 z" />  <!-- "NW" triangle -->
            <!-- There's a "SE" triangle, but it's unfilled -->

            <path style="stroke:#000; fill:none" d="M 72,72 m 38.89,38.89 l 14.235,-53.1259 -14.235,-53.1259 -53.1259,14.235 -53.1259,-14.235 14.235,53.1259 -14.235,53.1259 53.1259,14.235 53.1259,-14.235" />
        </svg>
    */
    // In terms of symmetries, we have rotation by 90 degrees and reflection, possibly with glide.
    // We obviously want a canonical representation.
    //   Reflection interchanges "anticlockwise" and "clockwise" squares, so we shall require at least as many anticlockwise as clockwise.
    //   Rotation anticlockwise by 90 maps NE -> NW -> SW -> SE -> NE. We rotate to get a standard necklace.
    //   Further ties must be broken lexicographically, after translating to give minimum X and Y of 0.
    class PPCG187763
    {

        internal static void Main()
        {
            SanityChecks();

            var polyominos = new HashSet<TileSet>();
            polyominos.Add(new TileSet(Enumerable.Repeat(new Tile { X = 0, Y = 0, Shape = TileShape.SE }, 1)));
            polyominos.Add(new TileSet(Enumerable.Repeat(new Tile { X = 0, Y = 0, Shape = TileShape.Anticlockwise }, 1)));
            Console.WriteLine($"1\t{polyominos.Count}");
            for (int tileCount = 2; tileCount < 60; tileCount++)
            {
                var sw = new Stopwatch();
                sw.Start();
                var nextPolyominos = new HashSet<TileSet>();
                // TODO This can be greatly optimised by tracking discarded insertion points
                foreach (var polyomino in polyominos)
                {
                    foreach (var neighbour in polyomino.SelectMany(tile => tile.Neighbours).Distinct())
                    {
                        if (!polyomino.Contains(neighbour)) nextPolyominos.Add(new TileSet(polyomino.Concat(Enumerable.Repeat(neighbour, 1))));
                    }
                }
                polyominos = nextPolyominos;
                Console.WriteLine($"{tileCount}\t{polyominos.Count}\t{sw.ElapsedMilliseconds}ms");
            }
        }

        private static void SanityChecks()
        {
            var cluster = new HashSet<Tile>();
            cluster.Add(new Tile { Shape = TileShape.Anticlockwise });
            for (int i = 0; i < 3; i++)
            {
                foreach (var tile in cluster.SelectMany(tile => tile.Neighbours).ToList()) cluster.Add(tile);
            }

            foreach (var tile in cluster)
            {
                foreach (var neighbour in tile.Neighbours)
                {
                    if (!neighbour.Neighbours.Contains(tile))
                    {
                        throw new Exception("Assertion failed: adjacency isn't symmetric");
                    }

                    if (!tile.Flip().Neighbours.Contains(neighbour.Flip()))
                    {
                        throw new Exception("Assertion failed: flip doesn't preserve adjacency");
                    }

                    if (!tile.Rot().Neighbours.Contains(neighbour.Rot()))
                    {
                        throw new Exception("Assertion failed: rot doesn't preserve adjacency");
                    }

                    if (!tile.Equals(tile.Rot().Rot().Rot().Rot()))
                    {
                        throw new Exception("Assertion failed: rot^4 should be identity");
                    }
                }
            }
        }

        struct Tile : IComparable<Tile>
        {
            public TileShape Shape { get; set; }
            public sbyte X { get; set; }
            public sbyte Y { get; set; }

            public IEnumerable<Tile> Neighbours
            {
                get
                {
                    switch (Shape)
                    {
                        case TileShape.Anticlockwise:
                            yield return new Tile { X = X, Y = Y, Shape = TileShape.SE };
                            yield return new Tile { X = X, Y = Y, Shape = TileShape.SW };
                            yield return new Tile { X = X, Y = (sbyte)(Y - 1), Shape = TileShape.NW };
                            yield return new Tile { X = (sbyte)(X - 1), Y = Y, Shape = TileShape.NE };
                            break;

                        case TileShape.Clockwise:
                            yield return new Tile { X = X, Y = Y, Shape = TileShape.SE };
                            yield return new Tile { X = X, Y = Y, Shape = TileShape.NE };
                            yield return new Tile { X = X, Y = (sbyte)(Y + 1), Shape = TileShape.SW };
                            yield return new Tile { X = (sbyte)(X + 1), Y = Y, Shape = TileShape.NW };
                            break;

                        case TileShape.NE:
                            yield return new Tile { X = X, Y = Y, Shape = TileShape.SW };
                            yield return new Tile { X = X, Y = Y, Shape = TileShape.Clockwise };
                            yield return new Tile { X = (sbyte)(X + 1), Y = Y, Shape = TileShape.Anticlockwise };
                            break;

                        case TileShape.NW:
                            yield return new Tile { X = X, Y = Y, Shape = TileShape.SE };
                            yield return new Tile { X = (sbyte)(X - 1), Y = Y, Shape = TileShape.Clockwise };
                            yield return new Tile { X = X, Y = (sbyte)(Y + 1), Shape = TileShape.Anticlockwise };
                            break;

                        case TileShape.SE:
                            yield return new Tile { X = X, Y = Y, Shape = TileShape.NW };
                            yield return new Tile { X = X, Y = Y, Shape = TileShape.Clockwise };
                            yield return new Tile { X = X, Y = Y, Shape = TileShape.Anticlockwise };
                            break;

                        case TileShape.SW:
                            yield return new Tile { X = X, Y = Y, Shape = TileShape.NE };
                            yield return new Tile { X = X, Y = (sbyte)(Y - 1), Shape = TileShape.Clockwise };
                            yield return new Tile { X = X, Y = Y, Shape = TileShape.Anticlockwise };
                            break;

                        default:
                            throw new NotSupportedException();
                    }
                }
            }

            public Tile Flip()
            {
                // We'll flip vertically.
                switch (Shape)
                {
                    case TileShape.Anticlockwise:
                        return new Tile { Shape = TileShape.Clockwise, X = X, Y = (sbyte)-Y };
                    case TileShape.Clockwise:
                        return new Tile { Shape = TileShape.Anticlockwise, X = (sbyte)(X + 1), Y = (sbyte)-Y };
                    case TileShape.NE: // G
                        return new Tile { Shape = TileShape.SE, X = (sbyte)(X + 1), Y = (sbyte)-Y };
                    case TileShape.NW: // Cy
                        return new Tile { Shape = TileShape.SW, X = X, Y = (sbyte)-Y };
                    case TileShape.SE: // W
                        return new Tile { Shape = TileShape.NE, X = X, Y = (sbyte)-Y };
                    case TileShape.SW: // Y
                        return new Tile { Shape = TileShape.NW, X = (sbyte)(X + 1), Y = (sbyte)-Y };
                    default:
                        throw new NotSupportedException();
                }
            }

            public Tile Rot()
            {
                // Anti-clockwise rotation: (x, y) = (-y, x)
                // But there will be offsets to account for the positions within the cell
                switch (Shape)
                {
                    case TileShape.Anticlockwise:
                        return new Tile { Shape = TileShape.Anticlockwise, X = (sbyte)-Y, Y = X };
                    case TileShape.Clockwise:
                        return new Tile { Shape = TileShape.Clockwise, X = (sbyte)(-Y - 1), Y = X };
                    case TileShape.NE:
                        return new Tile { Shape = TileShape.NW, X = (sbyte)-Y, Y = X };
                    case TileShape.NW:
                        return new Tile { Shape = TileShape.SW, X = (sbyte)(-Y - 1), Y = X };
                    case TileShape.SE:
                        return new Tile { Shape = TileShape.NE, X = (sbyte)(-Y - 1), Y = X };
                    case TileShape.SW:
                        return new Tile { Shape = TileShape.SE, X = (sbyte)-Y, Y = X };
                    default:
                        throw new NotSupportedException();
                }
            }

            public override int GetHashCode() => (X << 17) + (Y << 3) + (int)Shape;

            public bool Equals(Tile tile) => X == tile.X && Y == tile.Y && Shape == tile.Shape;

            public override bool Equals(object obj) => obj is Tile tile && Equals(tile);

            public int CompareTo(Tile other)
            {
                if (X != other.X) return X.CompareTo(other.X);
                if (Y != other.Y) return Y.CompareTo(other.Y);
                return Shape.CompareTo(other.Shape);
            }

            public override string ToString() => $"({X},{Y},{Shape})";
        }

        enum TileShape : byte
        {
            Anticlockwise,
            Clockwise,
            NE,
            SW,
            NW,
            SE
        }

        class TileSet : IReadOnlyCollection<Tile>
        {
            public TileSet(IEnumerable<Tile> tiles)
            {
                // Canonicalise
                var ordered = _Canonicalise(new HashSet<Tile>(tiles));
                int h = 1;
                foreach (var tile in ordered) h = h * 37 + tile.GetHashCode();
                _HashCode = h;

                #if SUPERLIGHT

                // Since we normalise to have minimum X and Y of 0, we can use unsigned coordinates.
                // And since we're looking at connected graphs of on the order of 20 items, 6 bits per coordinate is plenty.
                _Items = ordered.Select(tile => (short)((tile.X << 9) + (tile.Y << 3) + (int)tile.Shape)).ToArray();

                #else

                _Items = new HashSet<Tile>(ordered);

                #endif
            }

            private IReadOnlyList<Tile> _Canonicalise(ISet<Tile> tiles)
            {
                int ac = tiles.Count(tile => tile.Shape == TileShape.Anticlockwise);
                int c = tiles.Count(tile => tile.Shape == TileShape.Clockwise);

                if (ac < c) return _CanonicaliseRot(tiles);
                if (ac > c) return _CanonicaliseRot(tiles.Select(tile => tile.Flip()));

                return _Min(_CanonicaliseRot(tiles), _CanonicaliseRot(tiles.Select(tile => tile.Flip())));
            }

            private IReadOnlyList<Tile> _Min(IReadOnlyList<Tile> tiles1, IReadOnlyList<Tile> tiles2)
            {
                for (int i = 0; i < tiles1.Count; i++)
                {
                    int cmp = tiles1[i].CompareTo(tiles2[i]);
                    if (cmp < 0) return tiles1;
                    if (cmp > 0) return tiles2;
                }

                return tiles1;
            }

            private IReadOnlyList<Tile> _CanonicaliseRot(IEnumerable<Tile> tiles)
            {
                //   Rotation anticlockwise by 90 maps NE -> NW -> SW -> SE -> NE. We rotate to get one of these necklaces (in rank order, not exact values):
                //     Necklaces:
                //     SE NE NW SW
                //     0  0  0  0    ** Four positions to consider
                //     1  0  0  0
                //     1  0  1  0    ** Two positions to consider
                //     1  1  0  0
                //     1  1  1  0
                //     2  0  0  1
                //     2  0  1  0
                //     2  0  1  1
                //     2  1  0  0
                //     2  1  0  1
                //     2  1  1  0
                //     2  1  2  0
                //     2  2  0  1
                //     2  2  1  0
                //     3  0  1  2
                //     3  0  2  1
                //     3  1  0  2
                //     3  1  2  0
                //     3  2  0  1
                //     3  2  1  0

                int se = tiles.Count(tile => tile.Shape == TileShape.SE);
                int ne = tiles.Count(tile => tile.Shape == TileShape.NE);
                int nw = tiles.Count(tile => tile.Shape == TileShape.NW);
                int sw = tiles.Count(tile => tile.Shape == TileShape.SW);
                var sorted = new int[] { se, ne, nw, sw }.Distinct().OrderBy(x => x);
                var index = 1000 * sorted.IndexOf(se) + 100 * sorted.IndexOf(ne) + 10 * sorted.IndexOf(nw) + sorted.IndexOf(sw);
                switch (index)
                {
                    case 0:
                        // All four positions need to be considered
                        var best = _Translate(tiles);
                        best = _Min(best, _Translate(tiles.Select(tile => tile.Rot())));
                        best = _Min(best, _Translate(tiles.Select(tile => tile.Rot().Rot())));
                        best = _Min(best, _Translate(tiles.Select(tile => tile.Rot().Rot().Rot())));
                        return best;

                    case 101:
                        // Two options need to be considered;
                        return _Min(_Translate(tiles.Select(tile => tile.Rot())), _Translate(tiles.Select(tile => tile.Rot().Rot().Rot())));

                    case 1010:
                        // Two options need to be considered;
                        return _Min(_Translate(tiles), _Translate(tiles.Select(tile => tile.Rot().Rot())));

                    case 1000:
                    case 1100:
                    case 1110:
                    case 2001:
                    case 2010:
                    case 2011:
                    case 2100:
                    case 2101:
                    case 2110:
                    case 2120:
                    case 2201:
                    case 2210:
                    case 3012:
                    case 3021:
                    case 3102:
                    case 3120:
                    case 3201:
                    case 3210:
                        // Already in the canonical rotation.
                        return _Translate(tiles);

                    case    1:
                    case 1001:
                    case 1101:
                    case   12:
                    case  102:
                    case  112:
                    case 1002:
                    case 1012:
                    case 1102:
                    case 1202:
                    case 2012:
                    case 2102:
                    case  123:
                    case  213:
                    case 1023:
                    case 1203:
                    case 2013:
                    case 2103:
                        // Needs one rotation.
                        return _Translate(tiles.Select(tile => tile.Rot()));

                    case   10:
                    case   11:
                    case 1011:
                    case  120:
                    case 1020:
                    case 1120:
                    case   21:
                    case  121:
                    case 1021:
                    case 2021:
                    case  122:
                    case 1022:
                    case 1230:
                    case 2130:
                    case  231:
                    case 2031:
                    case  132:
                    case 1032:
                        // Needs two rotations.
                        return _Translate(tiles.Select(tile => tile.Rot().Rot()));

                    case  100:
                    case  110:
                    case  111:
                    case 1200:
                    case  201:
                    case 1201:
                    case  210:
                    case 1210:
                    case  211:
                    case  212:
                    case 1220:
                    case  221:
                    case 2301:
                    case 1302:
                    case 2310:
                    case  312:
                    case 1320:
                    case  321:
                        // Needs three rotations.
                        return _Translate(tiles.Select(tile => tile.Rot().Rot().Rot()));

                    default:
                        throw new NotSupportedException("Case analysis failed");
                }
            }

            private IReadOnlyList<Tile> _Translate(IEnumerable<Tile> tiles)
            {
                int minX = tiles.Min(tile => tile.X);
                int minY = tiles.Min(tile => tile.Y);
                return tiles.
                    Select(tile => new Tile { Shape = tile.Shape, X = (sbyte)(tile.X - minX), Y = (sbyte)(tile.Y - minY) }).
                    OrderBy(tile => tile).
                    ToList();
            }

            #if SUPERLIGHT

            private readonly short[] _Items;

            public int Count => _Items.Length;

            public IEnumerator<Tile> GetEnumerator()
            {
                foreach (var encoded in _Items)
                {
                    yield return new Tile { X = (sbyte)((encoded >> 9) & 0x3f), Y = (sbyte)((encoded >> 3) & 0x3f), Shape = (TileShape)(encoded & 0x7) };
                }
            }

            #else

            private readonly ISet<Tile> _Items;

            public int Count => _Items.Count;

            public IEnumerator<Tile> GetEnumerator() => _Items.GetEnumerator();

            public bool Contains(Tile tile) => _Items.Contains(tile);

            #endif

            IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();

            private readonly int _HashCode;
            public override int GetHashCode() => _HashCode;

            public bool Equals(TileSet tileset) => tileset != null && tileset.Count == Count && tileset._HashCode == _HashCode && _Items.SequenceEqual(tileset._Items);

            public override bool Equals(object obj) => obj is TileSet tileset && Equals(tileset);
        }
    }

    static class Extensions
    {
        internal static int IndexOf<T>(this IEnumerable<T> elts, T elt)
            where T : IEquatable<T>
        {
            int idx = 0;
            foreach (var item in elts)
            {
                if (item.Equals(elt)) return idx;
                idx++;
            }
            return -1;
        }
    }
}