Wuxings in Positive Action

JavaScript (Node.js), 39 bytes

I/O: Unicode codepoints as integers.

n=>Buffer("99944,777I")[n/543%62&15]<<9

Try it online!

How?

Step 1

We first look for some divisor \$d\$ such that the following element sets \$S_k\$ are disjoint:

$$S_k=\left\{\left\lfloor\dfrac{n}{d}\right\rfloor,\:C_{min}(k)\le n\le C_{max}(k)\right\}$$

where \$C_{min}(k)\$ and \$C_{max}(k)\$ are the codepoint bounds of the \$k\$-th element.

Because the Unicode range of Water (U+6C34 to U+706A) is immediately followed by the Unicode range of Fire (U+706B to U+7229), there are not many possible options for \$d\$: it must be a proper divisor of \$28779\$ (0x706B), i.e. \$d\in\{1, 3, 53, 159, 181, 543, 9593\}\$. The best value in there that actually works is \$d=543\$.

Step 2

We then look for some modulo chain that takes \$n\$ as input and turns it into an index corresponding to the counterpart element, using \$n/543\$. Instead of rounding the result towards zero, we force the last operation to a bitwise AND rather than a modulo.

A quick brute-force search leads to:

$$f(n)=((n/543)\bmod 62)\operatorname{and}15$$

Step 3

Finally, we look for some multiplier \$m\$ such that there exists some \$q_k\$ for each \$k\$ that satisfies:

$$C_{min}(k)\le q_k\times m\le C_{max}(k)$$

One possible golf-friendly value is \$m=512\$, leading to the following table:

 element | hexa range      | q  | q*512 | as hexa
---------+-----------------+----+-------+---------
  Soil   | 0x571F - 0x58EA | 44 | 22528 | 0x5800
  Wood   | 0x6728 - 0x6B1F | 52 | 26624 | 0x6800
  Water  | 0x6C34 - 0x706A | 55 | 28160 | 0x6E00
  Fire   | 0x706B - 0x7229 | 57 | 29184 | 0x7200
  Metal  | 0x91D1 - 0x9484 | 73 | 37376 | 0x9200

The values of \$q\$ are encoded as the ASCII codes of ,479I.


Haskell, 51 bytes

w x="金火林土水"!!sum[1|c<-"朧氳灪釐",x>c]

Try it online!


Jelly, 18 bytes

“Ọ{ƊɠṪƲ‘ɓ:Ḣ<Sịɗ‘×Ḣ

A monadic Link accepting an ordinal which yields an ordinal as below:

Input                           Output
Fire   (28779 火 - 29225 爩)    22444  垬  (Soil)
Soil   (22303 土 - 22762 壪)    37467  鉛  (Metal)
Metal  (37329 金 - 38020 钄)    27874  波  (Water)
Water  (27700 水 - 28778 灪)    26426  机  (Wood)
Wood   (26408 木 - 27423 欟)    28960  焠  (Fire)

Try it online! Or see all valid cases.

How?

Integer dividing by \$181\$:

  1. keeps the ranges distinct
  2. reduces all values to less than \$256\$
  3. has more than one result for each range
  4. has a single result greater than \$181\$

This allows us to place all our magic numbers into a single list of code-page indices, using these for the integer division, the category identification, a lookup of the new category, and a final multiplication:

“Ọ{ƊɠṪƲ‘ɓ:Ḣ<Sịɗ‘×Ḣ - Link: ordinal, n       e.g. n = 27700 (木 - minimal Water)
“Ọ{ƊɠṪƲ‘           - code-page indices (call this M) [181,123,145,159,206,153]
        ɓ          - new dyadic chain - f(n,M):
         :         -   integer divide                [27700//181, 27700//123,...]
          Ḣ        -   head (call this x)            27700//181 = 153
              ɗ    -   last three links as a dyad - f(x, M):
           <       -     (x) less than? (M)          [1, 0, 0, 1, 1, 0]
            S      -     sum                         3
             ị     -     index into (M)              145
               ‘   -   increment                     146
                ×  -   multiply (M)                  [146×181, 146×123, ...]
                 Ḣ -   head                          146×181 = 26426 (机 - a Wood)